深入理解Linux系统调用

实验内容

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

我的学号后两位是32,对应系统调用dup()

实验步骤

实验准备

安装开发工具

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

制作根文件系统

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)

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Welcome LIZHEN OS!"
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 

深入理解Linux系统调用_第1张图片

断点调试

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

同时重新打开一个终端,启动gdb

gdb vmlinux
taregt remote:1234
b start_kernel
c

深入理解Linux系统调用_第2张图片

系统调用

dup用来复制参数oldfd所指的文件描述符。当复制成功是,返回最小的尚未被使用过的文件描述符,若有错误则返回-1.错误代码存入errno中返回的新文件描述符和参数oldfd指向同一个文件,这两个描述符共享同一个数据结构,共享所有的锁定,读写指针和各项全现或标志位。

#include
#include
#include
#include
int main(int argc, char* argv[])
{
    int fd=open("text.txt", O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
    if(fd < 0)
    {
        printf("Open Error!\n");
        return 0;
    }
    int fd2=dup(fd);
    if(fd2<0)
    {
        printf("Error!\n");
        return 0;
    }
    char buf[1000];
    int n;
    while((n=read(STDIN_FILENO, buf,1000)) > 0)  //接受键盘输入,并将其存入buf所指向的缓存中   
    {
        if(write(fd2, buf, n)//将buf所指向的缓存区的n个字节的数据写入到由文件描述符fd2所指示的文件中
        {
            printf("Write Error!!\n");
            return 0;
        }
    }
    return 0;
}

从代码结果可以看出fd这个描述符指向text.txt,然后调用dup函数对 fd进行拷贝,拷贝到d2,然后write(fd2,buf,n)这句将从键盘输入到buf所指的缓冲区的数据写到 fd2所指向的文件后。所以我们在查看text.txt,看到了我们输入的东西。

改成汇编

#include
#include
#include
#include
int main(int argc, char* argv[])
{
    int fd=open("text.txt", O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
    if(fd < 0)
    {
        printf("Open Error!\n");
        return 0;
    }
    int fd2;
    asm volatile(
        "mov    %%eax,%%edi\n\t"
        "mov    $0x20,%%eax\n\t"
        "syscall\n\t"
        "mov    %%eax,%0\n\t"
        : "=m"(fd2)
        : "g"(fd)
     );
    
    if(fd2<0)
    {
        printf("Error!\n");
        return 0;
    }
    char buf[1000];
    int n;
    while((n=read(STDIN_FILENO, buf,1000)) > 0)  //接受键盘输入,并将其存入buf所指向的缓存中   
    {
        if(write(fd2, buf, n) 
 

运行结果

深入理解Linux系统调用_第3张图片

 

 

 

重新构建跟文件系统

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

gdb调试同上

在dup处设置断点

深入理解Linux系统调用_第4张图片

继续执行,可得到系统调用号

深入理解Linux系统调用_第5张图片

 

继续执行到结束,由于本文档存在多个系统调用,执行过长,就不一一截图了。

总结

系统调用的执行,是用户程序触发系统调用之后,CPU以及内核执行调用的过程。在调用之前首先进行一些压栈操作,保存快照,然后在执行切换。当执行完成以后根据快照信息,在重新恢复现场,继续之前的执行。

 

深入理解Linux系统调用_第6张图片

你可能感兴趣的:(深入理解Linux系统调用)