接上一篇:https://my.oschina.net/fileoptions/blog/3061997 ,linux的io路径比较复杂,通常我们在阅读内核源码的时候,有时候也很难完整的跟踪整个路径。其实,我们可以使用工具跟踪代码的io路径,看一下一次完成的open、write、read都调用了哪些内核函数,这里我使用ftrace工具。假设要跟踪的代码编译成iotrace ,则可以使用trace-cmd 对iotrace的io路径进行跟踪(命令如下)。
sudo trace-cmd record -p function -F ./iotrace
trace-cmd 会启动iotrace程序,然后开始跟踪它的内核函数调用(并不是所有内核函数都会被跟踪到,只有ftrace支持的才可以), 由于代码很少,因此这里trace会很快结束,然后使用trace-cmd report命令将产生的trace文件格式化为人类可读的文本,就可以清晰的看到io的调用路径和层次关系。
由于我不想在虚拟机Linux上做这些测试(总感觉虚拟机会带来很多没必要的虚拟机相关的代码调用,增加复杂性),而我又没有运行Linux的物理机,怎么办呢?幸好在我玩硬件的时候入手了一块树莓派3B+,上面运行着完整的linux系统,而且我还把内核升级到5.x版本,更重要的是,在树莓派上可以尽情的编写、加载内核模块和修改内核,而不用担心把内核搞core dump,大不了快速重启或者重新烧写内核。
iotrace的源码如下,测试了7中io场景,主要为了测试普通模式、带O_DIRECT、O_SYNC标志、mmap模式、fsync和msync的调用路径等。
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#define BUF_SIZE 1024
int main(void)
{
int fd,size,len;
int ret = 0;
/*1. 普通io模式 */
char * normal_write_buf = "normal io";
len = strlen(normal_write_buf);
char normal_read_buf[100] = {0};
/* mark start */
pid_t pid = getpid();
if ((fd = open("hello.c", O_CREAT | O_TRUNC | O_RDWR,0666 ))<0) {
perror("open:");
exit(1);
}
/* 第一次wrirte,此时文件应该还不在page cache */
if ((size = write( fd, normal_write_buf, len)) < 0){
perror("write:");
exit(1);
}
/* 第二次wrirte,此时文件应该在page cache,因此路径会比上一次端 */
if ((size = write( fd, normal_write_buf, len)) < 0){
perror("write:");
exit(1);
}
lseek(fd, 0, SEEK_SET );
if ((size = read( fd, normal_read_buf, len))<0) {
perror("read:");
exit(1);
}
if(strncmp(normal_read_buf,normal_write_buf,len) != 0){
perror("strncmp:");
exit(1);
}
/* 强制刷盘 */
if(fsync(fd) < 0){
perror("fysnc:");
exit(1);
}
if ( close(fd) < 0 ) {
perror("close:");
exit(1);
}
/*2. direct io模式*/
char *direct_write_buf,*direct_read_buf;
ret = posix_memalign((void **)&direct_write_buf, 512, BUF_SIZE);
if (ret) {
perror("posix_memalign:");
exit(1);
}
ret = posix_memalign((void **)&direct_read_buf, 512, BUF_SIZE);
if (ret) {
perror("posix_memalign:");
exit(1);
}
strcpy(direct_write_buf,"direct mode");
len = strlen("direct mode");
/* mark start */
pid = getpid();
if ((fd = open("hello.c", O_CREAT | O_TRUNC | O_RDWR | O_DIRECT,0666 ))<0) {
perror("open:");
exit(1);
}
if ((size = write(fd, direct_wri