深入理解系统调用
准备工作
下载并解压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\n\t" 5 "syscall\n\t" 6 ); 7 return 0; 8 }
重新打包内存根文件的系统镜像
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
可以看到确实执行到该系统调用出停止了。
具体的调用栈,依次为entry_SYSCALL_64[中断函数的入口],do_syscall_64[获取调用号,前往系统调用函数],__x64_sys_sched_yield[24号系统调用函数]
总结:
1.首先,test程序填充系统调用的寄存器,然后syscall出发系统调用,找到中断入口函数entry_syscall_64,主要是做准备工作.由用户态转到内核态, 并做一些准备工作, 保持用户态的信息(堆栈, 寄存器)待系统调用完之后恢复现场.初始化内核, 寄存器,堆栈 等等.
2.entry_SYSCALL_64 调用do_syscall_64[获取调用号,前往系统调用函数],然后通过eax的值到达真正的系统调用。3.系统调用完成, 恢复现场(通用寄存器, 旧的栈. flags).