OS:第六次理论作业

OS第六次理论作业 分析磁盘访问数据的时间。假设磁盘请求以柱面10、35、20、70、2、 3 和 38 的次序进入磁盘驱动器。寻道时磁头每移动一个柱面需要 5ms,以下各算法所需的寻道时间是多少: (1) 先来先服务 (2) 最短寻道时间优先 (3) SCAN算法 (4) LOOK算法 说明:假设以上三种情况磁头初始位置为15。对于(3)和(4), 磁头 当前向大柱面号方向运行,磁盘最大柱面号为85。 寻道时间:是把磁头从当前位置移动到指定磁道上所经历的时间 该时间是启动磁盘的时间s与磁头移动n条磁道所花费的时间之和 Ts = m * n + s 先来先服务: $5*(5+25+15+50+68+1+35)=995ms$ 最短寻道时间优先: 这里起始位置是15,距离10和20距离相等 若首先访问10,序列为:$15,10,3,2,20,35,38,70$ $5*(5+7+1+18+15+3+32)=405ms$​ 若首先访问20,序列为:$15,20,10,3,2,35,38,70$ $5*(5+10+7+1+33+3+32)=455ms$ SCAN电梯算法 序列为:$15,20,35,38,70,85,10,3,2$ $5*(5+15+3+32+15+75+7+1)=765ms$ LOOK算法 序列为:$15,20,35,38,70,10,3,2$ $5*(5+15+3+32+60+7+1)=615ms$ 在I/O系统中引入缓冲区的主要目标是什么?某文件占8个磁盘块, 现要把该文件的磁盘块逐个读入主存缓冲区,并送用户区进行分析。一个缓冲区与磁盘块大小相等。把一个磁盘块读入缓冲区的时间为 100μs,缓冲区数据传送到用户区的时间是50μs,CPU对一块数据 进行分析的时间为50μs。分别计算在单缓冲区和双缓冲区结构下, 分析完该文件的时间是多少? 在I/O系统中,CPU计算速度较快,而外设I/O速度较慢,为了解决CPU与外设速度不匹配的问题,提高CPU的利用率,减少CPU的忙等状态,设计了缓冲区 使用单缓冲区:第一块磁盘读入缓冲区的时间为100us,而后的磁盘读入和CPU进行分析的50us可以并行执行,对过程进行简单分析,第一块磁盘读入到传送到用户区:150us,此时CPU对第一块数据进行计算,并行地,第二块磁盘开始读入,需要50+50+50完成对第二块磁盘的计算,后边的磁盘同理。即第一块磁盘从读入到完成分析需要200us,后边每一块需要150us,总体$150*7+200=1250us$ 使用双缓冲区:CPU和I/O设备分别使用一块缓冲区,一块缓冲区由I/O设备进行磁盘块读入后将数据传送到CPU并由CPU进行分析,并行地,另一个缓冲区进行磁盘块读取,即第一块磁盘需要200us,后边每块磁盘需要100us,总体$200+100*7=900us$ 请结合操作系统课所学习的内容总结从哪些方面可以提高文件系统 的性能。 可以采用目录项分解、当前目录、磁盘碎片整理、块高速缓存、磁盘调度、提前读取、合理分配磁盘空间、信息的优化分布、RAID等技术 简述文件控制块(FCB-File Control Block)中所管理的主要信息 基本信息:文件名、物理位置、文件逻辑结构、文件物理结构 访问控制信息:文件所有者、访问权限 使用信息:创建时间、上一次修改时间、当前使用信息 在文件系统中,访问一个文件f时首先需要从目录中找到与f对应 的目录项。假设磁盘物理块的大小为1KB,一个目录项的大小为128 字节,文件的平均大小为100KB。该 文 件 系 统 的 目 录 结 构 如 下图所示。 假定不考虑磁盘块的提前读和缓存等加速磁盘访问技术。 请回答以下问题: (1) 按照当前的目录结构,且采用串联文件方式对数据块进行组织,并且根目录的目录项已读入内存中。如果目标文件f 在第三级目录下, 且其对应的第三级目录的目录项可以一次从磁盘读出,访问文件f中的 一个块平均需要访问几次磁盘? (2) 如果采用i节点的方法来构建文件目录,假定文件名占14个字 节,i节点的指针占2个字节。如果仅采用直接索引,每个第三级目录 下的文件数不超过50个,且根目录的i节点已读入内存,访问第三级 目录下的一个文件的一个块平均需要访问几次磁盘? (3) 假设该文件系统的空间最大容量为16ZB(1ZB=$2^{70}$B)。如 果 文 件 的 FCB 中包括512字节的索引区,且允许采用一级索引进行组织,那么该 文件系统支持的最大文件是多少字节? ...

May 25, 2024 · 1 min · sudo

OS:lab5实验报告

OS:lab5实验报告 Thinking 5.1 如果通过 kseg0 读写设备,那么对于设备的写入会缓存到 Cache 中。这是 一种错误的行为,在实际编写代码的时候这么做会引发不可预知的问题。请思考:这么做 这会引发什么问题?对于不同种类的设备(如我们提到的串口设备和IDE磁盘)的操作会 有差异吗?可以从缓存的性质和缓存更新的策略来考虑 当外部设备自身更新数据时,如果此时CPU写入外设的数据仍然保留在Cache中,而没有被及时写入到设备,则缓存的数据会在外设自身更新后再写入外设,发生错误 串口设备读写频繁,而IDE磁盘读写频率相对较小,因此串口设备发生错误的概率要远大于IDE磁盘 Thinking 5.2 查找代码中的相关定义,试回答一个磁盘块中最多能存储多少个文件控制 块?一个目录下最多能有多少个文件?我们的文件系统支持的单个文件最大为多大? 一个磁盘块的大小为4KB,一个文件控制块的大小为256B,故能存储16个 一个目录文件最多可以使用1024个磁盘块存储文件控制块,故最多有16384个文件 一个文件最多使用1024个磁盘块存储数据,1024*4KB=4MB Thinking 5.3 请思考,在满足磁盘块缓存的设计的前提下,我们实验使用的内核支持的最大磁盘大小是多少? 块缓存的地址空间大小为DISKMAX即0x4000_0000页即为1GB大小 故实验支持的的最大磁盘大小为1GB Thinking 5.4 在本实验中,fs/serv.h、user/include/fs.h 等文件中出现了许多宏定义, 试列举你认为较为重要的宏定义,同时进行解释,并描述其主要应用之处 比较重要的一个宏定义是FILE_STRUCT_SIZE 1 #define FILE_STRUCT_SIZE 256 这个宏定义指出了file结构体的大小 在f_pad域对file结构体空闲空间进行补全时用到,让整数个结构体占用一个磁盘块 Thinking 5.5 在Lab4“系统调用与fork”的实验中我们实现了极为重要的fork函数。那 么fork前后的父子进程是否会共享文件描述符和定位指针呢?请在完成上述练习的基础上 编写一个程序进行验证。 一个进程所有的文件描述符都存储在[FDTABLE, FILEBASE)这一地址空间中。在fork函数执行时,会将KSTACKTOP以下的地址空间复制到子进程地址空间中,因此fork前后的父子进程会共享文件描述符和定位指针。 Thinking 5.6 请解释File,Fd,Filefd结构体及其各个域的作用。比如各个结构体会在哪些过程中被使用,是否对应磁盘上的物理实体还是单纯的内存数据等。说明形式自定,要 求简洁明了,可大致勾勒出文件系统数据结构与物理实体的对应关系与设计框架 给出以上三种结构体的定义以及解释 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 struct File { char f_name[MAXNAMELEN]; // 文件名 uint32_t f_size; // 文件的大小 uint32_t f_type; // 文件类型 uint32_t f_direct[NDIRECT]; // 存储的直接磁盘块号 uint32_t f_indirect; // 存储的间接磁盘块号,其中保存间接指向的磁盘块号 struct File *f_dir; // 文件的目录,只有在内存中有效 char f_pad[FILE_STRUCT_SIZE - MAXNAMELEN - (3 + NDIRECT) * 4 - sizeof(void *)]; } __attribute__((aligned(4), packed)); // 填充剩余空间,只保存整数个文件结构体 // file descriptor struct Fd { u_int fd_dev_id; // 文件对应的设备id u_int fd_offset; // 文件指针指向的位置(在设备中的偏移量) u_int fd_omode; // 文件打开模式(open mode) }; // file descriptor + file struct Filefd { struct Fd f_fd; // 文件描述符结构体 u_int f_fileid; // 文件id,表示文件在opentab中的位置 struct File f_file; //文件控制块 }; Fd结构体主要用于记录已经打开的文件的状态,便于用户直接使用文件描述符对文件进行操作和申请服务。由于文件描述符主要是为用户所用,因此它对应的是磁盘映射到内存中的数据。 ...

May 19, 2024 · 3 min · sudo

OS:lab5课下基础

OS:lab5课下基础 1. 文件系统概述 ​ 计算机文件系统是一种存储和组织数据的方法,便于访问和查找数据。文件系统使用文件和树形目录的逻辑抽象屏蔽了底层硬盘和光盘等物理设备基于数据块进行存储和访问的复杂性。 即用户不必关心数据实际保存在硬盘/光盘的哪个数据块上,而只需要记住这个文件所属的目录和文件名。 在写入新数据之前,用户不必关心硬盘上哪个块是空闲的,硬盘上的存储空间管理(分配/释放)由文件系统自动完成,用户只需记住数据写入到了哪个文件 ​ 常见的文件系统通常基于硬盘和光盘等块存储设备,并维护文件在设备中的物理位置。 ​ 广义上,一切带标识的、在逻辑上有完整意义的字节序列都可以称为文件,文件系统将外部设备中的资源抽象为文件,从而可以统一管理外部设备,实现对数据的存储、组织、访问和修改。 2. 文件系统的设计与实现 MOS是一种微内核设计 将传统操作系统的文件系统移出内核 将一些内核数据暴露到用户空间 将传统操作系统的设备驱动移出内核 3. IDE磁盘驱动 ​ 为了在磁盘等外部设备上实现文件系统,我们必须为这些外部设备编写驱动程序。(MOS已经在kern/mechine.c中实现了一个简单的控制台驱动程序) ​ 本次要实现的硬盘驱动程序与已经实现的串口驱动都采用MMIO内存映射技术,并且本次编写的硬盘驱动程序完全运行在用户空间(微内核)。 3.1 内存映射I/O(MMIO) ​ 几乎每一种外设都是通过读写设备上的寄存器来进行数据通信,外设寄存器也称为I/O接口,主要用来访问I/O设备。 外设寄存器主要包括控制寄存器、状态寄存器、数据寄存器,这些寄存器被映射到指定的物理地址空间 ​ 在MIPS的内核地址空间中(kseg0/kseg1)实现了硬件级别(高位清0)的物理地址和内核虚拟地址的转换机制,其中,对于kseg1段地址的读写不经过MMU映射,且不使用高速缓存,这正是外部设备驱动所需要的。我们可以通过简单地读写某些固定的内核虚拟地址来实现驱动程序的功能。 在之前的实验中,我们曾经使用KADDR宏将一个物理地址转换为kseg0段的虚拟地址,即加上kseg0段的偏移量(0x8000_0000) ​ 在编写设备驱动时,我们需要将物理地址转换为kseg1段的内核虚拟地址,即给物理地址加上kseg1的偏移值0xA000_0000 ​ 以已经编写完成的串口设备驱动为例,MALTA提供的console设备基地址为0x1800_03F8,设备寄存器映射如表所示 ​ 例如写操作:通过向kseg1段的地址写入字符,就能在shell中看到对应的输出。 kseg0_offset + device_op_addr_base = KSEG1 + MALTA_SERIAL_DATA 1 2 3 4 5 //写 void printcharc(char ch) { //... *((volatile uint8_t *)(KSEG1 + MALTA_SERIAL_DATA)) = ch; } ​ 在本次实验中,我们需要编写的IDE磁盘驱动位于用户空间,用户态进程若是直接读写内核虚拟地址会引发一个地址错误ADEL/S,故对于设备的读写必须通过系统调用实现 ...

May 19, 2024 · 20 min · sudo

OO-Unit3

OO第三单元——JML 一. 单元架构设计 ​ 相比于前两单元需要自行设计架构,充当架构设计师的角色,U3不需要设计架构,需要完成的任务是依据课程组给出的架构设计合理的类协作关系以及依据JML写对应的代码(码农日常)。 ​ 关于课程组给出的架构可以简单概括如下图 二. 算法实现与性能 ​ 本单元更加关注的是对于既定的JML或者说给定的功能要求,具体的算法实现和效率。强测中对于性能有一定要求,性能不佳会爆CTLE。我的理解中,CTLE字面意思上是CPU时间超时,换句话说其实就是**“算的次数太多“**,在本单元作业中,可以采用缓存一些计算复杂度较高的结果进行优化等,下面基于每一次作业进行分析。 1. hw9 ​ 这一次作业中主要的性能考察点是关于query_block_sum中查询两个点是否联通的功能。在本次作业中,通过addRelation和modifyRelation可以建立起一个社交图,这其中需要注意的是modifyRelation操作可能删边。很多大佬同学实现了可以删边的并查集,我选择了实现较为简单(懒)的BFS。使用BFS进行联通查询面对大量查询指令有CTLE风险,故可以考虑对BFS进行一点简单优化,例如使用双向BFS。关于双向BFS,其实就是从起点和终点同时开始搜索,面对图中点非常多的情况可以大幅减少开销。 ​ 下面是一张摘自广度优先搜索之双向bfs(实操篇)-CSDN博客的一张很形象的图片。 ​ 双向BFS的优势其实就是大幅减少搜索宽度,减少遍历一些点,让它看上去没有那么暴力。由此还有一种优化的思路就是均衡取节点。当我们从给起点和终点分别设置的队列中取节点时,可以选择从size较小的那一个队列中取出(size较大的那一方说明他每一层中的宽度较大,遍历开销大)。下面给出我的具体实现(主要想法是起点终点两个队列两个visited数组,当前遍历如果另一边如果遍历过,结束): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import java.util.HashMap; import java.util.LinkedList; import java.util.Queue; public class BreadthFirstSearch { public static boolean isConnected(MyPerson person1,MyPerson person2) { if (person1.getId() == person2.getId()) { return true; } HashMap<Integer, Boolean> visited1 = new HashMap<>(); HashMap<Integer, Boolean> visited2 = new HashMap<>(); visited1.put(person1.getId(),true); visited2.put(person2.getId(),true); Queue<MyPerson> queue1 = new LinkedList<>(); Queue<MyPerson> queue2 = new LinkedList<>(); queue1.add(person1); queue2.add(person2); while (!queue1.isEmpty() && !queue2.isEmpty()) { if (queue1.size() < queue2.size()) { MyPerson now = queue1.poll(); if (next(now,visited1,visited2,queue1)) { return true; } } else { MyPerson now = queue2.poll(); if (next(now,visited2,visited1,queue2)) { return true; } } } return false; } private static boolean next(MyPerson now,HashMap<Integer,Boolean> visited, HashMap<Integer,Boolean> visited2,Queue<MyPerson> queue) { for (Person next : now.getAcquaintance().values()) { if (visited.containsKey(next.getId())) { continue; } if (visited2.containsKey(next.getId())) { return true; } queue.add((MyPerson) next); visited.put(next.getId(),true); } return false; } } ​ 对于另一个查询指令query_triple_sum也需要进行动态维护,在addRelation加边和modifyRelation删边时进行判断,并更新qts的值。 ...

May 16, 2024 · 3 min · sudo

OS第五次理论作业

OS第五次理论作业 有五个进程 P1、P2、P3、P4、P5,它们同时依次进入就绪队列,它们的优先数和需要的处理器时间如下表 进程 处理器时间 优先级(数小优先级高) P1 10 3 P2 1 1 P3 2 3 P4 1 4 P5 5 2 忽略进行调度等所花费的时间,回答下列问题: 写出采用 “先来先服务”、“短作业(进程)优先”、“非抢占式的优先数” 和 “轮转法” 等调度算法,进程执行的次序。(其中轮转法的时间片为 2) 先来先服务: P1,P2,P3,P4,P5 短作业优先: P2,P4,P3,P5,P1 非抢占式的优先数: P2,P5,P1,P3,P4 轮转法: P1,P2,P3,P4,P5,P1,P5,P1,P5,P1 分别计算上述算法中各进程的周转时间和等待时间,以及平均周转时间 先来先服务 进程 周转时间 等待时间 执行时间 P1 10 0 10 P2 11 10 1 P3 13 11 2 P4 14 13 1 P5 19 14 5 平均周转时间:$(10+11+13+14+19)/5=13.4$ 短作业优先 进程 周转时间 等待时间 执行时间 P2 1 0 1 P4 2 1 1 P3 4 2 2 P5 9 4 5 P1 19 9 10 平均周转时间:$(1+2+4+9+19)/5=7$ 非抢占式的优先数 ...

May 13, 2024 · 2 min · sudo

OS第四次理论作业

OS第四次理论作业 1.读者写者问题(写者优先): 1)共享读; 2)互斥写、读写互斥; 3)写者优先于读者(一 旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 int readcount = 0; int writecount = 0; semaphore mutex = 1;//信号量为1相当于互斥锁 semaphore write = 1; semaphore read = 1; semaphore r_wait = 1; writer() { P(write); // 保护 writecount writecount++; if(writecount == 1) { P(r_wait); // 有写入的,暂停读 } V(write); P(mutex); // 互斥写 write_data(); V(mutex); P(write); writecount--; if(writecount == 0) { // 没有写入的,恢复读 V(r_wait); } V(write); } reader() { P(r_wait); // 如果有写入的或等待写入的,暂停读,实现写优先于读 P(read); // 保护 readcount if(readcount == 0) { P(mutex); // 第一个读的,加锁 } readcount++; V(read); V(r_wait); read_data(); P(read); readcount--; if (readcount == 0) { // 最后一个读的,解锁 V(mutex); } V(read); } 2.寿司店问题。假设一个寿司店有 5 个座位,如果你到达的时候有一个空座位,你可以立 刻就坐。但是如果你到达的时候 5 个座位都是满的有人已经就坐,这就意味着这些人都是一 起来吃饭的,那么你需要等待所有的人一起离开才能就坐。编写同步原语,实现这个场景的约束。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int eating = 0, waiting = 0; Semaphore mutex = 1, queue = 1; bool must_wait = false; Customer(){ P(mutex); if (must_wait){ waiting++; V(mutex); // 对 waiting 变量的保护可以释放 P(queue); // 被阻塞,坐着等待排队,等待被唤醒 } else { eating++; must_wait = (eating == 5); V(mutex); // 对 eating 变量的保护可以释放 } // 上一部分已经解决了进店后是等待还是吃的问题 Eat_sushi();// else 的人和被唤醒的排队者成功进入这一步 P(mutex); // 开启对 eating, waiting 变量保护 eating--; // 吃的人 -1, 如果 5 个没全吃完,不可以换下一批人吃 if (eating == 0){ // 最后一个吃完的人离开才可以进顾客 int n = min(5, waiting); // 放顾客进来的数量,不超过 5 个 waiting -= n; eating +=n; must_wait = (eating == 5); for(int i = 0; i<n; i++){ V(queue); // 唤醒排队的 n 个人继续进程 } V(mutex); // 允许下一个吃完的人对变量和队列进行操作 } } 3.进门问题。(1)请给出P、V操作和信号量的物理意义。(2)一个软件公司有5名员工, 每人刷卡上班。员工刷卡后需要等待,直到所有员工都刷卡后才能进入公司。为了避免拥挤, 公司要求员工一个一个通过大门。所有员工都进入后,最后进入的员工负责关门。请用 P、 V 操作实现员工之间的同步关系 P操作:请求一个资源,V操作:释放一个资源 ...

May 6, 2024 · 3 min · sudo

OS:lab4实验报告

OS:lab4实验报告 Thinking 4.1 内核在保存现场的时候是如何避免破坏通用寄存器的? 通过执行SAVE_ALL对现场中的所有寄存器进行保存 系统陷入内核调用后可以直接从当时的$a0-$a3参数寄存器中得到用户调用msyscall 留下的信息吗? 不可以,a0-a3寄存器中的参数可能会发生改变,在陷入内核时将需要传递的参数保存进了一个Trapframe中,可以从传递的trapframe指针中获取需要的参数 我们是怎么做到让sys开头的函数“认为”我们提供了和用户调用msyscall时同样 的参数的? 用户调用时的参数保存进了一个Trapframe中,在do_syscall函数中从传入的trapframe指针中取出传递的参数再传入对应的系统调用处理函数sys_*中 内核处理系统调用的过程对Trapframe做了哪些更改?这种修改对应的用户态的变化是什么? 在处理系统调用过程中改变了trapframe指针中v0寄存器(返回值寄存器)的值,这个返回值可以标志系统调用是否成功,如果失败则返回对应的错误码 Thinking 4.2 思考 envid2env函数: 为什么 envid2env中需要判断 e->env_id != envid 的情况?如果没有这步判断会发生什么情况? 相关判断代码 1 2 3 if (e->env_status == ENV_FREE || e->env_id != envid) { return -E_BAD_ENV; } 在mkenvid中,envid的生成是通过asid与在envs中的偏移地址得到的,所以可能有envid越界的情况,所以需要判断申请到的env的id是不是等于请求的envid Thinking 4.3 思考下面的问题,并对这个问题谈谈你的理解:请回顾 kern/env.c 文件 中 mkenvid() 函数的实现,该函数不会返回 0,请结合系统调用和 IPC 部分的实现与 envid2env() 函数的行为进行解释。 mkenvid函数不会返回0,故不会有进程的envid为0,而IPC中其他函数有时候需要获取当前进程的PCB,故可以用0作为比较快速的索引 Thinking 4.4 关于 fork 函数的两个返回值,下面说法正确的是:C A、fork 在父进程中被调用两次,产生两个返回值 B、fork 在两个进程中分别被调用一次,产生两个不同的返回值 ...

May 6, 2024 · 1 min · sudo

OS:lab4课下基础

OS:lab4课下基础 1.系统调用 1.1 系统调用相关概念 ​ 在MIPS中,syscall是用于执行系统调用的自陷指令,它使得进程陷入到内核的异常处理程序中,由内核根据系统调用时的上下文执行相应的内核函数,完成相应的功能,并最终返回到syscall的后一条指令。 存在只能由内核来完成的操作(读写设备、创建进程、IO等) C标准库中的一些函数的实现依赖于操作系统 通过执行syscall指令,用户进程可以陷入到内核态,请求内核提供的服务 通过系统调用陷入到内核态时,需要在用户态与内核态之间进行数据传递与保护 ​ 系统调用保证了系统的安全性:内核将自己能够提供的服务以系统调用的方式提供给用户空间,用户程序只能将服务相关的参数交予操作系统执行 API : Application Programming Interface,程序之间的接口 ​ 直接使用系统调用较为麻烦,于是产生了一系列用户空间的API定义,他们在系统的调用的基础上,实现了更多更高级的常用功能。用户在编写程序时可以直接调用高层次的API来实现各种功能。 ​ 通过层级划分使得程序具有更好的可移植性,只要程序以来的API不变,无论底层的系统调用如何变化,都不会影响 1.2 系统调用机制的实现 ​ 异常分发向量组(exception_handlers)中的8号异常,即为操作系统处理系统调用时的异常。 MOS实验代码中,kern目录下即为内核态代码,user目录下即为用户态代码 ​ 以user/lib/debugf.c中的debugf函数来学习处理系统调用的流程(debugf函数是一个debug信息输出函数,进行了IO方面的系统调用) ​ debugf函数的调用链为(系统调用请求从用户态向内核态传递) debugf调用字符串输出函数debug_output debug_output调用了用户空间的syscall_*函数(这里的*为通配符,代表着用户空间进行系统调用的一组操作,都定义在用户态代码syscall_lab.c中,这里调用的是syscall_print_cons) syscall_*函数调用msyscall函数,系统陷入内核态(msyscall是汇编代码,直接调用syscall)。 内核态中将异常分发到handle_sys函数,将系统所需信息传递进内核(输出字符串s) 内核取得信息,执行对应的内核空间的系统调用函数sys_*(kern/syscall_all.c) 系统调用完成,返回值传递回用户态 从系统调用函数返回,回到debugf调用处 通过上述描述,对于系统调用的处理实际上是从用户空间向系统空间进行传递的,用户空间中的syscall_*函数与内核中的sys_*是一一对应的,syscall_*函数是用户空间中最接近内核的函数,他调用msyscall中的汇编代码syscall直接陷入内核态,sys_*函数是内核中系统调用的具体实现。 ​ 直接调用syscall陷入内核的msyscall函数具有六个参数,其中第一个参数是与调用名相似的宏,例如SYS_print_cons,被称为系统调用号(include/syscall.h),用来区分不同的系统调用,其余还有五个参数,即为系统调用时需要传递给内核的参数。 回忆MIPS函数调用规范中的参数传递,前四个参数保存在寄存器中 Exercise 4.1 msyscall 进行系统调用(syscall),并返回到msyscall的调用者处(jr),syscall_* #include <asm/asm.h> LEAF(msyscall) // Just use 'syscall' instruction and return. /* Exercise 4.1: Your code here. */ syscall #陷入内核 jr ra #返回调用者 syscall_* END(msyscall) ​ 通过汇编指令syscall陷入内核态后,处理器将PC寄存器指向一个内核中固定的异常处理入口(见lab3中不同异常处理跳转到的地址) ...

May 6, 2024 · 15 min · sudo