简体   繁体   English

inotify 是一次性解决方案吗

[英]Is inotify a one shot only solution

I have a program whiuch utilizes inotify.我有一个使用 inotify 的程序。

What it does is it start watching the directory for the file being created.它的作用是开始监视正在创建的文件的目录。 When it happens the program reads the content then deletes the file.当它发生时,程序读取内容然后删除文件。

Then the user initiates an action which will create the same file again.然后用户启动将再次创建相同文件的操作。 However, inotify does not see that the file has been created a second time and the file is not processed.但是,inotify 没有看到该文件已被第二次创建并且该文件未被处理。

Code is as follows:代码如下:

fileCreated = false;
m_wd1 = inotify_add_watch( m_fd, "/tmp", IN_CREATE );
if( m_wd1 == -1 )
{
}
else
{
    while( true )
    {
        poll_num = poll( &fds, nfds, -1 );
        if( poll_num == -1 )
        {
            if( errno == EINTR )
                continue;
            syslog( LOG_ERR, "Fail to run poll" );
            result = 1;
        }
        else if( poll_num > 0 && ( fds.revents & POLLIN ) )
        {
            syslog( LOG_DEBUG, "Polling is successful" );
            for( ;; )
            {
                len = read( m_fd, buf, sizeof( buf ) );
                if( len == -1 && errno != EAGAIN )
                {
                    syslog( LOG_ERR, "Failure to read the inotify event" );
                    result = 1;
                    break;
                }
                for( ptr = buf; ptr < buf + len; ptr += sizeof( struct inotify_event ) + event->len )
                {
                    event = (const struct inotify_event *) ptr;
                    if( event->mask & IN_CREATE )
                    {
                        std::string name( event->name );
                        if( name == "scan_results" )
                        {
                            fileCreated = true;
                            break;
                        }
                    }
                }
                if( fileCreated || result )
                    break;
            }
        }
        if( fileCreated )
        {
            std::ifstream log( "scan_results" );
            if( log.rdstate()  & std::ifstream::failbit ) != 0 )
            {
            }
            else
            {
            }
            log.close();
            if( remove( "scan_results" ) != 0 )
            {
                syslog( LOG_ERR, "Failed to remove the file" );
            }
            else
            {
                syslog( LOG_DEBUG, "File deleted successfully" );
            }
        }
        fileCreated = false;

The while() loop runs only once. while() 循环只运行一次。 When the action happens second time I see a message "Polling is successful".当该操作第二次发生时,我看到一条消息“轮询成功”。

Should I add IN_MODIFY as a mask for inotify?我应该添加 IN_MODIFY 作为 inotify 的掩码吗?

If it matters - this code is running inside std::thread.如果重要的话 - 此代码在 std::thread 中运行。

Turns out that polling plus reading inotify is not thread-safe.事实证明,轮询加读取 inotify 不是线程安全的。

So to overcome this I had to add inotify_init() to every thread.所以为了克服这个问题,我必须在每个线程中添加inotify_init() So now every thread have its own inotify file descriptor.所以现在每个线程都有自己的 inotify 文件描述符。 And now it looks like program works.现在看起来程序可以工作了。

Thank you everybody for reading and trying to help.感谢大家阅读并提供帮助。

Well, I have an answer, it's not a short one and it's directly from the man 7 inotify page in linked above in the comments.好吧,我有一个答案,它不是一个简短的答案,它直接来自上面评论中链接的man 7 inotify页面。 Your two pending questions from the narrowing of your questions in the comments are:您在评论中缩小问题的两个未决问题是:

  1. if I call remove() on the file - does this mean that the watch in the directory for file creation will be removed?;如果我在文件上调用remove() - 这是否意味着目录中用于创建文件的监视将被删除? and
  2. if it is true can you give me some pseudocode for the workaround?如果是真的,你能给我一些解决方法的伪代码吗?

The answer to (1) is Yes , but the watch may be recycled causing the next file/dir to which it is assigned to potentially read the pending requests for the file/dir that was closed/deleted: (1)的答案是Yes ,但是手表可能会被回收,从而导致分配给它的下一个文件/目录可能读取已关闭/删除的文件/目录的挂起请求:

 When a watch descriptor is removed by calling inotify_rm_watch(2) (or because a watch file is deleted or the filesystem that contains it is unmounted), ...

(caveat to be aware of) (需要注意的警告)

 ... any pending unread events for that watch descriptor remain available to read. As watch descriptors are subsequently allocated with inotify_add_watch(2), the kernel cycles through the range of possible watch descriptors (0 to INT_MAX) incrementally. When allocating a free watch descriptor, no check is made to see whether that watch descriptor number has any pending unread events in the inotify queue. Thus, it can happen that a watch descriptor is reallocated even when pending unread events exist for a previous incarnation of that watch descriptor number, with the result that the application might then read those events and interpret them as belonging to the file associated with the newly recycled watch descriptor. In practice, the likelihood of hitting this bug may be extremely low, since it requires that an application cycle through INT_MAX watch descriptors, release a watch descriptor while leaving unread events for that watch descriptor in the queue, and then recycle that watch descriptor. For this reason, and because there have been no reports of the bug occurring in real-world applications, as of Linux 3.15, no kernel changes have yet been made to eliminate this possible bug.

(I doubt you will ever have that many watches open) (我怀疑你会打开那么多手表)

In answer to the second question, the pseudo code would simply be to check the mask for:在回答第二个问题时,伪代码只是检查掩码

 IN_DELETE_SELF Watched file/directory was itself deleted. (This event also occurs if an object is moved to another filesystem, since mv(1) in effect copies the file to the other filesystem and then deletes it from the original filesys‐ tem.) In addition, an IN_IGNORED event will subsequently be generated for the watch descriptor.

So you can check with either the mask IN_DELETE_SELF or IN_IGNORED if the file/directory being watched is deleted.因此,如果正在监视的文件/目录被删除,您可以使用掩码IN_DELETE_SELFIN_IGNORED检查。

I can't see exactly what is wrong with your code since you haven't provided an mcve , but watching a directory should give you all of the subscribed events for the directory until you stop reading them.由于您没有提供mcve ,因此我无法确切地看到您的代码有什么问题,但是观看目录应该会为您提供该目录的所有订阅事件,直到您停止阅读它们。

Here's an example of using inotify to watch a directory...这是使用inotify监视目录的示例...

#include <iostream>

#include <unistd.h>
#include <sys/inotify.h>

int main(int argc, char *argv[]) {

  const int ifd = inotify_init();
  const int iwd = inotify_add_watch(ifd, "/tmp/inotify", IN_CREATE);

  char buf[4096];

  while (read(ifd, buf, sizeof(buf)) > 0) {

    const struct inotify_event* ie = (const struct inotify_event*) buf;

    std::cout << ie->name << std::endl;
  }

  return 0;
}

(jason@pi) [4868] ~/tmp touch /tmp/inotify/foo
(jason@pi) [4880] ~/tmp touch /tmp/inotify/bar
(jason@pi) [4881] ~/tmp rm /tmp/inotify/foo
(jason@pi) [4882] ~/tmp touch /tmp/inotify/foo

(jason@pi) [4879] ~/tmp ./so
foo
bar
foo

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM