Linux实验——跟踪分析Linux内核5.0系统调用处理过程

Linux实验——跟踪分析Linux内核5.0系统调用处理过程

学号后三位为342
原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/

实验工具

  • Linux 5.0.1 内核
  • VMware Workstation Pro
  • Ubuntu 18.04

实验目的

  • 编译Linux内核5.0.1
  • 选择系统调用号后两位与您的学号后两位相同的系统调用进行跟踪分析

实验过程

  • 下载并编译Linux 5.0.1内核
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.tar.xz
//下载Linux 5.0.1内核

xz -d linux-5.0.1.tar.xz 

tar -xvf linux-5.0.1.tar 

cd linux-5.0.1

make menuconfig
//编译过程中出错

经过查阅资料,解决问题

sudo apt-get install bison

sudo apt-get install flex

完成编译,找到kernel hacking->Compile-time checks and compiler options->[*] compile the kernel with debug info
Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第1张图片
开始编译

make -j8
//make -j* * 为核心数
//中间报错,添加下面的库
sudo apt-get install build-essential libssl-dev libelf-dev
libncurses-dev

完成编译(时间真的有点长)
Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第2张图片

  • 制作根文件系统
mkdir rootfs

git clone https://github.com/mengning/menu.git
//下载MenuOS

cd menu

gcc -pthread -o init linktable.c menu.c test.c -m32 -static
//出错

继续纠正错误
Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第3张图片

sudo apt-get install gcc-multilib

继续向下执行

cd ../rootfs

cp ../menu/init ./

find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img

启动MenuOS(如果不加-append nokaslr选项,start_kernel断点有可能断不住!!!)

 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

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

Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第4张图片
注意:如果出现上图情况,请采用以下解决措施

make i386_defconfig
//⽣成32位x86的配置⽂件
qemu-system-x86_64...

编译完成
Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第5张图片

  • 内核分析

首先,几乎所有的内核模块均会在start_kernel进行初始化。在start_kernel中,会对各项硬件设备进行初始化,包括一些page_address、tick等等,直到最后需要执行的rest_init中,会开始让系统跑起来。
那rest_init这个过程中,会调用kernel_thread()来创建内核线程kernel_init,它创建用户的init进程,初始化内核,并设置成1号进程,这个进程会继续做相关的系统初始化。
然后,start_kernel会调用kernel_thread并创建kthread,负责管理内核中得所有线程,然后进程ID会被设置为2。(参考pianogirl123)

  • 跟踪系统调用
    • 因为学号后两位为42,在/usr/include/asm/unistd_32.h中查找对应的系统调用

    • 内核源文件中的调用是#define __NR_pipe 42
      一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信。 一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据

    • 在test.c下添加系统调用#int Pipe()

int Pipe(int argc, char *argv[])
 {
     int result=-1;
     int fd[2];
     result = pipe(fd);
     if(result == -1)
        printf("fail to create pipe\n");
     else
        printf("pipe was created successfully\n");
     return 0;
}

在此文件中的main函数里加入以下代码,并重新编译制作rootfs.img文件

MenuConfig("pipe","Create Pipe",Pipe);

在系统中断时重新开启一个新终端,在其中调试

gdb

gdb>file linux-5.0.1/vmlinux

gdb>target remote:1234 

gdb>break sys_pipe

当调用系统函数时,触发断点
Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第6张图片
继续调试
Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第7张图片
Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第8张图片
结束调试
Linux实验——跟踪分析Linux内核5.0系统调用处理过程_第9张图片

实验总结

  • 用户态、内核态和中断

    • 内核态:在高的执行级别下,代码可以执行特权指令,访问任意的物理地址,这时的CPU就对应内核态
    • 用户态:在低级别的指令状态下,代码 只能在级别允许的特定范围内活动。在日常操作下,执行系统调用的方式是通过库函数,库函数封装系统调用,为用户提供接口以便直接使用。
    • 在Linux下0级表示内核态,3级表示用户态
    • 中断处理是从用户态进入内核态的主要方式,系统调用是一种特殊的中断。中断/int指令会在堆栈上保存用户态的寄存器上下文,其中包括用户态栈顶地址、当时的状态字、cs:eip的值,以及内核态的栈顶地址、当时的状态字、中断处理程序入口。中断处理结束前的最后一件事就是恢复现场,退出中断程序,恢复保存寄存器的数据。
  • 系统调用的意义

    操作系统为用户态进程与硬件设备进行交互提供的一组接口——系统调用

    • 把用户从底层的硬件编程中解放出来
    • 极大的提高了系统的安全性
    • 使用户程序具有可移植性
  • API(应用程序编程接口)与系统调用的关系

    • API是一个系统调用封装成的一个函数定义
    • 系统调用通过软中断向内核发出一个明确的请求
    • Libc库定义的一些API引用了封装例程,目的是发布系统调用,让程序员写代码的时候可以通过函数调用而非汇编指令触发一个系统调用
    • 一般每个系统调用对应一个封装例程,库再用这些封装例程定义出给用户的API

你可能感兴趣的:(Linux实验——跟踪分析Linux内核5.0系统调用处理过程)