深入理解Linux系统调用

实验目的

1.找一个系统调用,系统调用号为学号最后2位相同的系统调用,我学号最后两位为90。

2.通过汇编指令触发该系统调用。

3.通过gdb跟踪该系统调用的内核处理过程。

4.重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化。

首先下载必要工具

sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev

下载linux源码

sudo apt install axel
axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/ linux-5.4.34.tar.xz 
xz -d linux-5.4.34.tar.xz
tar -xvf linux-5.4.34.tar

配置内核编译选项

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

 

 

 编译和运行内核

make -j$(nproc) # nproc gives the number of CPU cores/threads available
# 测试⼀下内核能不能正常加载运⾏,因为没有⽂件系统终会kernel panic 
qemu-system-x86_64 -kernel arch/x86/boot/bzImage  #  此时应该不能正常运行,会报kernel pannic错误,因为没有文件系统。

制作内存根文件系统

mkdir rootfs
cd rootfs
cp ../busybox-1.31.1/_install/* ./ -rf
mkdir dev proc sys home
sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/<br>

touch init创建脚本文件init在目录下,并且输入:

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Wellcome OS!"
echo "--------------------"
cd home
/bin/sh

制作内存根文件系统的过程

step1 : 打包成内存根文件系统镜像

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

step2 : 测试挂载根文件系统,看内核启动完成后是否执行init脚本!

qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz

 可见脚本Wellcome OS 已经执行 ,则可以进行下一步

用汇编代码编写系统调用程序

学号后两位是90,对应的系统调用为chmod

编写test_chmod.c程序,调用90号系统调用。

#include<stdio.h>
int
main() { asm volatile( "movl $0x5A,%eax\n\t" //使⽤EAX传递系统调⽤号90 "syscall\n\t" //触发系统调⽤ ); printf("-----------chmod---------------\n"); return 0; }

用gcc静态编译,生成可执行文件

gcc -o test_chmod test_chmod.c -static

执行可执行文件,输出

将文件放至rootfs/home路径下,重新执行如下两条语句:

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz

 可以看到test_chmod可执行文件已经在文件系统中了

gdb调试

step1 : 用如下命令行启动qemu

qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -s -S

step2 : 新开一个terminal,启动gdb

cd linux-5.4.34/ # 切到对应的文件夹
gdb vmlinux

step3 : 在__x64_sys_chmod系统调用打断点

(gdb) target remote:1234
(gdb) b __x64_sys_chmod

这个时候qemu是黑屏,这个时候输入c ,qemu开始执行。

 

然后在qemu中执行test_chmod,在gdb中可以捕获到断点.

 可以使用bt来观察方法的堆栈从而知道 entry_SYSCALL_64是系统调用的入口,它在系统调用之前已经初始化了该方法对应的内核堆栈

 使用n 进行单步执行进行debug调试,可以看到保存现场信息的步骤:

继续调试 直到最后结束,可以看到恢复现场以及系统调用返回,以及从内核态返回用户态的步骤:

 

实验总结

本次实验从调试源码的角度,调试了linux内核系统调用的过程,深刻理解了入口的保存现场、恢复现场和系统调用返回的过程,以及系统调用过程中内核堆栈状态的变化。

总而言之:应用程序调用API,然后trap陷入,进入系统迪用,进入内核态,保存现场执行中断处理程序 然后恢复现场返回用户态。其中保存现场和恢复现场都是通过操作内核的堆栈实现的。同时,这次实验也告诉我们,学习一门技术应该深入底层进行了解和实践,眼见为实,而不能只学习理论,这样是不够扎实的。

 

posted @ 2020-05-26 19:52  驿站Even  阅读(196)  评论(0)    收藏  举报