深入理解系统调用

深入理解系统调用

一.实验要求

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

二.实验过程

  1. 实验准备
  • 配置内核选项

    •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)

配置完成后, 将源码重新编译, 然后运行qemu:

qemu-system-x86_64 -kernel arch/x86/boot/bzImage
  • 制作根文件系统

    • 下载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
      
    • 配置

      配置编译成静态链接库

      •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 MengningOS!"
      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
      

    可以看到我们的脚本 已经顺利的被打包到根文件系统中运行了

  1. 调试75号系统调用

    我的学号后两位是75, 查看系统调用表

    arch/x86/syscalls/syscall_64.tbl

该系统调用的功能 是将数据刷新到磁盘上面

fdatasync:

Linux的文件存储的是将数据文件信息inode 分别存储的,在inode中保存了如:名称、文件大小size、修改时间、访问时间等信息我们称作metadata元数据。数据和文件信息inode在物理上是分开存储的,innode修改时需要一次磁盘IO的

  • 编写一个简单的代码来触发fdatasync系统调用

    int main()
    {
        asm volatile(
            "movl $0x4B,%eax\n\t" //使用EAX传递系统调号75
            "syscall\n\t" //触发系统调用;
            );
        return 0;
    }
    
    

    编译这个代码生成可执行文件fdatasync_test(一定要加上-static参数使用静态编译, 因为我们设置了在内核中设置了静态编译选项)

  • 重新打包根文件系统

    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 -S -s -nographic -append "console=ttyS0"
    
  • 调试

    • 启动系统后会暂时停止, 这时我们新开一个终端, 输入命令:
    gdb vmlinux
    target remote:1234
    
    • 接着我们输入c, 虚拟机会继续运行, 完成初始化并进入初始界面

    ​ 因为fdatasync对应的函数入口是_x64_sys_fdatasync, 所以我们在这个打下断点:

    • qemu运行后, 我们在qemu的终端的home目录下, 运行我们之前编译好的fdatasync_test可执行文件, 此时虚拟机会卡主, 我们在调试终端查看调试信息

      可以看到gdb成功的捕捉到了断点

    • 接着我们进入单步调试, 看看系统调用做了哪些工作

      系统首先执行的 syscall_return_slowpath 函数, 准备恢复现场
      接下来, 执行用于恢复现场的汇编指令


      现场恢复完成

三.总结

  1. syscall首先通过MSR寄存器查找中断向量表, 找到中断函数的入口位置, 随后执行swags进行压栈和保存现场的动作
  2. 接着执行do_syscall_64函数, 在ax寄存器中获得系统调用号去执行系统调用
  3. 接着调用我们的75号系统调用
  4. 系统调用完结束后执行yscall_return_slowpath(regs), 准备恢复现场
  5. 恢复现场工作
posted @ 2020-05-26 13:41  Dlemon  阅读(192)  评论(0编辑  收藏  举报