深入理解系统调用

深入理解系统调用

准备工作

下载并解压kernel
1 #之前已下载过直接解压即可
2 xz -d linux-5.4.34.tar.xz 
3 tar -xvf linux-5.4.34.tar 
4 cd linux-5.4.34

配置内核选项

 1  make defconfig # Default configuration is based on 'x86_64_defconfig' 
 2  make menuconfig 
 3  # 打开debug相关选项
 4  Kernel hacking ---> 
 5  Compile-time checks and compiler options ---> 
 6  [*] Compile the kernel with debug info 
 7  [*] Provide GDB scripts for kernel debugging 
 8  [*] Kernel debugging 
 9  # 关闭KASLR,否则会导致打断点失败
10  Processor type and features ----> 
11  [] Randomize the address of the kernel image (KASLR)

编译并测试能否使用qemu启动内核

1 make -j
2 qemu-system-x86_64 -kernel arch/x86/boot/bzImage

此时由于没有文件系统,所以最终kernel panic==》需要制作根文件系统

使用busybox制作根文件系统
1 axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2 
2 tar -jxvf busybox-1.31.1.tar.bz2 
3 cd busybox-1.31.1
4 make menuconfig 
5 [*] Build static binary (no shared libs)#设置编译成静态链接
6 make -j$(nproc) && make install #编译并安装

  制作根文件系统的镜像

 1 mkdir rootfs 
 2 cd rootfs 
 3 cp ../busybox-1.31.1/_install/* ./ -rf 
 4 mkdir dev proc sys home 
 5 sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
 6 在根目录下添加init脚本,内容如下:
 7  #!/bin/sh 
 8  mount -t proc none /proc 
 9  mount -t sysfs none /sys 
10  echo "Wellcome MengningOS!" 
11  echo "--------------------" 
12  cd home 
13  /bin/sh
14  赋予其可执行权限
15  chmod +x init

把根文件系统打包生成rootfs.cpio.gz

qemu-system-x86_64 -kernel /home/lmm/下载/linux-5.4.34/arch/x86/boot/bzImage  -initrd rootfs.cpio.gz #在rootfs启动

根据显示结果可得:执行了init脚本。

测试能否使用gdb跟踪调试

bash1:

1 qemu-system-x86_64 -kernel /home/lmm/下载/linux-5.4.34/arch/x86/boot/bzImage -initrd  rootfs.cpio.gz -S -s #-s,在TCP 1234端⼝上创建了⼀个gdbserver -S 启动时暂停虚拟机,等待 gdb 执⾏ continue指令

bash2:

1 cd linux-5.4.34/ 
2 gdb vmlinux 
3 (gdb) target remote:1234 
4 (gdb) b start_kernel
5 (gdb) c

可以看到可成功调试。

汇编指令触发该系统调用并通过gdb跟踪该系统调用的内核处理过程

系统调用的过程图

学号19225224,选择24号系统调用

ched_yield()会主动放弃当前CPU给其他进程使用;但是如果当前CPU上无其他进程等待执行,则直接返回继续执行当前进程。

调用sched_yield()之后当前进程会被移动到进程优先级等待队列尾部,让相同或者更高优先级进程运行。

在rootfs/home下添加test[汇编文件],通过该程序调用该系统调用

1 int main()
2 {
3     asm volatile(
4     "movl $18,%eax
	" 
5     "syscall
	"  
6     );
7     return 0;
8 }

重新打包内存根文件的系统镜像

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz

首先在gdb出给该系统调用出打上断点,然后在qemu上执行该test看是否会执行该系统调用。

可以看到确实执行到该系统调用出停止了。

具体的调用栈,依次为entry_SYSCALL_64[中断函数的入口],do_syscall_64[获取调用号,前往系统调用函数],__x64_sys_sched_yield[24号系统调用函数]

然后继续单步执行,到了entry_SYSCALL_64 , 开始进行恢复现场

最后的pop rsp与pop rdi进行堆栈的切换,从而结束系统调用。

总结:

1.首先,test程序填充系统调用的寄存器,然后syscall出发系统调用,找到中断入口函数entry_syscall_64,主要是做准备工作.由用户态转到内核态, 并做一些准备工作, 保持用户态的信息(堆栈, 寄存器)待系统调用完之后恢复现场.初始化内核, 寄存器,堆栈 等等.

2.entry_SYSCALL_64 调用do_syscall_64[获取调用号,前往系统调用函数],然后通过eax的值到达真正的系统调用。3.系统调用完成, 恢复现场(通用寄存器, 旧的栈. flags).

 

原文地址:https://www.cnblogs.com/tlxclmm/p/12976966.html