2018-2019-1 20189218《Linux内核原理与分析》第六周作业

时间:2023-03-09 19:32:28
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

向menuOS中增加命令

修改menu目录下的test.c文件,增加自己的函数定义,并在修改main()函数,按照前面的menuconfig的写法写好自己的menuconfig。

我选择的是access系统调用,对于封装好的access()函数,接受两个参数,第一个为mode,接受四个默认值——X_OK,R_OK,W_OK,F_OK,分别表示是否可执行、是否可读、是否可写、文件是否存在;第二个参数是文件名。我添加的命令如下所示:
2018-2019-1 20189218《Linux内核原理与分析》第六周作业
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

命令运行结果如下所示:
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

调试系统调用

按照课本第三章讲的步骤,在sys_access处设置断点,可以追踪调试该系统调用,这里不再赘述如何才能对其进行调试,而是专注于调试本身的过程。

如图所示,内核停在sys_access
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

接下来我连续执行s(step),比较奇怪的是前三次调试器中代码位置没有变化。我们可以看到这三次调用的函数依次是SYSC_accsee,SyS_faccessat和SYSC_faccessat。是否可以理解为每一个系统调用SyS_*都对应着另一层封装SYSC_*呢?调试器中代码位置没有变化是为什么呢?如果SyS_access会调用SYSC_access,又是写在哪里的呢?如果是函数的话调试器应该会跳转过去,推测可能是用宏实现的。

另外查阅SYSC_access的相关资料,得知该函数确实由SyS_access调用,功能是返回sys_faccessat的值。
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

通过命令s一步一步往下执行,进入不能引起我注意的函数就finish,直到遇到了schedule函数,这时候系统调用已经执行完了,但还没有返回用户态,我做出这个判断的依据是menu窗口还有没打印程序执行的结果:
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

schedule函数结束后返回用户态,在menu窗口打印程序执行的结果,这和我们在上一章了解的内容也保持一致,即在执行完sys_access后会执行ret_from_sys_call,此时仍处于内核态,是进程调度最常见的时机,如果没有进程调度或者进程调度结束后才会执行iret返回用户态。
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

我的内核中没有发生进程调度,返回用户态并在menu窗口打印结果:
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

从图中可以发现,从schedule返回用户态时不能通过n或者s命令,会出现

cannot find bounds of current function

同样不是很明白为什么,大概是用户态和内核态的交界比较特殊。

system_call流程图

system_call位于kernel/entry_32.S中,是一段特殊的汇编代码,通过课本上简化代码可以很容易的理解他的工作流程:
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

问题

调试器不显示代码问题

前面已经提到过了,按照我的理解,以及调试器中显示的信息也确实应该是这样,sys_access会调用SYSC_access,SYSC_access会调用sys_faccessat,sys_faccessat会调用SYSC_faccessat。但在调试器中并由没相关代码出现,所以我对这个理解还有些怀疑。再来一遍SYSC_access的相关资料

可能要修改menu配置

在通过s指令进入的一些函数中不能通过sn来进行下一步,会出现
如下问题:
2018-2019-1 20189218《Linux内核原理与分析》第六周作业

因为没有对我追踪系统调用造成影响,所以我选择通过finish跳出该函数,后来搜索该问题发现可能要修改menu的配置,相关资料

添加自己的系统调用

原内核版本4.15.0-39-generic,选用4.13.13版本内核做该实验,但是在进入新编译的内核后,不论是通过汇编的方式还是syscall的方式都无法成功执行自己的系统调用,通过syscall方式调用,errno返回错误值38,经搜索是unimplemented function,如果通过内嵌汇编调用,%eax中的返回值为-9,errno不会记录错误值。

我比较疑惑的是,为什么自己新加的系统调用不和内核原来的系统调用写在一起呢?经过这次实验也知道内核中会有总共系统调用数的信息,但实验中并没有步骤修改那个值。同时在unistd.h中发现系统调用的数量并不很多,但我们修改的.tbl文件中却有400多个系统调用,这些都会被编译到内核中吗?是不是在make oldconfig时有一些选项要注意一下?

后来将自己写的系统调用传递参数类型由string改成int,运行成功,为什么string类型做参数就不可以呢?编译中也没有提示错误信息。

调试system_call

gdb并不支持直接对system_call进行调试,我们可以通过UML将内核当成一个进程启动,或者利用虚拟机的优点,在宿主机通过vmware调试内核,具体参照用KGdb和VMware调试Linux内核,System Call