• 深入理解Linux系统调用


    一、实验内容

    1.学号末尾为98,故采用98号系统调用

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

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

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

    二、环境准备

    安装开发工具

    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),添加如下内容到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


    内存根文件系统制作完毕

    三:查看系统调用,编写调用汇编代码

    根据学号后两位在/arch/x86/entry/syscalls/syscall_64.tbl 中找到对应的系统调用函数__x64_sys_getrusage

    查阅可以得知,getrusage()用来获得进程的相关资源信息。如:用户开销时间,系统开销时间,接收的信号量等等;其数据结构如下:

    struct rusage {
        struct timeval ru_utime; /* user time used 用户态使用的时间 */
        struct timeval ru_stime; /* system time used 内核态使用的时间 */
        long   ru_maxrss;        /* maximum resident set size  */
        long   ru_ixrss;         /* integral shared memory size */
        long   ru_idrss;         /* integral unshared data size */
        long   ru_isrss;         /* integral unshared stack size */
        long   ru_minflt;        /* page reclaims */
        long   ru_majflt;        /* page faults */
        long   ru_nswap;         /* swaps */
        long   ru_inblock;       /* block input operations */
        long   ru_oublock;       /* block output operations */
        long   ru_msgsnd;        /* messages sent */
        long   ru_msgrcv;        /* messages received */
        long   ru_nsignals;      /* signals received */
        long   ru_nvcsw;         /* voluntary context switches */
        long   ru_nivcsw;        /* involuntary context switches */
    };

    并且提供wait3,wait4和getrusage来获取该结构。

    #include <sys/time.h>
    #include <sys/resource.h>
    
    int getrusage(int who, struct rusage usage);

    汇编进行系统调用

    写一个test.c的测试文件

    int main()
    {
      asm volatile (
          "movq $0x62, %rax
    	"
          "syscall
    	"
          );
      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进行调试。

    纯命令行启动qemu

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

    在linux-5.4.34目录下启动gdb调试

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

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

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

     可以看到该系统调用涉及到do_syscall_64和entry_SYSCALL_64两个内核函数。

    四、总结

    本次实验中,使用gdb工具断点调试,查看了98号系统调用的调用过程,了解了系统调用的保存和恢复现场。

    整个系统调用,保存和恢复现场过程:syscall指令触发系统调用,通过MSR寄存器找到了中断函数入口

    然后进如 entry_SYSCALL_64( )执行现场保存

    do_syscall_64( )查找调用入口并执行

    准备恢复现场 调用entry_SYSCALL_64( )最后完成现场恢复

  • 相关阅读:
    【css】rem及其替换方案
    【css】如何实现环形进度条
    【js】我们需要无限滚动列表吗?
    【js】再谈移动端的模态框实现
    【js】callback时代的变更
    【js】为什么要使用react+redux
    【js】JavaScript parser实现浅析
    【css】回想下经典的布局
    【JS】温故知新: 从parseInt开始
    【渲染原理】浏览器渲染原理的个人整理
  • 原文地址:https://www.cnblogs.com/zzydexiaowu/p/12969671.html
Copyright © 2020-2023  润新知