1.配置内核debug环境
1.1 通过两个命令行窗口进行内核debug
已经有很多其它同学的博客详细的描述了这个过程,另外自己在网络程序设计课程中已经配置过类似的环境,很多操作不想再重复一遍,这里就不在赘述。下面直接给出系统启动时暂停在start_kernel处的截图。
1.2 在vscode中配置debug环境(确实挺好用,vscode真香,可以加分吗)
通过搜索引擎找到了一个可以配置的方案,使用 VSCode + qemu 搭建 Linux 内核调试环境。具体过程可以看博文,我就不当搬运工了。
需要注意的是,博文中给出的配置文件中的路径需要改成自己本机下对应的路径。
按照博文经过一步步配置,成功的实现了在vscode中debug内核,截图如下。
2. 编写代码触发自己学号对应的系统调用,跟踪相关关键代码
2.1 找到学号对应的系统并简单了解
自己学号最后两位是75。查看syscall_64.tbl文件如下,对应的ABI和系统调用入口是fdatasync和__x64_sys_fdatasync
稍微看一下这个系统调用是干啥的。 在ubuntu terminal中输入man fdatasync可以看到如下内容
fsync和fdatasync看来是一对孪生兄弟,都存在于
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相应函数打上断点
在qemu中运行fdatasync-asm,如下面截图所示,内核在sync.c的第232行停了下来,左边的call_stack小窗口也显示了整个调用栈。