epoll处理指向目录的文件描述符时会发生什么?

13

就像标题所说的一样,如果我使用 epoll 注册一个文件描述符,而该文件描述符是一个目录,那么会发生什么?


3
如果您想在Linux上监视文件系统事件,请使用inotify - jxh
1个回答

18

Nothing -- 调用注册fd的函数将会(至少对于常见的Linux文件系统而言)失败并返回EPERM

我使用以下演示程序进行了测试:

#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(void) {
    int ep = epoll_create1(0);
    int fd = open("/tmp", O_RDONLY|O_DIRECTORY);
    struct epoll_event evt = {
        .events = EPOLLIN
    };

    if (ep < 0 || fd < 0) {
        printf("Error opening fds.\n");
        return -1;
    }

    if (epoll_ctl(ep, EPOLL_CTL_ADD, fd, &evt) < 0) {
        perror("epoll_ctl");
        return -1;
    }
    return 0;
}

以下是结果:

[nelhage@hectique:/tmp]$ make epoll
cc     epoll.c   -o epoll
[nelhage@hectique:/tmp]$ ./epoll
epoll_ctl: Operation not permitted

为了弄清楚这里发生了什么,我去查找了资料。我碰巧知道epoll的大部分行为是由与目标文件对应的struct file_operations上的->poll函数决定的,这取决于所涉及的文件系统。我选择了ext4作为典型示例,并查看了fs/ext4/dir.c,其中定义ext4_dir_operations如下:

const struct file_operations ext4_dir_operations = {
    .llseek     = ext4_dir_llseek,
    .read       = generic_read_dir,
    .readdir    = ext4_readdir,
    .unlocked_ioctl = ext4_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = ext4_compat_ioctl,
#endif
    .fsync      = ext4_sync_file,
    .release    = ext4_release_dir,
};

请注意缺少.poll定义,这意味着它将被初始化为NULL。因此,我们转回到epoll,它在fs/eventpoll.c中定义,我们寻找poll是否为NULL的检查,我们在epoll_ctl系统调用定义中的early on中找到了一个。
/* The target file descriptor must support poll */
error = -EPERM;
if (!tfile->f_op || !tfile->f_op->poll)
    goto error_tgt_fput;

根据我们的测试结果,如果目标文件不支持 poll,则插入尝试将以 EPERM 失败。
其他文件系统可能会在其目录文件对象上定义 .poll 方法,但我怀疑很少有这样做。

“dirfd(opendir("/tmp"))”相对于“open(path, O_RDONLY|O_DIRECTORY);”更受欢迎吗?这只是一个风格问题。使用“opendir”并不能让文件系统自动支持poll。 - schmichael
dirfd(opendir("...")) 更具可移植性,因此通常更受欢迎。我是一个 Linux 内核黑客,所以即使不是最合适的情况,我个人倾向于默认使用系统调用接口,因为我更了解它。显然,在这里并不重要,因为 epoll 也是 Linux 特定的。 - nelhage

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接