“Linux内核分析”实验五
系统调用(上)
作者:何振豪
原创作品转载请注明出处 http://www.cnblogs.com/scoyer/p/6624232.html
《Linux内核分析》MOOC课程 http://mooc.study.163.com/course/USTC-1000029000
实验步骤(由于实验楼git被墙住了,所以在自己的PC下进行试验)
1. 删除menu目录
从git上下载最新版(带有time系统调用的版本),由于我之前加载的就是git上新版的menu,所以就省去了这一步。
2. 修改menu里面的test.c文件
在test.c中加上上一个实验写的两个函数,然后按照MenuConfig的参数格式加上菜单选项,代码如下:
3. 然后键入下面命令
gcc -o init linktable.c menu.c test.c -m32 -static –lpthread cd ../rootfs cp ../menu/init ./ find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
编译成init文件然后打包成一个文件系统。编译成功后,启动Menu系统
cd ~/LinuxKernel/ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
输入help得到如下界面就说明成功了。
可以看到里面有我们新添加的uid选项。
4. 启动gdb进行调试:
具体启动方法可以参照实验三。
设置断点sys_getuid并且利用continue指令执行:
可以看到整个系统已经开机完毕,没有触发断点,因为sys_getuid还没有被调用,所以我们需要执行uid的指令进行系统调用执行到断点处:
利用continue继续执行:
这样就会跳出断点,列出附近代码:
实验总结
了解系统调用实际上只要分析好具体的调用过程就好了。姑且先把我们的函数叫做f,f里面调用了getuid这个系统提供的接口,f利用getuid这个函数来获取当前进程的用户id。因此上述调用的具体过程可以描述为
(1)f调用了getuid,需要它返回该进程的用户id;
(2)getuid先是做好了保存现场SAVE_ALL的工作,然后设置好相应的寄存器参数,使用软中断int80x进行系统调用system_call;
(3)system_call从eax取出系统调用号,检查系统调用号合法并且该系统调用没有被跟踪后,从系统调用表sys_call_table里面找到内核函数入口地址之后,跳到相应地址去调用对应的服务例程
sys_getuid,注意这时候系统处于内核态;
(4)sys_getuid经过一些处理得到进程的用户id之后,保存到eax,返回到system_call;
(5)system_call继续执行就到ret_system_call来结束系统调用,返回到用户态,利用iret指令回到之前从getuid中断的下一句的位置,恢复调用前的现场RESTORE_ALL,接着将得到的uid返回到f;
(6)f得到这个uid才是真正的结束。
当然实际上的系统调用没有这么简单,这只是一个大概的轮廓,因为还要考虑到同时存在的几个进程的因素,处理一些信号产生的问题等。