深入理解系统调用

1.配置内核debug环境

1.1 通过两个命令行窗口进行内核debug

已经有很多其它同学的博客详细的描述了这个过程,另外自己在网络程序设计课程中已经配置过类似的环境,很多操作不想再重复一遍,这里就不在赘述。下面直接给出系统启动时暂停在start_kernel处的截图。

深入理解系统调用_第1张图片

  

 1.2 在vscode中配置debug环境(确实挺好用,vscode真香,可以加分吗)

通过搜索引擎找到了一个可以配置的方案,使用 VSCode + qemu 搭建 Linux 内核调试环境。具体过程可以看博文,我就不当搬运工了。

需要注意的是,博文中给出的配置文件中的路径需要改成自己本机下对应的路径。

按照博文经过一步步配置,成功的实现了在vscode中debug内核,截图如下。

深入理解系统调用_第2张图片

 

 

 2. 编写代码触发自己学号对应的系统调用,跟踪相关关键代码

2.1 找到学号对应的系统并简单了解

自己学号最后两位是75。查看syscall_64.tbl文件如下,对应的ABI和系统调用入口是fdatasync和__x64_sys_fdatasync

稍微看一下这个系统调用是干啥的。 在ubuntu terminal中输入man fdatasync可以看到如下内容

深入理解系统调用_第3张图片

 fsync和fdatasync看来是一对孪生兄弟,都存在于头文件下。

深入理解系统调用_第4张图片 

fsync会将文件描述符对应的文件修改强制写回到磁盘,防止系统崩溃和重启。又linux下文件的数据和metadata是分开存储的,fsync会将二者的改变同时写回到持久存储设备。

fdatasync与fsync相似,只是metadata会有条件的写回磁盘。比如在文件大小改变时会写回metadata,而文件的最后获取时间和修改时间等信息改变时则不会触发metadata的写回。

介绍到这,下面来我们写两个c函数来触发fdatasync

2.2 用系统调用c接口和汇编分别触发fdatasync

// fdatasync.c
#include 
#include <string.h>
#include 
#include 
int main(void)
{
    int fd,len;
    char *buf = "Hello World!\n", Out[30];
    fd = open("a.txt", O_CREAT | O_TRUNC | O_RDWR, 0600);
    printf("open file:a.txt fd = %d\n", fd);
    len = strlen(buf);
    int size  = write(fd, buf, len);
    fdatasync(fd); // 通过c接口触发相应系统调用
    close(fd);
    fd = open("a.txt", O_RDWR, 0600);
    lseek(fd, 0, SEEK_SET);
    size = read(fd, Out, 12);
    printf("size = %d\nread from file:\n %s\n",size,Out);
    close(fd);
    return 0;
}
// fdatasync-asm.c
int main() {
    asm volatile(
    "movl $0x4B,%eax\n\t" //使⽤EAX传递系统调⽤号75
    "syscall\n\t" //通过内联汇编触发系统调⽤ 
    );
    return 0;
}

通过以下命令编译上述两个c文件

# 注意要加-static参数
gcc
fdatasync.c -o fdatasync -static gcc fdatasync-asm.c -o fdatasync-asm -static

通过下面的命令将生成的两个可执行文件放到rootfs文件夹下

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz

2.3 在vocode进行跟踪调试

在linux-5.4.34/fs/sync.c相应函数打上断点

 深入理解系统调用_第5张图片

在qemu中运行fdatasync-asm,如下面截图所示,内核在sync.c的第232行停了下来,左边的call_stack小窗口也显示了整个调用栈。

深入理解系统调用_第6张图片

汇编指令触发系统调用,找到函数中断入口,执行 entry_SYSCALL_64() ,通过 swapgs 指令保存现场,触发 do_syscall_64 。通过 do_syscall_64 函数得到了 系统调用号 ,执行系统调用内容.执行完毕后,又跳回了 do_syscall_64 中的 syscall_return_slowpath() ,准备进行现场恢复操作,并准备跳回用户态。最后回到 entry_SYSCALL_64() 执行 popq ,完成堆栈切换。

你可能感兴趣的:(深入理解系统调用)