举例跟踪分析Linux内核5.0系统调用处理过程
学号:508。原创作品转载请注明出处,中国科学技术大学孟宁老师的Linux操作系统分析 https://github.com/mengning/linuxkernel/
一、使用Ubuntu编译Linux Kernel 5.0
-
本次实验所用环境:Ubuntu 18.04 gcc version 7.3.0
-
在用户目录下,新建本次实验的文件夹
LinuxKernel
,将内核压缩包linux-5.0.1.tar.xz
放到该文件夹下,并解压,:mkdir LinuxKernel xz -d linux-5.0.1.tar.xz tar -xvf linux-5.0.1.tar
-
安装所需依赖 :
sudo apt-get install bison sudo apt-get install flex
-
然后进行编译,加上
-j8
参数可大大加快编译速度:make i386_defconfig make -j8
-
准备根文件系统:
cd ~/LinuxKernel/ mkdir rootfs git clone https://github.com/mengning/menu.git cd menu/ gcc -o init linktable.c menu.c test.c -m32 -static -pthread cd ../rootfs/ cp ../menu/init ./ find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
-
此时,
LinuxKernel
目录下的结构为:
-
运行MenuOS
qemu -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img
二、使用GDB跟踪调试内核
-
设置断点,打开MenuOS
qemu -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -s -S -append nokaslr // 需要加上“-append nokaslr”这个选项,否则设置的断点不起作用
-
另外打开一个terminal
cd /usr/src/linux-5.0.1/ gdb (gdb) file vmlinux (gdb) target remote:1234 (gdb) break start_kernel
-
start_kernel函数作为内核的入口函数,定义在init/main.c文件中。它主要是初始化系统相关的内容,以便系统进入一种服务状态,提供各种API调用的服务。至此,使用Ubuntu编译Linux内核5.0以及使用gdb跟踪调试内核基本成功。
三、选择系统调用进行跟踪分析
-
我的学号是508,故选择8号,cerat系统调用
-
进入menu文件夹,编辑test.c文件:
cd ~/LinuxKernel/menu/ vim test.c
-
引入头文件
-
使用库函数API方式触发系统调用creat的代码如下所示:
-
使用C代码中嵌入汇编方式触发系统调用creat的代码如下所示:
-
给qemu增加两个有关使用creat系统调用的菜单命令,代码如下所示:
-
按照如上的操作步骤,重新编译Linux内核,并且重新准备根文件系统,如下所示:
gcc -o init linktable.c menu.c test.c -m32 -static -pthread cd ../rootfs/ cp ../menu/init ./ find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
-
再次运行MenuOS,查看执行效果:
qemu -kernel /usr/src/linux-5.0.1/arch/x86/boot/bzImage -initrd ../rootfs.img
-
使用GDB调试,跟踪creat系统调用
四、总结
-
**系统调用:**Linux系统就是通过内核发出的系统调用(system call)实现了用户态进程和硬件设备之间的大部分接口。计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同一时候执行的多个进程都须要訪问这些资源,为了更好的管理这些资源进程是不同意直接操作的,全部对这些资源的訪问都必须有操作系统控制。也就是说操作系统是使用这些资源的唯一入口,而这个入口就是操作系统提供的系统调用(System Call)。在linux中系统调用是用户空间訪问内核的唯一手段,除异常和陷入外,他们是内核唯一的合法入口。
有关creat系统调用的分析 -
creat系统调用:
- 程序中调用库的封装函数creat,该封装例程将系统调用号0x08压入eax寄存器中。
- 调用软中断 Int $0x80汇编指令进入内核。
- 在内核态中首先执行system_call() 函数,接着根据系统调用号在系统调用表(sys_call_table)中找到对应的系统调用服务例程 sys_creat()。
- 执行系统调用服务例程sys_creat()。
- 执行完毕后,跳转到ret_from_sys_call(),终止系统调用程序的执行,从系统调用返回到用户态。