跟踪分析Linux内核5.0系统调用处理过程
学号517原创作品转载请注明出处
本实验来源https://github.com/mengning/linuxkernel/
实验要求
举例跟踪分析Linux内核5.0系统调用处理过程
- 编译内核5.0
- qemu -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img
- 选择系统调用号后两位与您的学号后两位相同的系统调用进行跟踪分析
- https://github.com/mengning/menu
- 给出相关关键源代码及实验截图,撰写一篇博客(署真实姓名或学号最后3位编号),并在博客文章中注明“原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ ”,博客内容的具体要求如下:
- 题目自拟,内容围绕系统调用进行;
- 博客中需要使用实验截图
- 博客内容中需要仔细分析系统调用、保护现场与恢复现场、系统调用号及参数传递过程
- 总结部分需要阐明自己对系统调用工作机制的理解。
实验环境
Ubuntu 17 gcc 4.8
编译内核5.0
1.下载内核5.0内核代码,配置编译Linux内核,使之携带调试信息
2.mkdir LinuxKernel
3.下载内核源码:Linux内核5.0 source code
4.解压到LinuxKernel目录下,cd linux-5.0.1
5.make menuconfig,找到kernel hacking,->Compile-time checks and compiler options,选择 [*]compile the kernel with debug info
6.make
制作根目录
- cd ~/LinuxKernel/
- mkdir rootfs
- git clone https://github.com/mengning/menu.git
- cd menu
- gcc -pthread -o init linktable.c menu.c test.c -m32 -static
- cd …/rootfs
- cp …/menu/init ./
- find . | cpio -o -Hnewc |gzip -9 > …/rootfs.img
启动MenuOS
- qemu -system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img
跟踪调试内核启动
- qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
- cd LinuxKernel/linux-5.0.1
- gdb vmlinux
- (gdb) target remote:1234
分析
首先,几乎所有的内核模块均会在start_kernel进行初始化。在start_kernel中,会对各项硬件设备进行初始化,包括一些page_address、tick等等,直到最后需要执行的rest_init中,会开始让系统跑起来。
那rest_init这个过程中,会调用kernel_thread()来创建内核线程kernel_init,它创建用户的init进程,初始化内核,并设置成1号进程,这个进程会继续做相关的系统初始化。
然后,start_kernel会调用kernel_thread并创建kthreadd,负责管理内核中得所有线程,然后进程ID会被设置为2。
最后,会创建idle进程(0号进程),不能被调度,并利用循环来不断调号空闲的CPU时间片,并且从不返回。
跟踪系统调用
根据学号517,选择
#define __NR__break
可以看出在每一次从用户态进入内核态,都会有上下文切换的问题,那么就要把用户态寄存器上下文保存起来,从运行结果disass显示出来的可以看出,再每次的进行中都会先进行压栈,再结束过程中再进行出栈,以此来保护原函数的运行现场,再最后寄存器集体出栈时恢复。也就是说保护现场就是进入中断服务程序之前,保存使用的寄存器值,也就是说进行压栈操作; 恢复现场就是进入中断服务程序后,恢复之前保存的寄存器数据,也就是进行出栈操作
系统调用分析:
系统中的调用,就是通过系统调用号来给这些system_call来编号,不同功能给予不同的功能号,如这次使用的就是29号 Pause功能,通过这样让系统明确知道用户想要执行的是哪个系统调用。而这之间的传递主要是通过eax寄存器来传递。
除了系统调用号外,系统调用也可能需要传递参数,系统调用由于是从用户态到内核态,两者的栈堆不一样,所以他们之间只能由寄存器来进行传递值,也就是ebx、ecx、edx、esi和edi寄存器中。
总结
系统调用主要就是一种用户态到内核态最后再到用户态的一种过程,用户态可以说成间接操作内存的程序,而内核态也就是通过汇编代码直接操作内存,而他们再转换过程中,由于涉及上下文的切换问题,所以需要对内容进行保护,所以再进入中断程序前,将内核态寄存器的值放入栈中,在程序结束后再进行出栈。
而且由于用户态与内核态的栈堆不同,所以他们之间只能使用寄存器来传递值,也就是eax,ebx、ecx、edx、esi和edi寄存器中,其中eax主要是存放系统调用号,以及调用的返回值,而其他的五个寄存器主要是存放系统调用的参数。
系统调用号是内核为每个系统调用分配的相当于id的事务,用户态进程必须明确的指定使用的系统调用号,来使内核确定使用的服务。