2018-2019-1 20189206 《Linux内核原理与分析》第六周作业
linux内核分析学习笔记 ——第五章 系统调用的三层机制
学习重点——深入理解系统调用的过程
给MenuOS添加命令
添加命令的方式较为简单,在LinuxKernel/menu/test.c
目录下,打开test.c
,main函数中的MenuConfig就是对应的系统调用的功能增加,之后在上面给出具体实现即可。之后就可以在MenuOS中使用该系统调用函数。
使用gdb跟踪内核函数sys_getuid
上面在MenuOS中添加了功能,我这里改成了getuid获得当前用户的id号,调试步骤和实验三基本一样
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
冻结内核运行,然后利用gdb调试工具,加载内核,连接端口1234
file linux-3.18.5/vmlinux
target remote:1234
之后在系统调用sys_getuid
处设置断点,当启动MenuOS调用功能Getuid后 一旦调用系统函数,程序暂停。可以看到程序停在了sys_getuid
处,但该系统调用是利用宏函数实现的,经过预编译处理后是sys_getuid
的形式。
在system_call
位置设置断点并不能停下,还是会停在sys_getuid这里,因为system_call并不是一个正常的函数,只是一段汇编代码的起点,内部没有严格的函数调用堆栈机制,所以gdb不能完成跟踪任务
系统调用的处理过程
在用户态中有一个c语言提供的APIxyz()
,在用户看来这就是系统调用,而xyz()
中调用了SYSCALL
来触发系统调用。
即中断向量0x80对应system_call
中断服务程序入口。
start_kernel函数执行内核启动的初始化工作,其中会调用trap_init函数,在上图所示目录中可以看到以下代码。
当接受到int 0x80 中断请求后,通过set_sysytem_trap_gate
函数将中断向量和入口函数绑定,将直接进入中断,也就是跳转到system_call
的位置。
这里将trap_init
中绑定中断向量和入口函数的代码中的宏定义实现,可以看到系统调用对应的中断向量就是0x80
system_call函数的理解
`system_call`是一段中断服务程序入口的汇编代码,就是系统调用的处理过程,是系统调用用户态和系统调用内核态想转换的过程。中断过程都会有现场保护和恢复现场代码一开始的SAVE_ALL和restore_all就是实现了中断上下文过程。
sys_call_table
是一个系统调用表,EAX寄存器保存了传递的系统调用号,来调用相应的系统调用,调用结束,EAX寄存器还要保存系统调用的返回值。
syscall_exit中判断当前的任务是否需要进程调度,如果需要进程调度进入syscall_exit_work
,执行进程调度,结束后恢复现场返回用户态。
系统调用函数
上图所示是系统调用system_call的流程图
- SAVE ALL 是中断发生后保存现场的宏
cmpl $(nr_syscalls),%eax
判断检查系统调用号的合法性如果系统调用号大于nr_syscalls 那么出现异常,跳入异常处理syscall_badsys
-call *sys_call_table(,%eax,4)
通过系统调用号在系统调用表中找到相应的系统调用内核处理函数,就是将函数API和系统调用联系起来。movl %eax,PT_EAX(%esp)
作用是将系统调用返回值入栈syscall exit
检查是否有任务要处理,如果发生进程调度需要进入syscall_exit_work
- 进程间通信会有信号发生,所以进入进程调度后会有
work_pending
、work_notifysig
信号处理、和work_resched
等处理work_resched
中的schedule存着进程调度的代码,也就是进程调度的时机点
- 进程间通信会有信号发生,所以进入进程调度后会有
restore_all
和iret
用于恢复现场和返回用户态。
Linux内核编译实践中遇到的问题
- 在为函数添加系统调用号时的路径问题
sudo gedit /usr/src/linux-3.14.40/arch/x86/syscall/syscall_32.tbl。
根据文档我在这个路径下并没有找到syscall文件夹,也就无法修改 syscall_32.tbl
最后参考了一篇博客,我想问题应该是内核版本更新,目录有了一定的修改,最后找到了路径
- 使用
make bzImage
时出现错误
根据错误提示,我考虑问题出在了内核调用时添加的系统调用格式出现了问题对比之后,我对代码进行更改,结果编译成功
内核编译时间非常久,编译好了之后无法启动,错误问题如下
后来发现是虚拟机的内存不足的原因,重新设置了虚拟机的内存之后成功进入虚拟机,利用命令可以看到该内核版本已经是刚刚编译的4.18.18
写一个程序调用我们刚刚创建的系统调用,返回值得到的是我们当时设定的返回值
参考这篇博客给我提供了解决问题的很多灵感:
Linux4.18.9添加系统调用传递参数示例