深入理解Linux系统调用

Posted sa19225518朱帅帅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Linux系统调用相关的知识,希望对你有一定的参考价值。

一、实验内容

  • 找一个系统调用,系统调用号为学号最后2位相同的系统调用
  • 通过汇编指令触发该系统调用
  • 通过gdb跟踪该系统调用的内核处理过程
  • 重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化

二、实验步骤

由于启动内核需要根文件系统,我们上次的实验制作了简单的内核,但是没有制作根文件系统,内核检测不到初始化文件,

所以内核无法正常启动。

2.1 环境配置

这里和第一个实验类似,这里就不截图了。

安装开发工具

sudo apt install build-essential
sudo apt install qemu # install QEMU 
sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev

下载内核源代码

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 cd linux-5.4.34

配置内核编译选项

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  #  此时应该不能正常运行

 

 

2.2 制作根文件系统

电脑加电启动⾸先由bootloader(启动加载程序)加载内核,内核紧接着需要挂载内存
根⽂件系统,其中包含必要的设备驱动和⼯具, bootloader加载根⽂
件系统到内存中,内核会将其挂载到根⽬录/下,然后运⾏根⽂件系统
init脚本执⾏⼀些启动任务,最后才挂载真正的磁盘根⽂件系统

我们这⾥为了简化实验环境,仅制作内存根⽂件系统。这⾥借助
BusyBox 构建极简内存根⽂件系统,提供基本的⽤户态可执⾏程序。


⾸先从https://www.busybox.net下载 busybox源代码解压,解压完成
后,跟内核⼀样先配置编译,并安装。

axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2
tar -jxvf busybox-1.31.1.tar.bz2
cd busybox-1.31.1

安装完成后我们开始制作根文件系统

make menuconfig 
#记得要编译成静态链接,不⽤动态链接库。
Settings  --->
    [*] Build static binary (no shared libs) 
#然后编译安装,默认会安装到源码⽬录下的 _install ⽬录中。 
make -j$(nproc) && make install

制作内存根文件系统镜像

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/

我们还要准备init脚本文件放在根文件系统跟目录下(rootfs/init),这样内核才能加载启

动文件从而启动,添加如下内容到init文件。

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

#给init脚本添加可执行权限
chmod +x init


#打包成内存根文件系统镜像
 find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz


#测试挂载根文件系统,看内核启动完成后是否执行init脚本
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz

可以看到内核成功启动

 

 

2.3 找一个系统调用,系统调用号为学号最后2位(18)相同的系统调用

Linux源代码中的arch/x86/entry/syscalls/syscall_32.tblarch/x86/
entry/syscalls/syscall_64.tbl 分别定义了32x86x86-64的系统调
⽤内核处理函数
通过查看linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tbl系统调用表

可以看到18号系统调用为pwrite64,此系统调用功能为

从给定偏移量处读取或写入文件描述符,对应内核处理函数为:__x64_sys_pwrite64。

 

 

 

2.3.1 编写汇编代码调用该系统调用

创建test.c文件

#include <stdio.h>

int main()
{
    asm volatile(     
            "movl $0x12, %eax\\n\\t" //传递系统调用号
            "syscall\\n\\t" //系统调用
            );

        return 0;
}

使用下面命令将test.c进行静态编译

gcc -o test test.c -static

将形成的可执行文件放到rootfs/home/目录下,然后重新打包rootfs文件夹

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

这样,我们就完成了系统调用的准备,接下来使用gdb进行调试。

 

2.4 gdb跟踪系统调用

先输入如下命令:

 因为我们不需要看图形界面,所以使用纯命令启动

qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"

 

然后新开启一个terminal

输入如下命令来开启gdb调试并在我们要调用的系统调用处设置断点:

cd linux-5.4.34/
gdb vmlinux
target remote:1234
b __x64_sys_pwrite64 

运行如下:

 

 然后在gdb里面输入c使qemu继续运行,在qemu虚拟机里面,

输入./test ,然后就可以在gdb页面看到系统执行到了我们打断点的位置

 

 

继续单步执行:

 

 在系统调用执行完成之后就开始恢复现场,其中entry_SYSCALL_64代码如下

 

 

 

继续单步执行,直到恢复现场完成:

 

 

三、实验总结

pwrite64 函数通过系统调用函数__x64_sys_pread64出发 ,其功能由ksys_pread64() 实现;

本次实验通过汇编指令传递系统调用号并触发系统调用,找到函数中断入口。通过do_syscall_64 函数

得到了系统调用号 ,执行系统调用内容。然后程序跳到read_write.c文件,触发了ksys_pread64() 函数,

执行完毕后,又跳回了do_syscall_64 中的syscall_return_slowpath() ,准备进行现场恢复操作,

执行entry_SYSCALL_64() ,在entry_SYSCALL_64() 里通过swapgs 指令保存现场,

并准备跳回用户态。最后执行popq ,完成堆栈切换。

 

以上是关于深入理解Linux系统调用的主要内容,如果未能解决你的问题,请参考以下文章

深入理解Linux系统调用

深入理解Linux系统调用

深入理解Linux系统调用

LinuxLab2---深入理解Linux系统调用

深入理解系统调用

深入理解系统调用