深入理解系统调用

一、实验环境准备

按照课程PPT的步骤进行:

1. 编译linux内核

2. 安装开发工具qemu

3. 制作根文件系统

4. 准备gdb调试工具

由于本人环境为ubuntu16.04,在使用gdb调试的过程中出现了下面的错误:

 经过查阅资料得知这是由于ubuntu16.04的gdb版本问题,且不能通过apt升级,故下载gdb7.5版本源码进行编译安装。

在编译之前需修改remote.c文件中的部分代码,将下面代码:

if (buf_len > 2 * rsa->sizeof_g_packet)
    error (_(“Remote ‘g’ packet reply is too long: %s”), rs->buf);

修改为:

if (buf_len > 2 * rsa->sizeof_g_packet) {
   rsa->sizeof_g_packet = buf_len ;
   for (i = 0; i < gdbarch_num_regs (gdbarch); i++) {
       if (rsa->regs->pnum == -1)
          continue;
       if (rsa->regs->offset >= rsa->sizeof_g_packet)
          rsa->regs->in_g_packet = 0;
       else  
          rsa->regs->in_g_packet = 1;
   }    
}

二、系统调用实现

本人学号尾号为49,通过vscode查阅arch/x86/ entry/syscalls/syscall_64.tbl文件,可以得到系统调用号为49的系统调用为bind,通过查阅资料,Bind一般是在server端调用,通过bind,会把本端的地址和端口号与socket描述符进行绑定,而目的地址和端口在connect的时候才能确定。

 

由于本次实验旨在理解系统调用如何执行,故没有深入研究如何在程序中使用bind系统调用,而使用嵌入的汇编代码进行了bind系统调用。

 新建/rootfs/home/systemcall文件夹,创建bind.c文件。

#include <stdio.h>
int main(){
    asm volatile(
    "movl $0x31,%eax
	"
    "syscall
	");
    printf("end!
");
    return 0;
}

然后编译:gcc -o bind bind.c -static

返回roofs目录,打包系统镜像:find . -print0 | cpio --null -ov --format=newc | gzip -9 > ~/linux_code/linux-5.4.34/rootfs.cpio.gz

执行  qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"  启动内核

打开新终端源代码目录下执行  gdb vmlinux  打开gdb调试工具,运行bind文件,进行如下调试过程

 分析:

1. linux内核定义了大量的系统调用,内核通过EAX寄存器传递的系统调用号即可得知用户态程序希望哪个系统调用。

2. 除了系统调用号以外,系统调用还可能要传递参数,由于32为的x86体系结构下是将参数压栈的方式传递,这样效率较低,而64位的x86体系结构使用寄存器来传递参数,效率较高。

3. 使用syscall汇编指令来触发系统调用。

4. 保存现场:如调试过程所示,系统自动将rip保存到rcx,然后将系统调用入口加载到rip,syscall借助CPU内部的MSR寄存器来存放,查找系统调用入口地址会更快。

4.恢复现场:使用swapgs指令,将保存现场和恢复现场时的CPU寄存器也通过CPU内部的存储器快速保存和恢复,更加加快了系统调用。

原文地址:https://www.cnblogs.com/happyyouli/p/12971721.html