深入理解系统调用

一、实验要求:

  

  • 本人学号后两位为17,则寻找系统调用号为17的系统调用
  • 通过汇编指令触发该系统调用

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

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

二、环境搭建:

  2.1按照实验一中的步骤来进行初始环境搭建

   

复制代码
sudo apt install build-essential
sudo apt 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
复制代码

 

 

 

   2.2配置内核选项

 make defconfig 
 make menuconfig

  进入 Kernel hacking的Compile-time checks and compiler options:

 

  进入Processor type and features:

 

  2.3编译内核:

make -j$(nproc)
qemu-system-x86_64 -kernel arch/x86/boot/bzImage

  由于没有⽂件系统最终会kernel panic,这属于正常现象。

  2.4利用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
make menuconfig
make -j$(nproc) && make install

  在配置选项时进入Settings,选择Build static binary (no shared libs)

  制作内存根文件系统镜像:

 

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脚本,并修改权限:

复制代码
vi init

#!/bin/sh
mount -t proc none /proc mount -t sysfs none /sys
echo "Wellcome WangbaOS!"
cd home
/bin/sh

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

 

三、汇编指令触发该系统调用

因为学号后两位是82,在/home/zd/linux-5.4.34/arch/x86/entry/syscalls目录下查阅syscall_64.tbl文件,82号系统调用为__x64_sys_gettimeofday,对应的函数是gettimeofday。

在网上查询关于gettimeofday函数的使用方法,编写测试函数,使用汇编指令触发该系统调用。

(1)在C语言中可以使用函数gettimeofday()函数来得到时间。它的精度可以达到微秒

(2)函数原型

  #include<sys/time.h>

  int gettimeofday(struct  timeval*tv,struct  timezone *tz )

(3)返回值:gettimeofday()会把目前的时间用tv 结构体返回,当地时区的信息则放到tz所指的结构中。

C代码调用

复制代码
#include<stdio.h>
#include<sys/time.h>
int main()
{
    struct timeval tv;
    struct timezone tz;
    gettimeofday(&tv,&tz);
    printf("tv_sec:%ld\n",tv.tv_sec);
    printf("tv_usec:%ld\n",tv.tv_usec);
    printf("tz_minuteswest:%d\n",tz.tz_minuteswest);
    printf("tz_dsttime:%d\n",tz.tz_dsttime);
    return 0;
}
复制代码

内嵌汇编调用

复制代码
#include<stdio.h>
#include<sys/time.h>
int main()
{
    struct timeval tv;
    struct timezone tz;
    int flag;
    asm volatile(
        "movl %1, %%edi\n\t"
        "movl %2, %%esi\n\t"
        "movl $0x4E, %%eax\n\t"
        "syscall\n\t"
        "movl %%eax, %0\n\t"
        :"=m"(flag)
        :"b"(&tv),"c"(&tz)
    );
    printf("tv_sec:%ld\n",tv.tv_sec);
    printf("tv_usec:%ld\n",tv.tv_usec);
    printf("tz_minuteswest:%d\n",tz.tz_minuteswest);
    printf("tz_dsttime:%d\n",tz.tz_dsttime);
    return 0;
}
复制代码

运行结果如下

 

 

四、使用gdb跟踪该系统调用的内核处理过程

1、启动qemu:

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

此时终端界面会停止

 

 2开启一个新的终端,设置断点

1 cd lab2-linux-5.4.34
2 gdb vmlinux
3 target remote:1234
4 b __x64_sys_socketpair
5 c            

 

 

 在之前停止的终端下执行socketpair

在设置的断点处停下

接下来进行单步调试:可以看到有系统调用entry_SYSCALL_64()

 

 

五、总结

从这次实验中,学习了通过汇编指令来触发系统调用,并用gdb调试跟踪了整个系统调用的过程,让我对linux内核的系统调用过程有了深刻而且系统的理解。下面是对整个过程的总结。

从系统调用的整个过程来看,主要有以下几个阶段:

(1)用户态程序,发生syscall,触发系统调用;

(2)进入内核态,完成内核初始化后,调用entry_SYSCALL_64 ()

(3)完成现场的保存,将关键寄存器压栈,并从CPU内部的MSR寄存器来查找系统调⽤处理⼊⼝,更改CPU的指令指针(eip/rip)到系统调⽤处理⼊⼝ ,调用do_syscall_64()

(4)do_syscall_64()函数中得到系统调用号,调用相关的函数gettimeofday()

(5)调用结束后,保存现场和恢复现场时的CPU存器也通过CPU内部的存储器快速保存和恢复 

(6)系统调用返回,回到用户态程序

 

posted @ 2020-05-27 21:16  公元1864  阅读(322)  评论(0)    收藏  举报