首页 > 其他 > 详细

深入理解系统调用

时间:2020-05-24 19:20:36      阅读:51      评论:0      收藏:0      [点我收藏+]

实验要求

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

理论知识

用户态

用户态是用户程序执行时机器所处的状态,是具有较低特权的执行状态,它只能执行规定的指令,只能访问指定的寄存器和存储区。

核心态

核心态是操作系统管理程序执行时机器所处的状态。它具有较高的特权,能执行包括特权指令的一切指令,能访问所有寄存器和存储区。特权指令只能由操作系统内核使用,如IO指令、设置中断屏蔽指令、清内存指令、存储保护指令和设置时钟指令

用户态和核心态的转换

划分核心态和用户态后,这两类程序以及各自的存储空间被严格区分。当用户态想要调用核心态程序时,可以通过系统调用,即执行访问核心态的命令,引起中断,由中断系统转入操作系统内的相应程序

系统调用过程

  1. 首先准备并传递系统调用所需的参数,通过陷入trap指令(汇编中的entry_INT80_32或entry_SYSCALL_64)进入操作系统的内核,此时将从用户态进入核心态;
  2. 执行相应的系统调用函数,使用特定的内核功能;
  3. 最后将处理结果返回给用户进程,此时将从内核态返回用户态
    技术分享图片

linux的系统调用

  1. 在linux中参数传递中通过压栈完成。64位x86体系结构下普通的函数调用和系统调用都是通过寄存器传递参数,RDI、RSI、RDX、RCX、R8、R9这6个寄存器用作函数/系统调用参数传递,依次对应第 1 参数到第 6 个参数
  2. 然后通过使用C库函数和int $0x80/syscall汇编代码触发系统调用,引发中断时会有保存现场,执行中断函数,恢复现场,中断返回等操作
  3. 返回结果ret

搭建实验环境

下载内核源码

注意,由于使用实验一的搭建的内核会出现很多莫名的错误,所以重新使用实验一下载好的tar压缩包重新解压搭建。另外需要的工具也与实验一一致,详见https://www.cnblogs.com/junljw/p/12878158.html
tar -xvf linux-5.4.34.tar
cd linux-5.4.34

配置内核选项

make defcon?g
make menucon?g
选中Kernel hacking --->
再选中Compile-time checks and compiler options --->
勾选Compile the kernel with debug info 和 Provide GDB scripts for kernel debugging
技术分享图片
返回上一页,勾选Kernel debugging
技术分享图片
返回首页,选中Processor type and features ---->
关闭Randomize the address of the kernel image (KASLR)
技术分享图片
保存后退出即可

编译和运行内核

make -j$(nproc)#时间较长,耐心等待
qemu-system-x86_64 -kernel arch/x86/boot/bzImage
技术分享图片
此时内核不能正常加载属于正常

制作根文件系统

axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2
注意请下载在根文件目录,而不是linux-5.4.34内
tar -jxvf busybox-1.31.1.tar.bz2
cd busybox-1.31.1

make menucon?g
选中Settings --->
然后勾选Build static binary (no shared libs)
技术分享图片

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

镜像设置

?nd . -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
技术分享图片

查看系统调用指令

选定系统调用getpriority

由于我的学号尾数为40,首先查看了sendfile指令
技术分享图片
但由于sendfile会触发read和write的系统调用,引发多次上下文切换,不易于跟踪,查看了140的系统调用为getpriority
技术分享图片

getpriority分析

getpriority函数原型为int getpriority( int which, int two );
该函数返回一组进程的优先级。参数 which 和 who 组合确定返回哪一组进程的优先级。which 的可能取值以及 who 的意义如下:

  • PRIO_PROCESS : 一个特定的进程,此时 who 的取值为进程 ID
  • PRIO_PGRP : 一个进程组的所有进程,此时 who 的取值为进程组 ID
  • PRIO_USER : 一个用户拥有的所有进程,此时参数 who 取值为实际用户 ID getpriority() 函数如果调用成功返回指定进程的优先级,如果出错将返回 -1

    编写代码mytest.c触发系统调用,mytest.c内容如下

    #include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/resource.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    
    int main(void)
    {
        pid_t pid;
       int stat_val = 0;
       int oldpri, newpri;
    
        printf("nice study\n");
    
        pid = fork();
       switch( pid ) {
           case 0:
           printf("Child is running, Curpid is %d, Parentpid is %d\n",
               pid, getppid());
    
           oldpri = getpriority(PRIO_PROCESS, getpid());
           printf("Old priority = %d\n", oldpri);
    
           newpri = nice(2);
           printf("New priority = %d\n", newpri);
    
           exit(0);
    
           case -1:
           perror("Process creation failed\n");
           break;
       
           default:
           printf("Parent is running,Childpid is %d, Parentpid is %d\n", pid, getpid());
           break;
        }
    
        wait(&stat_val);
        exit(0);
    }
    

    输出结果如下:
    技术分享图片

    gdb跟踪调试

    重新打包根文件目录
    ?nd . -print0 | cpio --nll -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调试
    ·gdb vmlinux·
    技术分享图片
    ·target remote:1234·
    技术分享图片
    查看得知系统调用入口为__x64_sys_getpriority,设置断点
    b __x64_sys_getpriority
    技术分享图片

    单步调试
    技术分享图片

    显示步骤详情
    技术分享图片

    总结

    getpriority函数内有两个参数,其首先将其压栈值RDI、RSI寄存器,然后发生中断,从__x64_sys_getpriority入口进入,最后获取的优先级返回。该过程与理论分析基本一致。

    深入理解系统调用

    原文:https://www.cnblogs.com/junljw/p/12951862.html

  • (0)
    (0)
       
    举报
    评论 一句话评论(0
    关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
    © 2014 bubuko.com 版权所有
    打开技术之扣,分享程序人生!