zoukankan      html  css  js  c++  java
  • 深入理解系统调用-40号调用

    深入了解系统调用过程——40号系统调用


    一、环境描述

      操作系统环境:Ubuntu 20.04

      Linux镜像版本:Linux_5.34.1

      busyBox版本:1.31.1

     gcc库版本:9.3.0


    系统调用过程理论:

    2.1 中断

    我们知道,中断是操作系统的一个重要概念,是操作系统并发操作的的基石。下面是中断的大致分类。

    • 外部中断(硬件中断)
    • 内部中断(软件中断)/异常
      • 故障(fault)
      • 陷阱(trap)【系统调用从用户态进入内核态的方式】

    2.2 用户态和内核态

    在Linux 中分为用户态和内核态两种运行状态。
    对于普通进程,平时都是运行在用户态下,仅拥有基本的运行能力。当进行一些特殊操作,比如说要打开文件(open)然后进行写入(write)、分配内存(malloc)时,就会切换到内核态。
    内核态进行相应的检查,如果通过了,则按照进程的要求执行相应的操作,分配相应的资源。
    这种机制就被称为系统调用,用户态进程发起调用,切换到内核态,内核态完成,返回用户态继续执行,是用户态唯一主动切换到内核态的合法手段(exception 和 interrupt 是被动切换)。

    2.3 系统调用

    系统调⽤的库函数就是我们使⽤的操作系统提供的 API(应⽤程序编程接⼝),API 只是 函数定义。系统调⽤是通过特定的软件中断(陷阱 trap) 向内核发出服务请求,int $0x80 和syscall指令的执⾏就会触发⼀个系统调⽤。C库函数内部使⽤了系统调⽤的封装例程, 其主要⽬的是发布系统调⽤,使程序员在写代码时不需要⽤汇编指令和寄存器传递参数来 触发系统调⽤。⼀般每个系统调⽤对应⼀个系统调⽤的封装例程,函数库再⽤这些封装例 程定义出给程序员调⽤的 API ,这样把系统调⽤终封装成⽅便程序员使⽤的C库函数。

    Linux系统调用过程
    • 当⽤户态进程调⽤⼀个系统调⽤时,CPU切换到内核态并开始执⾏system_call(entry_INT80_32或entry_SYSCALL_64)汇编代码,其 中根据系统调⽤号调⽤对应的内核处理函数
    • 保存现场,执行中断函数,恢复现场,中断返回(简要来说就是这么些)
    Linux系统调用传参(为编写嵌入式汇编做准备)
    • 32位x86体系结构下普通的函数调⽤是通过将参数压栈的⽅式传递的。系统调⽤从⽤户 态切换到内核态,在⽤户态和内核态这两种执⾏模式下使⽤的是不同的堆栈,即进程的⽤户态堆栈和进程的内核态堆栈,传递参数⽅法⽆法通过参数压栈的⽅式,⽽是通过寄存器 传递参数的方式。

    • 32位x86体系结构下寄存器的⻓度⼤32位。除了EAX⽤于传递系统调⽤号外,参数按顺序赋值给EBX、ECX、EDX、ESI、EDI、EBP,参数的个数不能超过6个, 即上述6个寄存器。如果超过6个就把某⼀个寄存器作为指针,指向内存,就可以通过内 存来传递更多的参数。

    • 64位x86体系结构下普通的函数调⽤和系统调⽤都是通过寄存器传递参数,RDI、RSI、RDX、RCX、R8、R9这6个寄存器⽤ 作函数/系统调⽤参数传递,依次对应第 1 参数到第 6 个参数。


    实验操作步骤:(以调用40号系统调用为例)

    • 查询系统调用号
      学号440,通过查阅Linux源代码中的arch/x86/entry/syscalls/syscall_64.tbl 可以找 到40号sendfile系统调用对应的内核处理函数为__x64_sys_sendfile64.

    sendfile 相关介绍:
    sendfile系统调用在内核版本2.1中被引入,目的是简化通过网络在两个本地文件之间进行的数据传输过程。sendfile系统调用的引入,不仅减少了数据复制,还减少了上下文切换的次数。
    sendfile(socket, file, len);

    • 安装开发工具及下载内核源代码
    # 安装相关依赖
    sudo apt install build-essential
    sudo apt install qemu # install QEMU 
    sudo apt install libncurses5-dev bison fiex 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.1.tar.xz 
    xz -d linux-5.4.1.tar.xz 
    tar -xvf linux-5.4.1.tar cd linux-5.4.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)
    # 关闭网络选项中超级服务器选项
    Networking until-->
    [] ineted

    配置相关:

    • 编译和运行内核
    make -j$(nproc) 
    # nproc gives the number of CPU cores/threads available
    # 测试内核是否正常加载运⾏,因为没有⽂件系统终会kernel panic 
    qemu-system-x86_64 -kernel arch/x86/boot/bzImage  
    # 此时还不能正常运行

    • 制作根⽂件系统
    # 下载 busybox源代码解压,解压完成后,配置编译,并安装。
    pwd=~
    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 #pwd = ~ 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⽂件内容如下。记得给init脚本添加可执⾏权限
    #!/bin/sh
     mount -t proc none /proc 
     mount -t sysfs none /sys
     echo "Wellcome YangZhenOS!"
     echo "--------------------" 
     cd home
     /bin/sh 
    
    chmod +x init
    

    • 打包成内存根⽂件系统镜像
    #打包成内存根⽂件系统镜像 
    find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz 
    #测试挂载根⽂件系统,看内核启动完成后是否执⾏init脚本 
    # cd.. 退到rootfs.cpio.gz所在的目录
    qemu-system-x86_64 -kernel linux-5.4.1/arch/x86/boot/bzImage -initrd rootfs.cpio.gz
    
    • 运行结果

    3.3 汇编改写手动触发系统调用

    • 在 rootfs/home 目录下新建文件 sendfile-asm.c

    我们通过写一个小程序触发这一系统调用。使用内联汇编小程序sendfile-asm.c如下:

    int main()
    {
        asm volatile(
        "movl $0x28,%eax
    	" //使⽤EAX传递系统调⽤号40
        "syscall
    	" //触发系统调⽤ 
        );
        return 0;
    }
    
    • gcc编译(这里采用静态编译)
      gcc -o sendfile-asm sendfile-asm.c -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

    • 观察结果

    在我们的构建系统的根目录下可发现可执行文件sendfile-asm的可执行文件

    3.4 通过GDB进行调试

    • 连接进行调试

    接上步启动虚拟机,此时虚拟机会暂停在启动界面。
    在另一个terminal中开启gdb调试 gdb vmlinux。连接进行调试,target remote:1234。

    #pwd=~/linux_5.4.1

    gdb vmlinux

    target remote:1234

    • 为40号系统调用打断点b __x64_sys_sendfile64,通过c继续运行,此时在qemu虚拟机运行可执行文件sendfile。就可发现该文件确实触发了系统调用。即我们通过汇编实现了系统调用。

    3.5 系统调用入口的保存现场和恢复现场

    • 通过bt可观察当前堆栈信息
      • 第一层/ 顶层 __x64_sys_sendfile 系统调用函数所在
      • 第二层 do_syscall_64 获取系统调用号, 前往系统调用函数
      • 第三层 entry_syscall_64 中断入口,做保存线程工作,调用 do_syscall_64
      • 第四层 OS相关

    从中,我们可发现该系统调用涉及到do_syscall_64和entry_SYSCALL_64两个内核函数。


    • 首先断点定位到/home/tx/linux-5.4.1/fs/read_write.c的1511行,让我们去看一看源文件:

    • 进入do_sendfile函数查看,在这里是运行程序的代码段,前期的保存现场工作已经完成。
    • 执行完这个函数,发现回到了函数堆栈上一层的do_sys_call_64 中,接下来要执行的 syscall_return_slowpath 函数要为恢复现场做准备。

    • 继续执行,发现再次回到了函数堆栈的上一层,entry_SYSCALL_64 ,接下来执行的是用于恢复现场的汇编指令.

    最后伴随着pop指令,恢复了rdi和rsp寄存器。系统调用完成。


    四、问题:

    1.Ubuntu 20.4中的glibc 库中,将原本的stime做了修改,但是busyBox中没有做修改,所以busyBox无法编译:

     

    2.Ubuntu 中的对于网络的系统调用做了一些小的修改,但是与BusyBox冲突:

    解决方案:

    参照网上的实例,和查看了Ubuntu 20.4的文档,对busuBox的源码作了修改,

    个人修改后的 适用于 Ubuntu 20.4版本 且 glic版本为 2.3.1 的busybox下载链接如下:

    链接: https://pan.baidu.com/s/1aVO4PzsMRSnLAWEcgEZ5DQ  密码: 7ugd


    五、总结:

    在实验中,掌握了Linux系统的系统接口调用,对于Linux的学习,大有帮助。

  • 相关阅读:
    python 3.5下用户登录验证,三次锁定的编码
    Python之面向对象
    Python基础之模块
    Python基础之yield,匿名函数,包与re模块
    Python基础之函数
    Python基础之字符编码,文件操作流与函数
    Python基础之字符串,布尔值,整数,列表,元组,字典,集合
    Python基础之(判断,循环,列表,字典)
    mysql学习
    linux 下的 正则表达式(awk,sed,awk)学习
  • 原文地址:https://www.cnblogs.com/AmosYang6814/p/12976333.html
Copyright © 2011-2022 走看看