当前位置: 首页 > article >正文

Lab: system calls

​ 在这个lab当中6.1810 / Fall 2025 它要求你在xv6当中添加一个新的系统调用以此来帮助你理解在操作系统当中系统调用的底层实现逻辑和调用链条​ 之后该lab当中会告诉你一个故意留下来的系统漏洞要求你利用该漏洞获取之前的进程已经被清理的进程的私有数据通过此lab你可以学到操作系统是如何隔离每个进程的同时也会告诉你在回收进程的资源时如果处理不当会导致原本应该被清理的进程它的私有数据可能会被其他进程窃取从而打破了操作系统的进程隔离机制。1.Using gdb​ 这一部分涉及gdb的调试所以我们暂时跳过更多GDB的调试技巧可以去网上搜索一下这里就不再阐述了。2.Sandbox a command(中等难度)​ 在这一小节当中我们需要给xv6操作系统引入一种进程级系统调用限制机制sandbox。具体而言允许用户进程通过一个新的系统调用interpose(mask, path)为当前进程及其子进程设置一组“被禁止的系统调用”使得后续执行中一旦触发这些系统调用就会被内核拒绝。官网当中告诉我们interpose接收两个参数一个是是屏蔽掩码mask另一个是路径path当前用不到。​来自官网的提示个人解析版在 Makefile 中向 UPROGS 添加$U/_sandbox以保证编译器会编译该源文件。由于interpose没有任何声明和实现所以要在user/user.h中添加一个interpose原型不要遗漏参数 。在user/usys.pl当中增加一个新的项该文件是用户态系统调用接口的生成脚本它会帮助生成一个汇编文件user/usys.S该文件中指定了每一个系统调用的参数陷入指令和返回指令。因为interpose是一个新的系统调用所以我们要在kernel/syscall.h当中添加一个新的系统调用码用于之后syscall函数的使用。因为要添加一个新的系统调用所以我们要严格按照xv6关于系统调用函数声明的规范进行命名我们可以参考xv6当中已有的函数声明所以我们在kernel/sysproc.c当中实现一个名为sys_interpose(void)的函数它就是最终的调用实现。按照官网的要求我们的屏蔽掩码需要父进程传递给子进程或者说是子进程继承了父进程的屏蔽掩码所以这就代表了这个屏蔽掩码需要被持久存储于进程中于是我们需要在进程的结构体当中添加一个字段用于记录屏蔽掩码同时因为子进程是父进程通过调用fork创造出来的所以一定存在一个函数用于将父进程当中某些状态/属性纹丝不动地赋值给子进程当中对应的字段因此根据官网的提示我们可以在kernel/proc.c当中找到一个名为kfork的函数这里就是父子进行状态/属性继承的地方我们需要在这里修改一下使得其可以将父进程新添加的“屏蔽掩码”字段同样赋值给子进程。因为每个系统调用都是一个函数指针所以在kernel/syscall.c当中有一个数组syscalls里面存放的是每一个系统调用的入口地址我们需要在该数组当中添加一项新的数据同时需要在此文件中添加sys_interpose的声明可以参考已有的xv6代码照葫芦画瓢。因为我们要实现的是系统调用的屏蔽机制所以在xv6当中任何系统调用最终都会通过内核态函数syscall进行调用号的识别和分发调用所以我们可以在此函数当中添加某些判断逻辑通过将当前请求系统调用的进程当中的屏蔽掩码与当前进程请求的系统调用的调用码向比对来得到是否要屏蔽该系统调用。以下是代码相关内容##user/user.h中新增的内容用户态函数声明 int interpose(int,char *path); ##user/usys.pl中新增的内容 entry(interpose); ##kernel/syscall.h中新增的内容系统调用号 #define SYS_interpose 22 //interpose的系统调用码 ##kernel/proc.c/kfork函数体内中新增的内容子进程继承父进程的mask ... ... /*修改点父进程的状态mask传递给子进程 * 父进程的mask已经被修改此时若创建新 * 的子进程则mask也要一并传递。 */ np-mask p-mask; ... ... ##kernel/syscall.c中新增/修改的内容系统调用声明添加新的的项到函数指针数组修改syscall函数 extern uint64 sys_interpose(void); //新添加的系统调用声明 // An array mapping syscall numbers from syscall.h // to the function that handles the system call. static uint64 (*syscalls[])(void) { [SYS_fork] sys_fork, [SYS_exit] sys_exit, [SYS_wait] sys_wait, [SYS_pipe] sys_pipe, [SYS_read] sys_read, [SYS_kill] sys_kill, [SYS_exec] sys_exec, [SYS_fstat] sys_fstat, [SYS_chdir] sys_chdir, [SYS_dup] sys_dup, [SYS_getpid] sys_getpid, [SYS_sbrk] sys_sbrk, [SYS_pause] sys_pause, [SYS_uptime] sys_uptime, [SYS_open] sys_open, [SYS_write] sys_write, [SYS_mknod] sys_mknod, [SYS_unlink] sys_unlink, [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, [SYS_interpose] sys_interpose, //新添加的系统调用 }; //这里是syscall函数修改后的样子 void syscall(void) { int num; struct proc *p myproc(); // 取出在a7中存放的调用号 num p-trapframe-a7; if(num 0 num NELEM(syscalls) syscalls[num]) { //将调用屏蔽掩码和系统调用码进行相与的操作判断当前调用是否被屏蔽/禁止 if(p-mask (1 num) ){ p-trapframe-a0 -1; return; } // Use num to lookup the system call function for num, call it, // and store its return value in p-trapframe-a0 p-trapframe-a0 syscalls[num](); } else { printf(%d %s: unknown sys call %d\n, p-pid, p-name, num); p-trapframe-a0 -1; } } ##kernel/proc.h当中修改和新增的内容屏蔽掩码 / Per-process state struct proc { struct spinlock lock; // p-lock must be held when using these: enum procstate state; // Process state void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parents wait int pid; // Process ID int mask; // 新进程的系统调用屏蔽掩码 ... ... ##kernel/syscall.c当中新增的内容sys_interpose的实现 uint64 sys_interpose(void){ //获取参数 int n; argint(0, n); //修改状态 struct proc *p myproc(); p-mask n; return 0; }验收成果按照官网给出的输入进行输入如果输出的和官网结果一致则代表成功。在ubuntu的shell当中xv6目录下输入./grade-lab-syscall sandbox_mask后如果出现以下提示则代表成功 Test sandbox_mask sandbox_mask: OK (1.5s)3.Sandbox with allowed pathnames(简单难度)​ 这一小节是对上一阶段系统调用屏蔽的扩展上一小节只是简单粗暴地屏蔽了某个系统调用一棒子打死的那种在本小节我们用到了interpose的第二个参数Path这个参数的具体意思是“允许访问的路径”。​ 假设我们的屏蔽掩码屏蔽了open和exec这个两个系统调用但是这两个系统调用在调用时都需要向其传入一个路径我们假设该路径的名字为pathA当我们调用open和exec时如果向其传入的路径 pathA和之前的Path一致则代表open和exec正常进行不会被屏蔽反之则直接返回不再执行open和exec。​个人的一些解析由于用到了interpose的第二次参数因此我们需要在进程结构体当中添加新的字段用于存放允许访问的路径。由于在进程结构体当中添加了新成员因此父子进程继承状态/属性时需要传递刚才添加的新成员。官网说了如果屏蔽码屏蔽的是open和exec则会继续判断PathA和进程结构体当中的特点字段是否一致一致则代表open和exec可以正常执行所以结合前面的例子屏蔽掩码具体屏蔽了谁应该在syscall当中进行判断而进一步地判断需要在sys_open和sys_exec两个调用的具体实现当中。以下是代码相关内容##kernel/proc.h当中修改和新增的内容屏蔽掩码 // Per-process state struct proc { struct spinlock lock; // p-lock must be held when using these: enum procstate state; // Process state void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parents wait int pid; // Process ID int mask; // 进程的系统调用屏蔽字 char allowPathName[MAXPATH]; // 被允许的路径名 ... ... ##kernel/proc.c/kfork函数体内中新增的内容子进程继承父进程的mask ... ... /*修改点父进程的状态mask传递给子进程 * 父进程的mask已经被修改此时若创建新 * 的子进程则mask也要一并传递。 * 子进程也要继承父进程的allowPathName。 */ np-mask p-mask; strncpy(np-allowPathName,p-allowPathName,MAXPATH); ... ... ##kernel/syscall.c当中修改的内容遇到open和exec则“放行”在open和exec中再次判断 void syscall(void) { int num; struct proc *p myproc(); // 取出在a7中存放的调用号 num p-trapframe-a7; if(num 0 num NELEM(syscalls) syscalls[num]) { //修改前将调用屏蔽掩码和系统调用码进行相与的操作判断当前调用是否被屏蔽/禁止 //修改后如果屏蔽的是open和exec则在open或者exec当中再次判断 if(p-mask (1 num) ){ if(num SYS_open || num SYS_exec){ p-trapframe-a0 syscalls[num](); return; } p-trapframe-a0 -1; return; } // Use num to lookup the system call function for num, call it, // and store its return value in p-trapframe-a0 p-trapframe-a0 syscalls[num](); } else { printf(%d %s: unknown sys call %d\n, p-pid, p-name, num); p-trapframe-a0 -1; } } ##kernel/sysfile.c/sys_open函数体内新增的内容添加判断逻辑 ... ... // 只有当 open 被 mask 掉时才检查路径 if(p-mask (1 SYS_open)){ if(strncmp(path,p-allowPathName,MAXPATH) ! 0){ return -1; } } ... ... ##kernel/sysfile.c/sys_exec函数体内新增的内容添加判断逻辑 ... ... // 只有当 exec 被 mask 掉时才检查路径 if(p-mask (1 SYS_exec)){ if(strncmp(path, p-allowPathName,MAXPATH) ! 0){ return -1; } } ... ...验收成果按照官网给出的输入进行输入如果输出的和官网结果一致则代表成功。在ubuntu的shell当中xv6目录下输入make grade后如果出现以下提示则代表成功 Test sandbox_mask $ make qemu-gdb sandbox_mask: OK (3.0s) Test sandbox_fork $ make qemu-gdb sandbox_fork: OK (1.1s) Test sandbox_path $ make qemu-gdb sandbox_path: OK (1.2s) Test sandbox_most $ make qemu-gdb sandbox_most: OK (0.7s) Test sandbox_minus $ make qemu-gdb sandbox_minus: OK (1.0s) Test attack $ make qemu-gdb attack: OK (1.1s)4、Attack xv6 (中等难度)​ 这一小节我们将利用系统漏洞打破进程之间的屏障从而中进程B当中访问到进程A已被回收但未彻底重置该进程使用过的内存当中的私有数据。​ xv6 通过虚拟内存和系统调用机制实现了进程之间、用户态与内核态之间的隔离在正常情况下一个用户进程不可能直接访问另一个进程的内存数据。正常情况下进程在被销毁时其使用过的内存空间也要被清理一下例如全部置为0或者其他值但是xv6当中负责回收进程内存的逻辑没有对进程使用过的内存进行清理这就导致新的进程被创建后其私有的内存空间很可能与之前的进程相重叠方便理解先这么说后面会给出具体的解释导致新进程可能访问到旧进程的私有数据。​ 所以在本次小节xv6会先通过secret程序创建进程并且向该进程的私有内存空间当中存放一些数据最后销毁进程注意存放的数据没有被销毁之后我们通过实现attack这个程序来让一个新的进程尝试从自己的私有内存空间当中寻找secret进程遗留下来的蛛丝马迹找到后输出它。​官网的一些提示和本人的解析user/secret.c是secret的源文件。我们在user/attack.c当中实现本小节让我们做的内容。官网说通过sbrk()这个系统调用来请求分配一块内存空间堆区然后在该堆区当中寻找蛛丝马迹。因为secret会向内存当从存放字符串所以我们在遍历堆区时需要判断当前访问的内存当中存放的内容是否符合字符串的特征同时字符串的字符应该是大于等于2个字符连续并且以\0结尾。要为进程分配合适大小的堆区并且检测字符串时存放字符串的容器长度也要设计合理。相关代码##user/attack.c #include kernel/types.h #include kernel/fcntl.h #include user/user.h #include kernel/riscv.h #define DATASIZE (8*4096) //heap的大小为8页共32k一页4kB int main(int argc, char *argv[]) { // Your code here. //分配heap char* buf sbrk(DATASIZE); //堆的大小为8页 char ch[DATASIZE/4]; //字符串大小为8k int j 0;// for(int i 0; i DATASIZE; i){ char c buf[i]; if(c \0 (j 2 j DATASIZE/4)){ //遇到/0,并且j大于2且在合法范围内则代表可能找到了想要的东西截断字符串 ch[j] \0; //打印 printf(%s,ch); printf(\n); //j置为0继续找剩余符合条件的字符串假设还没有扫码到heap的尽头的情况下 j 0; } //符合字符条件并且j的范围合理 if( ((c a c z) || (c A c Z) || (c 0 c 9)) j DATASIZE/4 - 1){ //符合条件则赋值给字符串数组 ch[j] c; } else{ //不连续或者不是字符则将j值为0 j0; } } exit(1); }验收成果按照官网给出的输入进行输入如果输出的和官网结果一致则代表成功。在ubuntu的shell当中xv6目录下输入./grade-lab-syscall attack后如果出现以下提示则代表成功 Test attack attack: OK (1.1s)关于内存方面的解释在 xv6 中物理内存被划分为固定大小的物理页每页 4096 字节。当一个进程创建时内核会为其建立页表用于将进程的虚拟页映射到具体的物理页上。假设进程 A 通过页表映射将数据写入某个物理页例如编号为 P 的物理页。当进程 A 退出时其页表会被销毁并且该物理页会被归还到空闲页链表中但由于本实验中内核没有对该物理页执行清零操作该物理页中的内容仍然保留。随后当进程 B 创建并调用sbrk()分配内存时内核可能会将该物理页重新分配给进程 B并通过新的页表项将其映射到进程 B 的虚拟地址空间中。此时进程 B 只要访问对应的虚拟地址就能够读取到此前进程 A 遗留下来的数据从而造成信息泄露。为什么官方文档中提到“第一次攻击可能失败需要第二次”当 secret 进程退出后其使用过的物理页会被归还到内核的空闲页链表中但这些物理页未被清零。随后 attack 进程通过sbrk()申请新的内存页时内核会从空闲页链表中分配物理页。由于空闲页的分配顺序取决于内核内部状态例如此前的内存分配和释放顺序attack 进程在第一次运行时未必能恰好获得 secret 进程曾使用过的物理页因此可能无法读取到残留数据。当 attack 程序再次运行时物理页分配状态可能发生变化此时更有可能分配到此前包含 secret 的物理页从而成功读取到敏感信息。因此攻击的成功具有一定的概率性。5、写在最后接下来要开始研究6.1810 / Fall 2025了。由于还要复习408数学所以会更新很慢。

相关文章:

Lab: system calls

​ 在这个lab当中6.1810 / Fall 2025 它要求你在xv6当中添加一个新的系统调用,以此来帮助你理解在操作系统当中,系统调用的底层实现逻辑和调用链条; ​ 之后该lab当中会告诉你一个故意留下来的系统漏洞,要求你利用该漏洞获取之前…...

企业智能体系统架构的团队管理:AI应用架构师的领导技巧

好的,请看我为您撰写的关于“企业智能体系统架构的团队管理:AI应用架构师的领导技巧”的技术博客。本文将遵循“知识金字塔构建者”的方法论,从基础概念到深层原理,再到实践整合,为您呈现一个全面而深入的视角。 企业智能体系统架构的团队管理:AI应用架构师的领导技巧 第…...

Qwen-Image-2512惊艳生成:‘水墨亭子’笔触层次与留白意境还原

Qwen-Image-2512惊艳生成:‘水墨亭子’笔触层次与留白意境还原 Qwen-Image-2512 极速文生图创作室 基于阿里通义千问团队的最新模型构建,专为追求极致效率和创意表达的用户设计。通过深度优化的中文语义理解和10步极速出图技术,让您快速将文字…...

ChatGPT Conversation Not Found 问题分析与AI辅助开发解决方案

在集成ChatGPT这类大模型API构建应用时,我们常常会追求流畅、智能的多轮对话体验。然而,一个令人头疼的报错 Conversation Not Found 或类似提示,却可能让精心维护的对话上下文瞬间“失忆”,用户体验直线下降。今天,我…...

Python GUI现代化改造:用ttk模块让你的界面颜值翻倍(附完整代码示例)

Python GUI现代化改造:用ttk模块让你的界面颜值翻倍(附完整代码示例) 1. 为什么你的Tkinter界面看起来像古董? 每次打开用Tkinter开发的GUI程序,总有种穿越回Windows 98的错觉。那些棱角分明的按钮、单调的灰色背景和生…...

Iceoryx(冰羚):无锁队列与并发控制的设计与实现2(源码解析)

接上篇设计3: MpmcLockFreeQueue (Multiple Producer Multiple Consumer)特点:数据存储与索引分离原理: 当有多个写,需要同时写入数据,那么写入的位置必定发生竞争。写入数据和更改w指针是两步,…...

新概念英语第一册081_Roast beef and potatoes

Lesson 81: Roast beef and potatoes Watch the story and answer the question Why is Carol disappointed? Key words and expressions bath 洗澡nearly adv. 几乎,将近ready adj. 准备好的,完好的dinner n. 正餐,晚餐restaurant n. 饭馆&…...

linxu 安装 python-3.11.9

在 Linux 上安装 Python 3.11.9,推荐通过源码编译的方式,这样可以获得最佳的兼容性和灵活性。以下是详细步骤: 1. 安装依赖包 # Ubuntu/Debian sudo apt update sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev…...

ADHD运动干预是什么?主要有哪几种有效的方法?

ADHD运动干预的现状与前景分析 当前,ADHD运动干预已逐渐成为帮助儿童改善注意力缺陷和行为问题的重要方法。研究表明,通过组织有针对性的运动活动,儿童的专注力和自控能力有显著改善。比如,体育课程和团体活动不仅提供了身体锻炼机…...

Spring Boot 环境变量配置详解:从 IDEA 到 Docker 部署

Spring Boot 环境变量配置详解:从 IDEA 到 Docker 部署 文章目录Spring Boot 环境变量配置详解:从 IDEA 到 Docker 部署一、问题背景1.1 环境二、问题分析2.1 现象描述2.2 根本原因1. Spring Boot RelaxedBinding 机制2. Linux 环境变量大小写敏感3. 为什…...

DataHub GraphQL API终极指南:30分钟掌握现代数据栈的元数据查询神器

DataHub GraphQL API终极指南:30分钟掌握现代数据栈的元数据查询神器 【免费下载链接】datahub The Metadata Platform for the Modern Data Stack 项目地址: https://gitcode.com/GitHub_Trending/da/datahub 你是不是经常为数据资产的管理而头疼&#xff1…...

OpenCode:让AI编程触手可及的开源工具

OpenCode:让AI编程触手可及的开源工具 【免费下载链接】opencode 一个专为终端打造的开源AI编程助手,模型灵活可选,可远程驱动。 项目地址: https://gitcode.com/GitHub_Trending/openc/opencode 作为开发者,你是否曾在复杂…...

手把手教你用Arduino和RS485搭建工业级温度监控系统(附完整代码)

工业级温度监控系统实战:Arduino与RS485的完美结合 在工业自动化领域,温度监控系统扮演着至关重要的角色。从食品加工到化工生产,从制药车间到数据中心,精确的温度数据采集与监控直接影响产品质量、设备安全和能源效率。传统的有线…...

毕业论文神器!高效论文写作全流程AI论文网站推荐(2026 最新)

论文写作全流程可拆解为文献调研→选题/开题→大纲/初稿→文献综述→降重/去AI味→润色/格式→查重/投稿七大环节,以下AI论文网站按环节精准匹配,兼顾中文适配、降重能力、去AI痕迹、学术合规四大核心需求,覆盖免费/付费、通用/垂直场景&…...

单文件网页保存高效解决方案:Monolith核心功能与实践指南

单文件网页保存高效解决方案:Monolith核心功能与实践指南 【免费下载链接】monolith ⬛️ CLI tool for saving complete web pages as a single HTML file 项目地址: https://gitcode.com/GitHub_Trending/mo/monolith 在数字化信息管理中,网页内…...

语音交互背后的黑科技:ASR、NLP、TTS 如何让机器听懂人话?

语音交互背后的黑科技:ASR、NLP、TTS 如何让机器听懂人话? 当你说"播放周杰伦的《七里香》"时,智能音箱能在1秒内完成从声波识别到音乐播放的全流程。这背后是三项核心技术的无缝衔接:**ASR(自动语音识别&am…...

开箱即用!春联生成模型-中文-base快速体验:1秒出对联,效果超预期

开箱即用!春联生成模型-中文-base快速体验:1秒出对联,效果超预期 1. 前言:AI写春联的时代来了 春节贴春联是中国人延续千年的传统习俗,但创作一副对仗工整、寓意吉祥的春联并不容易。现在,借助AI技术&…...

抛弃SMB!用Docker三分钟搭建带权限控制的WebDAV服务(2023最新chonjay21镜像版)

企业级WebDAV解决方案:基于Docker的权限管理与安全共享实践 在数字化协作日益普及的今天,企业文件共享需求呈现出爆发式增长。传统SMB/NFS协议虽然普及度高,但在跨平台兼容性、互联网传输安全性方面存在明显短板。WebDAV作为HTTP协议的扩展&a…...

Arduino SPI驱动霍尼韦尔TruStability压力传感器库

1. 项目概述Honeywell TruStability SPI 是一个专为 Arduino 平台设计的轻量级 C 库,用于通过标准 SPI 总线与霍尼韦尔(Honeywell)TruStability 系列数字压力传感器进行可靠通信。该库原生支持 HSC(High Stability and Accuracy&a…...

说起来上周还在为怎么把PFC5.0里颗粒接触力按角度统计出来头疼,翻了好几篇教程终于摸清楚门道,今天把整个流程捋一遍,顺便把踩过的坑都标出来

pfc5.0类岩石材料在进行单轴压缩,双轴压缩、直接剪切、巴西劈裂试验时,数值模拟岩石颗粒各个角度的平均接触力,角度输出代码及后处理绘制接触力的极坐标等高线图 具体内容见图片文件夹,有具体教程,很清楚 不管是单轴压…...

单片机裸机编程的系统化工程实践

单片机裸机编程的系统化工程实践1. 裸机编程概念解析1.1 裸机编程的本质特征裸机编程(Bare-metal Programming)是指在无操作系统支持的硬件环境下直接编写控制程序的技术实践。在嵌入式系统领域特指基于单片机硬件平台的直接编程方法,其核心特征包括:直接…...

EasyAnimateV5图生视频模型:VMware环境搭建与中文提示词实战

EasyAnimateV5图生视频模型:VMware环境搭建与中文提示词实战 1. 为什么选择EasyAnimateV5进行图生视频创作 在当今内容创作领域,视频内容的需求呈现爆发式增长。EasyAnimateV5-7b-zh-InP作为一款专注于图生视频任务的AI模型,为创作者提供了…...

一文读懂 PageQueryUtil:分页查询的优雅打开方式

适用人群&#xff1a;Java 开发者&#xff0c;想了解函数式编程在实际项目中的应用 前置知识&#xff1a;了解 Java 8 Lambda 表达式基础一、先来看一个实际场景场景&#xff1a;同步 10000 条债券数据到接口平台传统做法&#xff1a;Java// 一次性查询所有数据List<BondQuo…...

GetQzonehistory完整教程:三步轻松备份QQ空间所有历史说说

GetQzonehistory完整教程&#xff1a;三步轻松备份QQ空间所有历史说说 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否担心QQ空间中的珍贵回忆随着时间流逝而消失&#xff1f;Get…...

daily_stock_analysis多语言支持开发实战

daily_stock_analysis多语言支持开发实战 1. 引言 想象一下这样的场景&#xff1a;一位香港投资者收到了一份全英文的股票分析报告&#xff0c;虽然内容专业但阅读起来颇为吃力&#xff1b;一位内地用户看到繁体中文的分析结果&#xff0c;需要额外时间理解&#xff1b;而一位…...

开源项目的依赖管理:平衡兼容性与扩展性的艺术

开源项目的依赖管理&#xff1a;平衡兼容性与扩展性的艺术 【免费下载链接】IPED IPED Digital Forensic Tool. It is an open source software that can be used to process and analyze digital evidence, often seized at crime scenes by law enforcement or in a corporat…...

OpenRGB终极指南:一站式跨平台RGB设备控制解决方案

OpenRGB终极指南&#xff1a;一站式跨平台RGB设备控制解决方案 【免费下载链接】OpenRGB Open source RGB lighting control that doesnt depend on manufacturer software. Supports Windows, Linux, MacOS. Mirror of https://gitlab.com/CalcProgrammer1/OpenRGB. Releases …...

ChatGPT合租架构设计与实现:高可用代理服务的技术解析

ChatGPT合租架构设计与实现&#xff1a;高可用代理服务的技术解析 作为一名开发者&#xff0c;我最近在项目中频繁使用ChatGPT API&#xff0c;虽然效果惊艳&#xff0c;但账单也着实让人心疼。更头疼的是&#xff0c;官方对单个账户的请求速率和月度配额都有严格限制&#xf…...

C语言完美演绎5-6

/* 范例&#xff1a;5-6 */#include <stdio.h>void main(void){int a;a2; /* 将整数2赋予给变量a&#xff0c;变量a的类型与整数2一样*/printf("a%d\n",a);a6.83; /* 将浮点数6.83重新赋予给变量a&#xff0c;浮点数6.83可以自动转型为int并赋予给变量a …...

PyTorch 2.8镜像入门必看:RTX 4090D显存24G下8bit量化加载Llama3-70B方法

PyTorch 2.8镜像入门必看&#xff1a;RTX 4090D显存24G下8bit量化加载Llama3-70B方法 1. 环境准备与快速验证 在开始之前&#xff0c;让我们先确认你的环境已经准备就绪。这个PyTorch 2.8镜像已经针对RTX 4090D显卡进行了深度优化&#xff0c;预装了所有必要的组件。 1.1 环…...