From 530c4723212c701e683a04233be4f86af5686152 Mon Sep 17 00:00:00 2001 From: pv9jfohxb <1907694477@qq.com> Date: Thu, 8 Jul 2021 23:06:42 +0800 Subject: [PATCH] ADD file via upload --- 18组实验报告(栗端宇、贺子然) .md | 357 +++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 18组实验报告(栗端宇、贺子然) .md diff --git a/18组实验报告(栗端宇、贺子然) .md b/18组实验报告(栗端宇、贺子然) .md new file mode 100644 index 0000000..b3764f3 --- /dev/null +++ b/18组实验报告(栗端宇、贺子然) .md @@ -0,0 +1,357 @@ +# 操作系统实 验 报 告 + + + +## 实验名称: 系统调用扩充 + +--- + +学 员1:栗端宇 学 号: 201902001022 + +学 员2:贺子然 学 号: 201902001027 + +培养类型:无军籍本科学员 + +年 级:2019级 + +专 业:计算机类 + +所属学院:计算机学院 + +指导教员:文艳军 + +职 称:教授 + +实 验 室:306-704 + +实验日期: 2021.7.8 + +国防科学技术大学训练部制 + +--- + +《实验报告》填写说明 + +1. 学员完成人才培养方案和课程标准要所要求的每个实验后,均须提交实验报告。 + +2. 实验报告封面必须打印,报告内容可以手写或打印。 + +3. 实验报告内容编排及打印应符合以下要求: + +(1)采用A4(21cm×29.7cm)白色复印纸,单面黑字打印。上下左右各侧的页边距均为3cm;缺省文档网格:字号为小4号,中文为宋体,英文和阿拉伯数字为Times New Roman,每页30行,每行36字;页脚距边界为2.5cm,页码置于页脚、居中,采用小5号阿拉伯数字从1开始连续编排,封面不编页码。 + +(2)报告正文最多可设四级标题,字体均为黑体,第一级标题字号为4号,其余各级标题为小4号;标题序号第一级用“一、”、“二、”……,第二级用“(一)”、“(二)” ……,第三级用“1.”、“2.” ……,第四级用“(1)”、“(2)” ……,分别按序连续编排。 + +(3)正文插图、表格中的文字字号均为5号。 + +--- + +## 一、实验目的和内容 + +(一)实验目的:深入掌握系统调用的实现方法,掌握进程地址空间的表示方法,掌握可执行文件的加载过程,掌握 Linux 的页故障处理过程。 + +(二)实验内容:以版本 0 内核为基础,增加一组系统调用(详情如下),并通过给定的测试用例。 + +| **系统调用名字** | **功能** | **是否必做** | +| ---------------- | ------------------------------------------------------------ | ------------ | +| execve2 | 以“立即加载”方式执行一个可执行文件,要求加载完后运行时该进程不产生缺页异常。 | 是 | +| getdents | 获取一组目录项 | 是 | +| pipe2 | 创建管道 | 是 | +| sleep | 进程睡眠 | 是 | +| getcwd | 获取当前工作目录 | 否 | +| mmap | 将一个文件映射到进程地址空间中 | 否 | +| munmap | 解除文件到地址空间的映射 | 否 | +| clone | 创建进程,要求新进程共享当前进程除栈以外的地址空间 | 否 | + +要求提交实验报告和所有源码,实验报告应记录各系统调用的设计思路、实现方法和测试过程及画面。 + +## 二、操作方法与实验步骤 + +### 1. 首先了解整个实验的工作流程,明确小组分工 + +认真阅读实验说明,对实验进行整体把握,通过011文件中的oscomp_syscalls.md文件了解实验的系统调用需要做什么事情,然后明确小组分工。 + +### **2.** 添加八个系统调用的声明 + +因为使用到的系统调用都没有声明,因此应在内核代码中统一添加八个系统调用的声明和实现,修改unistd.h中的宏定义,sys_call_table。并且要保证虚拟机内的头文件与内核中保持一致。 + +添加系统调用号,在系统调用表中添加系统调用实现函数,声明系统调用实现函数(在sys.h中): + +unistd.h代码修改如下: + +```C +#define __NR_execve2 87 +#define __NR_getdents 88 +#define __NR_pipe2 89 +#define __NR_sleep 90 +#define __NR_getcwd 91 +#define __NR_mmap 92 +#define __NR_munmap 93 +#define __NR_clone 94 +``` + +sys.h代码修改如下: + +```C +extern int sys_execve2(); +extern int sys_getdents(); +extern int sys_pipe2(); +extern int sys_sleep(); +extern int sys_getcwd(); +extern int sys_mmap(); +extern int sys_munmap(); +extern int sys_clone(); + +fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, +sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, +sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod, +sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount, +sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm, +sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access, +sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir, +sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid, +sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys, +sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit, +sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid, +sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask, +sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending, sys_sethostname,sys_setrlimit, sys_getrlimit, sys_getrusage, sys_gettimeofday, sys_settimeofday, sys_getgroups, sys_setgroups, sys_select, sys_symlink,sys_lstat, sys_readlink, sys_uselib, + + sys_execve2,sys_getdents,sys_pipe2,sys_sleep,sys_getcwd,sys_mmap,sys_munmap,sys_clone +}; +``` + +修改系统调用数量的宏定义如下图所示,由原来的87个修改为95个: + +```C +nr_system_calls = 95 /* 72 */ +``` + +### **3.** 对内核进行修改,完成系统调用 + +使用mkdir test命令新建文件夹,将老师给的include以及测试文件放入test文件夹(将文件导入b文件夹,使用mcopy命令导入内核,在这里由于文件夹不能导入,所以每导入一个文件夹需要创建一个空文件夹,再导入其中的内容),然后使用make指令,生成可执行文件 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps4.jpg)![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps5.jpg) + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps6.jpg) + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps7.jpg) + +--- + +#### **(1) sys_execve2:** + +关键在于“立即加载”的实现。考虑在execve的基础上进行修改,先考察sys_execve的实现。其中调用了do_execve。先根据sys_execve的实现完全复制一个sys_execve2的实现: + +```assembly +.align 4 +sys_execve2: + lea EIP(%esp),%eax + pushl %eax + call do_execve2 + addl $4,%esp + ret +``` + +同样复制一份do_execve2,下面修改do_execve2: + +do_no_page函数是处理缺页故障的函数,如果在do_execve2执行功能结束后人工调用do_no_page来处理故障,可以使得每一次do_execve2处理后都不出现缺页。(等于是把本来操作系统做的缺页处理提前到了do_execve2中),因此从current结构体里面找到do_no_page需要的参数:分别是进程的起始地址和数据段偏移。 + +```c +void do_no_page_myself(unsigned long error_code,unsigned long address) +{ + int nr[4]; + unsigned long tmp; + unsigned long page; + int block,i; + + + address &= 0xfffff000; + tmp = address - current->start_code; + if (!current->executable || tmp >= current->end_data) { + get_empty_page(address); + return; + } + if (share_page(tmp)) + return; + if (!(page = get_free_page())) + oom(); +/* remember that 1 block is used for header */ + block = 1 + tmp/BLOCK_SIZE; + for (i=0 ; i<4 ; block++,i++) + nr[i] = bmap(current->executable,block); + bread_page(page,current->executable->i_dev,nr); + i = tmp + 4096 - current->end_data; + tmp = page + 4096; + while (i-- > 0) { + tmp--; + *(char *)tmp = 0; + } + if (put_page(page,address)) + return; + free_page(page); + oom(); +} +``` + + + +其中do_no_page_myself函数与do_no_page函数完全一致,重新复制是为了区分do_execve2与其余系统调用产生缺页的区别,方便调试。在execve2中修改代码如下(修改部分红框标出) + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps10.jpg) + +测试代码运行如下,测试成功 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps11.jpg) + +--- + +#### **(2) sys_getdents** + +通过文件可知,该系统调用目的在于获取目录项,由上课学习的知识可知,文件系统有PCB(打开文件表)、读写状态信息表、活跃文件目录表,其中目录中的目录项储存在活跃文件目录表所指向的块中 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps12.jpg) + +根据函数的参数类型可以发现,我们可以根据fd变量找到当前目录,并获得其读写信息表信息 + +```C +int getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count); +``` + +根据struct file的结构,我们发现有指向活跃文件目录表的f_inode指针,并据此我们得到该目录内容 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps13.jpg) + +通过i_zone[]我们可以得到存放目录项得块内容 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps14.jpg) + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps15.jpg) + +为了读取块中信息,我们使用bread函数来读取(理论上应该遍历i_zone,但由于目录项只有16字节,而一个块有1024字节,可容纳64个目录项,故不予考虑) + +```c +block=bread(f_inode->i_dev,f_inode->i_zone[0]); +``` + +根据bread函数返回值,block是buffer_head *类型,指向块内容,块中目录项结构如图所示: + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps17.jpg) + +然后根据老师要求的输出格式中,将目录项结构中的inode和name进行赋值(d_reclen赋linux_dirent,d_off赋0),可以通过for循环遍历直到所有目录项被录入。 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps18.jpg) + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps19.jpg) + +同时我们需要将用户态的数据传入内核态 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps20.jpg) + +定义一个int类型变量用来记录目录项长度。 + +运行检验代码如下 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps21.jpg) + +--- + +#### **(3) sys_sleep:** + +sleep的功能是使进程睡眠seconds秒。利用sys_signal函数,sys_alarm函数和sys_pause函数实现功能:首先分析睡眠的含义:进程挂起一段确定时间后继续重新调度并且运行。 + +要达到挂起进程,我们想到使用sys_pause,但是sys_pause挂起后就不会按时唤醒,只能依靠固有的调度算法,所以我们需要用sys_alarm函数来唤醒进程。 + +但如果仅仅使用sys_alarm唤醒,该函数会向系统发出一个软中断信号SIGALRM,来使得操作系统中断掉这个进程,因此我们需要屏蔽掉这个信号产生的效果。 + +利用sys_signal函数 ,其第一个参数是需要处理的信号,第二个参数是处理信号的方式,以如下方式使用代表忽略接下来产生的SIGALRM信号。因此可以实现目的功能。 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps22.jpg) + +--- + +#### **(4) sys_pipe2:** + +根据名称判断,该系统调用为sys_pipe的变形。同时根据描述,两者功能没有区别,故尝试直接对sys_pipe进行封装 + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps23.jpg) + +其中filedes[0] 和filedes[1]分别是管道的读取端和写入端。 + +直接将利用pipe2封装pipe即可: + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps24.jpg) + +运行检测代码测试如下: + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps25.jpg) + +#### **(5)sys_getcwd:** + +与getdents相似,都运用了文件管理方面的知识。为了获取当前工作目录,我们可以访问“..”,从而我们可以获得上一级目录的目录项的i节点号(所有),同时我们可以得到当前目录的i节点号,采用这种方法我们就可以通过匹配来得到当前目录的名称,并以此类推直到达到根目录(具体数据结构与sys_getdents相同故不再赘述) + +获取上级目录,记录目录内容并计数 + +```C + while (i >= 0) + { + int last_node=dr->inode; + node = iget(current->root->i_dev, (dr + 1)->inode); + bh = bread(current->root->i_dev, node->i_zone[0]); + dr = (struct dir_entry *)(bh->b_data); + int j = 2; + while (dr->name[0] != '\0') + { + if ((dr + j)->inode == last_node) + break; + j++; + } + if ((dr + j)->name[0] == '\0') + break; + dir[i] = (dr + j)->name; + i++; + } +``` + +将保存的数据链接成路径导入rd中 + +```C +for (; i >= 0; i--) + { + k2 = 0; + while (dir[i][k2] != '\0') + { + rd[k1] = dir[i][k2]; + k1++; + k2++; + } + if (i == 0) + break; + rd[k1] = '/'; + k1++; + } +``` + +将用户内容导入内核 + +```C + char word; + for (i = 0; i <= k1; i++) + { + word = rd[i]; + put_fs_byte(word, (char *)(buf + i)); + } + return rd; +``` + + 运行测试程序结果如下: + +![img](file:///C:\Users\19076\AppData\Local\Temp\ksohtml16212\wps29.jpg) + +## 三、实验结果与分析 + +添加了8个系统调用,完成了其中必做的四个系统调用,并且全部通过检验。检验结果已截图展示在实验步骤中。 + +## 四、问题与建议(可选) + +<对实验过程中出现的问题进行描述、分析,提出解决思路和方法,无法解决的,要说明原因;记录实验心得体会,提出建议。> +