原创作品转载请注明出处 + 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 18.04
  • gcc

实验步骤

一 编译内核5.0
方法 下载内核、解压、make
内核下载地址:https://github.com/mengning/linux.git,此内核是中科大孟宁教授,也是国内顶级的linux内核专家,拉出的内核分支,单独由于教学使用。
命令如下

1
2
3
4
5
6
7
8
cd ~
mkdir linux5
cd ~/linux5/
git clone git@github.com:mengning/linux.git
cd linux
make menuconfig #去设置后面用到的gdb调试信息
make i386_defconfig # 第一次没有这一步,导致在启动MenuOS的时候报错,找不到对于i686的启动boot,后来查阅资料,可能使编译的时候不是32位环境,所以尝试加了这个make config之后,重新编译了一次,后来启动成功了!
make -4j ## 多线程去编译,大概需要快两个小时才能编完



note:编译的时候可能会出现有些依赖库未找到,需要我们去手动安装,我编译的时候就遇到了两个问题
遇到了这个错误:Unable to find the ncurses libraries,然后可以sudo apt insatll ncurses-dev解决,但是在我sudo apt install的时候,遇到了这个死锁错误,如下,网上搜了一下,结局了

1
2
3
4
5
E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?
# 解决方法:
ps -A | grep apt
sudo kill -9 processnumber



make完之后,我们需要将git上的一个根文件系统下载下来打包成img文件

1
2
3
4
5
6
7
8
cd ~/linux5/
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 -kernel ~/linux5/arch/x86/boot/bzImage -initrd rootfs.img

三 跟踪调试内核启动
qemu -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
注意:如果不加-append nokaslr选项,start_kernel断点有可能断不住!!!

1
2
3
4
gdb
(gdb)file linux-5.0.1/vmlinux # 在gdb界面中targe remote之前加载符号表
(gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
(gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后

具体如下图

首先,几乎所有的内核模块均会在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时间片,并且从不返回。

增加系统调用
根据学号后两位44,在/usr/include/asm/unistd_32.h中可查得#define __NR_prof 44。
在test.c中增加函数,Prof()。
重新编译制作rootfs.img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int GetPID(int argc, char *argv[])
{
pid_t pid; /* pid_t 需include <sys/types.h> 实际为int型*/
asm volatile(
"mov $0x2c,%%eax\n\t" /* 用eax传系统调用号0x2c */
"int $0x80\n\t"
"mov %%eax,%0\n\t" /* eax存放返回值,送给%0,即pid变量 */
: "=m" (pid)
);
printf("pid: %d\n",pid);
return 0;
}
int main()
{
PrintMenuOS();
SetPrompt("MenuOS>>");
MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
MenuConfig("quit","Quit from MenuOS",Quit);
MenuConfig("time","Show System Time",Time);
MenuConfig("time-asm","Show System Time(asm)",TimeAsm);
MenuConfig("Prof","Show System Prof",Prof);
ExecuteMenu();
}

下面是运行的结果
从结果来看,已经调用成功,但是由于Prof系统函数我没有找到函数详解,下次补上。



实验结果分析

系统调用的触发及参数传递

  • 当调用一个系统调用时,CPU从用户态切换到内核态并开始执行一个system_call和系统调用内核函数。在Linux中通过执行int 0x80来触发系统调用,内核为每个系统调用分配一个系统调用号,用户态进程必须明确指明系统调用号,需要使用EAX寄存器来传递。
  • 系统调用可能需要参数,但是不能通过像用户态进程函数中将参数压栈的方式传递,因为用户态和内核态有不同的堆栈,必须通过寄存器的方式传递参数。
  • 概括:EAX用来传递系统调用号,EBX、ECX、EDX、ESI、EDI、EBP用来传递参数,若参数较多,则把指向内存的指针存入寄存器。

系统调用流程

参考网络资料