Linux系统调用分析

学号372,原创作品转载请注明出处

参考:Linux Kernel

实验环境

  • 内核代码:Linux Kernel
  • Ubuntu虚拟机:运行环境,本次使用VM Ware安装的Ubuntu18
  • QEMU:本次使用的模拟器,运行内核代码
  • gdb断点调试工具

实验过程

1.编译内核

  • https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.1.tar.xz
  • xz -d linux-5.0.1.tar.xz
  • tar -xvf linux-5.0.1.tar //得到内核文件
  • cd linux-5.0.1
  • sudo apt install build-essential flex bison libssl-dev libelf-dev libncurses-dev //安装依赖工具,按需安装,否则会报错,贴出所需供参考,有些工具我装过所以我并未全部安装。遇到的问题之一如下图所示,反正缺什么工具装什么

    sudo apt install libssl-dev 即可解决
  • make i386_defconfig //这部很关键,我使用的是32位的qemu,因此kernel需要同为32位的;
  • make menuconfig //然后kernel hacking,->Compile-time checks and compiler options,选择 [*]compile the kernel with debug info。效果如下图:

    //我在此处并未make,在第3步检查完qemu后才make

2.制作img文件

在linux-5.0.1目录下:

  • mkdir rootfs
  • git clone https://github.com/mengning/menu.git
  • cd menu
  • gcc -o init linktable.c menu.c test.c -m32 -static
  • cd ../rootfs
  • cp ../menu/init ./
  • find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img

3.虚拟机加载内核

在linux-5.0.1目录下:

  • sudo apt install qemu //没装过的话装一下,我安装过
  • sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu //连接一下,以后简单的qemu即可,不需要打长长的qemu-system-i386
  • make //开始编译,过程可能比较久,等待即可。若出现卡死,然后过一段时间后虚拟机重启这种问题,需检查依赖工具是否齐全,我出现过类似问题,补齐需要的工具后正常编译完

  • qemu -kernel linux-5.0.1/arch/x86/boot/bzImage //和3行二选一,32位就本条命令;若64位则 qemu-system-x86_64 -kernel linux-5.0.1/arch/x86/boot/bzImage

4.构造MenuOS

在linux-5.0.1目录下:

  • mkdir rootfs
  • git clone https://github.com/mengning/menu.git
  • cd menu
  • gcc -pthread -o init linktable.c menu.c test.c -m32 -static
  • cd ../rootfs
  • cp ../menu/init ./
  • find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
  • qemu arch/x86/boot/bzImage -initrd rootfs.img //此处可打开qemu窗口,确保是在linux-5.0.1目录下执行的本命令,正常情况下是可以打开下图界面的;若报错“文件无法找到”,请修改指令内使用到的文件为其对应目录

    若可正常运行,则关闭页面,开始下一步。

5.测试用例

我的学号是372,取最后2位相同的为sigsuspend()函数,如下图所示:

72号进程为中断,正常demo中尚可通过强制中断来发送一个中断信号使该函数触发,但在gdb调试中无法触发中断信号因此无法触发该函数,所以无法测试。因此改为172号:prctl().
使用172号进程

写一个.c文件调用该函数(即prctl()

#include<stdio.h>
#include<pthread.h>
#include<sys/prctl.h>

void* tmain(void*arg)
{
    char name[32];
    prctl(PR_SET_NAME,(unsigned long)"xx");
    prctl(PR_GET_NAME,(unsigned long)name);
    printf("%s\n", name);

}

int main(void)
{
    pthread_t tid;
    pthread_create(&tid,NULL, tmain,NULL);
    pthread_join(tid,NULL);

    return 0;
}

编译并运行之,可运行出期望的结果

使用gdb断点调试,分析系统调用过程:


设置断点至函数调用处

在函数调用前的断点前,指令在call上一条,eax值如下:

发生系统调用时,指令执行至call时,eax中的值为调用编号,系统通过存储在eax中的系统调用编号实现定向的系统调用。

通过eax寄存器中的值的变化可以发现,用户程序使用的prctl()函数会请求一个系统调用,系统调用会触发中断来执行该函数;该函数对应的内核的函数编号为172号,因此系统通过eax寄存器传递172这个值,在中断处理函数中找到对应的函数并执行。

实验总结

发生系统调用时,int 0x80触发中断,系统保存现场后进入内核态,根据调用号寻找到相应的处理程序完成中断处理,结束后再返回。
系统调用的工作机制是:当用户态进程调用一个系统调用时,CPU切换到内核态并执行对应的内核函数,内核函数由操作系统预先定义并分配了编号,调用过程中系统通过eax寄存器传递调用函数的编号,通过编号寻找相应的中断处理程序完成所需的功能,完成后通过已保存的现场返回并继续往下执行。

猜你喜欢

转载自www.cnblogs.com/wangxianfei/p/10553871.html