【问题标题】:Using read with inotify将读取与 inotify 一起使用
【发布时间】:2011-03-06 17:09:54
【问题描述】:

我一直在研究inotify call,但是在读接口方面我还是有点不爽。这些是我能找到的关于如何使用 read(2) 与 inotify 正确交互的最相关资源:

他们都以相同的方式实现它,他们首先定义了以下大小:

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 )

然后他们以这种方式使用它们:

length = read( fd, buffer, BUF_LEN );  

if ( length < 0 ) {
    perror( "read" );
}  

while ( i < length ) {
    struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
    /* some processing */
    i += EVENT_SIZE + event->len;
}

现在,我们知道 name 是 struct inotify_event 的一部分,并且它具有可变长度。那么,buffer中最后一个inotify_event不能被截断吗?

假设有 1023 个 inotify_events,路径为 16 字节,一个为 32 字节。那时会发生什么?后期会被截断吗?或者内核会发现它不适合缓冲区并完全保留它吗?

【问题讨论】:

    标签: c linux-kernel inotify


    【解决方案1】:

    基本用法

    根据inotify(7),您可以使用 FIONREAD ioctl 找出有多少数据可供读取并相应地调整缓冲区大小。这里有一些(非常粗略的)代码可以做到这一点:

    unsigned int avail;
    ioctl(inotify_fd, FIONREAD, &avail);
    
    char buffer[avail];
    read(fd, buffer, avail);
    
    int offset = 0;
    while (offset < avail) {
        struct inotify_event *event = (inotify_event*)(buffer + offset);
    
        // Insert logic here
        my_process_inotify_event(event);
    
        offset = offset + sizeof(inotify_event) + event->len;
    }
    

    更强大的用法

    inotify-tools 提供更高级别的接口来进行 inotify。您可以使用它而不是访问 inotify,或者您可以查看它如何实现 inotifytools_next_events 以安全可靠地读取所有可用事件。

    部分事件和截断

    在回答您关于截断的问题时,如果给定的缓冲区对于所有事件来说都太小,我认为内核不会返回部分 inotify_event 或截断 inotify_event。 inotify(7) 联机帮助页中的以下段落表明了这一点:

    当给 read(2) 的缓冲区太小而无法返回有关下一个事件的信息时的行为取决于内核版本:在 2.6.21 之前的内核中,read(2) 返回 0;从内核 2.6.21 开始,read(2) 失败并出现错误 EINVAL。

    inotifytools.c 的以下 cmets 也是如此:

    // oh... no.  this can't be happening.  An incomplete event.
    // Copy what we currently have into first element, call self to
    // read remainder.
    // oh, and they BETTER NOT overlap.
    // Boy I hope this code works.
    // But I think this can never happen due to how inotify is written.
    

    【讨论】:

    • 我已经咨询过#kernelnewbies 关于短读/截断的问题。手册页states 指出“每次成功的 read(2) 都会返回一个包含以下一个或多个结构的缓冲区 [...]”。文件名是这些结构的一部分。中断在这里无关紧要,信号不能只发生在内核上下文中。输出缓冲区要么包含完整的结构,要么什么都不包含。在内核code 中,read() 的返回值是 sizeof(struct inotify_event) + round(name)。
    • “哦...不。这不可能发生。...男孩,我希望这段代码有效。” - 健壮,嗯?
    • 不要使用avail 来调整缓冲区大小。 avail当前可用的数据量,当你第一次开始时它会为零。并且每次通过循环设置缓冲区大小不是一个好主意。
    • @Ariel - 我不明白你的担忧。只有当select 或类似报告有数据可供读取时才会调用此代码,因此我们知道avail 不为零。而且,由于buffer[avail] 是一个在堆栈上分配的 C99 可变长度数组,因此每次通过循环设置缓冲区大小的成本是微不足道的。
    • 是的,你可以这样做,但将read() 放在与poll() 不同的函数中对我来说没有意义。实际的上限最大值为 4MB,实际的缓冲区大小约为 1K 到 64K。由于堆栈上的 4MB 有点不确定,因此将它放在堆栈上真的不值得 - 你什么也得不到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    • 2016-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多