我正在创建一个具有以下特征的 ebpf 程序系统调用上的跟踪点读()。
该程序的目的是计算文件被读取的次数。
我目前追踪此问题的方法如下:
- 当 read() 的跟踪点命中时,它可以访问文件描述符 (fd) 和进程 ID (pid)。我的 c 回调代码会使用此信息执行。
- 代码从 /proc/{pid}/fd 中的每个文件获取 inode。如果 inode 与要监视的特定文件匹配,则我增加计数。这一切都发生在 read() 系统调用完成之前。
有时这很有效...但是我注意到一些奇怪的事情......
- 较小的文件(例如总共 2 行)通常不会以这种方式被拾取,但较大的文件则会。
例如:
猫小.txt=错过
猫大.txt=已看过
- 如果我在 cat 调用之前添加 strace 命令,它就会起作用
例如:
strace -q 猫小.txt=已看过
处理此问题的 ebpf 代码可以在这里看到:
void handle_event(void *ctx, void *data, unsigned int data_sz)
{
//struct that holds data like pid, and fd
struct data_t *m = data;
//get /proc/pid/fd as string
char path_str[100];
sprintf(path_str, "/proc/%d/fd", m->pid);
//for traversing the proc dir
DIR *mydir;
struct dirent *myfile;
struct stat mystat;
//traverse /proc/pid/fd
char buf[512];
mydir = opendir(path_str);
if(mydir != NULL){
while((myfile = readdir(mydir)) != NULL)
{
//stat each file
sprintf(buf, "%s/%s", path_str, myfile->d_name);
stat(buf, &mystat);
//check if inode matches from list
if(mystat.st_ino == 1396301 || mystat.st_ino == 1319264 || mystat.st_ino == 5768513 || mystat.st_ino == 1318781){
//alert message to signify file was read
printf("Read file!\n");
printf("inode = %d \n", mystat.st_ino);
}
}
}
//close the dir
if(mydir != NULL){
closedir(mydir);
}
}
我注意到的是猫大.txt/proc/pid/fd 中始终有一个 big.txt 的 fd,strace -q 小.txt对于 small.txt。
然而对于猫小.txt它似乎从来没有 fd。
我假设这与缓存有关,但我似乎无法弄清楚在以下情况下文件是如何被访问的猫小.txt,因为即使它通过缓存访问文件,它也不会产生 fd,并更新 /proc/pid/fd?
如果不是,为什么?应该使用什么机制来访问文件内容?
任何帮助将非常感激。
答案1
感谢 @user1686 提出您的问题。这让我明白,本质上没有什么可以确保我的跟踪点回调(在用户空间中执行)在内核空间中执行的 read() 系统调用之前完成。
因此,可能发生的情况是,在我的回调可以访问 /proc/pid/fd 之前,进程关闭了文件,因此将 fd 添加到 /proc/pid/fd 的正常行为如预期发生。