微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何停止inotify结果的重复?

如何解决如何停止inotify结果的重复?

在这代码中,我试图同时监控两条路径。为此,我使用了 while(1)。但我面临的问题是,每当我运行代码时,它都会给我两次相同的结果。

给出结果

Pathname1 "file" is modified 
Pathname1 "file" is modified

预期结果

Pathname1 "file" is modified 

我调试了代码。打破主函数并跳过它后,下一个命令停在这一行length = read(fd,buffer,EVENT_BUF_LEN )。每当我在这个长度可变命令之后换行时,程序就会启动,修改文件后,程序会停在这一行 struct inotify_event *event = ( struct inotify_event *)&buffer[i]; 虽然程序不应该中断。

我也用 IN_CLOSE_WRITE 代替 IN_MODIFY 但结果没有变化。

typedef struct{
    int length,fd,wd1,wd2;
    char buffer[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;

int getNotified(char *pathname1,char *pathname2){
    inotify.fd = inotify_init();
    inotify.wd1 = inotify_add_watch(inotify.fd,pathname1,IN_MODIFY);
    inotify.wd2 = inotify_add_watch(inotify.fd,pathname2,IN_MODIFY);

    while(1){
        inotify.length = read(inotify.fd,inotify.buffer,EVENT_BUF_LEN); 
        int i = 0;
        while(i < inotify.length){     
            struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
            if(event->len){
                if(event->mask & IN_MODIFY){
                    if(event->wd == inotify.wd1){
                        printf("Pathname1 '%s' is modified\n",event->name);
                        break;
                    }
                    if(event->wd == inotify.wd2){
                        printf("Pathname2 '%s' is modified\n",event->name);
                        break;
                    }
                }
            }
            i += EVENT_SIZE + event->len;
        }
    }
    inotify_rm_watch(inotify.fd,inotify.wd1);
    inotify_rm_watch(inotify.fd,inotify.wd2);

    close(inotify.fd);
    exit(0);
}

解决方法

一些说明:

  • 当你走出内部“while”循环并再次调用read()时,你不应该“中断”
  • IN_MODIFY 事件未填充 event->name 字段(即 event->len = 0)
  • IN_MODIFY 事件的数量取决于您修改文件的方式(“echo xxx >> file” = 只写 = 1 IN_MODIFY;“echo xxx > file” = truncate + write = 2 IN_MODIFY)

这是对您的程序的建议(带有附加打印):

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

#define EVENT_BUF_LEN 4096
#define EVENT_SIZE sizeof(struct inotify_event)

typedef struct{
  int length,fd,wd1,wd2;
  char buffer[EVENT_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;

notification inotify;

int getNotified(char *pathname1,char *pathname2){

  inotify.fd = inotify_init();
  inotify.wd1 = inotify_add_watch(inotify.fd,pathname1,IN_MODIFY);
  printf("wd1 = %d\n",inotify.wd1);
  inotify.wd2 = inotify_add_watch(inotify.fd,pathname2,IN_MODIFY);
  printf("wd2 = %d\n",inotify.wd2);

  while(1){
    inotify.length = read(inotify.fd,inotify.buffer,EVENT_BUF_LEN); 
    int i = 0;
    printf("read() = %d\n",inotify.length);
    while(i < inotify.length){     
      struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
      printf("event->len = %u\n",event->len);
      if(event->mask & IN_MODIFY){
        if(event->wd == inotify.wd1){
          printf("Pathname1 is modified\n");
        } else if (event->wd == inotify.wd2){
          printf("Pathname2 is modified\n");
        }
      }
      i += (EVENT_SIZE + event->len);
      printf("i=%d\n",i);
    }
  }
  inotify_rm_watch(inotify.fd,inotify.wd1);
  inotify_rm_watch(inotify.fd,inotify.wd2);

  close(inotify.fd);
  exit(0);
}



int main(void)
{
  getNotified("/tmp/foo","/tmp/bar");

  return 0;

} // main

这是一个执行示例:

$ gcc notif.c -o notif
$ > /tmp/foo
$ > /tmp/bar
$ ./notif 
wd1 = 1
wd2 = 2

====== Upon "> /tmp/foo": 1 event (truncate operation)
read() = 16
event->len = 0
Pathname1 is modified
i=16

====== Upon "echo qwerty > /tmp/foo": 2 events (write operation,one event for truncate operation and one for the write of "qwerty" at the beginning of the file)
read() = 16
event->len = 0
Pathname1 is modified
i=16
read() = 16
event->len = 0
Pathname1 is modified
i=16

====== Upon "echo qwerty >> /tmp/foo": 1 event (write of "qwerty" at the end of the file)
read() = 16
event->len = 0
Pathname1 is modified
i=16
,

如果两个路径名指向同一个 inode,则 inotify.wd1 == inotify.wd2。在那种情况下,因为你有

    if (event->wd == inotify.wd1) {
        printf("Pathname1 '%s' is modified\n",event->name);
    }
    if (event->wd == inotify.wd2) {
        printf("Pathname2 '%s' is modified\n",event->name);
    }

两个主体都将被执行;但输出会有所不同(Pathname1 ...Pathname2 ...)。

您确实应该监控文件所在的目录,而不是实际的路径名,这样您也可以捕获重命名事件。 (许多编辑器会创建一个临时文件,然后将临时文件重命名或硬链接到旧文件上,以便程序看到旧文件或新文件,而不是两者混合。)

考虑以下程序,它没有表现出任何不适当的重复事件:

// SPDX-License-Identifier: CC0-1.0
#define  _POSIX_C_SOURCE  200809L
#define  _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

static volatile sig_atomic_t  done = 0;

static void handle_done(int signum)
{
    done = signum;
}

static int install_done(int signum)
{
    struct sigaction  act;
    memset(&act,sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = handle_done;
    act.sa_flags = 0;
    return sigaction(signum,&act,NULL);
}

struct monitor {
    /* Supplied by caller */
    const char  *directory;
    const char  *pathname;
    int        (*modified)(struct monitor *);
    int        (*completed)(struct monitor *);
    /* Reserved for internal use */
    int          dirwatch;
};

int monitor_files(struct monitor *const list,const size_t count)
{
    char   *events_ptr = NULL;
    size_t  events_len = 65536;
    size_t  i;
    int     err;

    /* Verify sane parameters */
    if (count < 1) {
        errno = ENOENT;
        return -1;
    } else
    if (!list) {
        errno = EINVAL;
        return -1;
    }
    for (i = 0; i < count; i++) {
        if (!list[i].directory || !list[i].directory[0]) {
            errno = EINVAL;
            return -1;
        }
        if (!list[i].pathname || !list[i].pathname[0]) {
            errno = EINVAL;
            return -1;
        }
        list[i].dirwatch = -1;
    }

    /* Obtain a descriptor for inotify event queue */
    int  queue = inotify_init1(IN_CLOEXEC);
    if (queue == -1) {
        /* errno set by inotify_init1() */
        return -1;
    }

    /* Use a reasonable dynamically allocated buffer for events */
    events_ptr = malloc(events_len);
    if (!events_ptr) {
        close(queue);
        errno = ENOMEM;
        return -1;
    }

    /* Add a watch for each directory to be watched */
    for (i = 0; i < count; i++) {
        list[i].dirwatch = inotify_add_watch(queue,list[i].directory,IN_CLOSE_WRITE | IN_MOVED_TO | IN_MODIFY);
        if (list[i].dirwatch == -1) {
            err = errno;
            close(queue);
            free(events_ptr);
            errno = err;
            return -1;
        }
    }

    /* inotify event loop */
    err = 0;
    while (!done) {
        ssize_t  len = read(queue,events_ptr,events_len);
        if (len == -1) {
            /* Interrupted due to signal delivery? */
            if (errno == EINTR)
                continue;
            /* Error */
            err = errno;
            break;
        } else
        if (len < -1) {
            /* Should never occur */
            err = EIO;
            break;
        } else
        if (len == 0) {
            /* No events watched anymore */
            err = 0;
            break;
        }

        char *const end = events_ptr + len;
        char       *ptr = events_ptr;
        while (ptr < end) {
            struct inotify_event *event = (struct inotify_event *)ptr;

            /* Advance buffer pointer for next event */
            ptr += sizeof (struct inotify_event) + event->len;
            if (ptr > end) {
                close(queue);
                free(events_ptr);
                errno = EIO;
                return -1;
            }

            /* Call all event handlers,even duplicate ones */
            for (i = 0; i < count; i++) {
                if (event->wd == list[i].dirwatch && !strcmp(event->name,list[i].pathname)) {
                    if ((event->mask & (IN_MOVED_TO | IN_CLOSE_WRITE)) && list[i].completed) {
                        err = list[i].completed(list + i);
                        if (err)
                            break;
                    } else
                    if ((event->mask & IN_MODIFY) && list[i].modified) {
                        err = list[i].modified(list + i);
                        if (err)
                            break;
                    }
                }
            }
            if (err)
                break;
        }
        if (err)
            break;
    }

    close(queue);
    free(events_ptr);

    errno = 0;
    return err;
}

static int report_modified(struct monitor *m)
{
    printf("%s/%s: Modified\n",m->directory,m->pathname);
    fflush(stdout);
    return 0;
}

static int report_completed(struct monitor *m)
{
    printf("%s/%s: Completed\n",m->pathname);
    fflush(stdout);
    return 0;
}

int main(void)
{
    struct monitor  watch[2] = {
        { .directory = ".",.pathname  = "file1",.modified  = report_modified,.completed = report_completed },{ .directory = ".",.pathname  = "file2",.completed = report_completed }
    };
    int  err;

    if (install_done(SIGINT) == -1 ||
        install_done(SIGHUP) == -1 ||
        install_done(SIGTERM) == -1) {
        fprintf(stderr,"Cannot set signal handlers: %s.\n",strerror(errno));
        return EXIT_FAILURE;
    }

    fprintf(stderr,"To stop this program,press Ctrl+C,or send\n");
    fprintf(stderr,"INT,HUP,or TERM signal (to process %ld).\n",(long)getpid());
    fflush(stderr);

    err = monitor_files(watch,2);
    if (err == -1) {
        fprintf(stderr,"Error monitoring files: %s.\n",strerror(errno));
        return EXIT_FAILURE;
    } else
    if (err) {
        fprintf(stderr,"Monitoring files failed [%d].\n",err);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

如果您使用例如编译它gcc -Wall -Wextra -O2 example.c -o example 并通过 ./example 运行它,它将报告 file1file2 在相同的目录(当前工作目录)。要退出程序,请按 Ctrl+C

(注意,我们可能应该添加第三个事件类型,“Deleted”,对应于 IN_DELETEIN_MOVED_FROM 事件。)

如果您在文本编辑器中打开 file1file2,保存文件会生成一个或多个修改 (IN_MODIFY) 事件​​,以及一个完成(IN_CLOSE_WRITE 或 IN_MOVED_TO)事件。这是因为每个导致文件被截断的 open()/truncate()/ftruncate() 系统调用,都会为该文件生成一个 IN_MODIFY 事件;与修改文件内容的每个底层 write() 系统调用一样。因此,当某个进程修改文件时,很自然地会收到多个 IN_MODIFY 事件。

如果列表中有多个 struct monitor 条目具有相同的有效目录和相同的路径名,示例程序将为它们提供多个事件。如果您想避免这种情况,只需确保每当 .pathname 匹配时,两者都会得到不同的 .dirwatches。

,

while() 循环重写为 for() 循环,并将 if() 变平”


while(1){
    int i ;
    struct inotify_event *event ;

    inotify.length = read(inotify.fd,EVENT_BUF_LEN);

    for(i=0; i < inotify.length; i += EVENT_SIZE + event->len ) {
        event = (struct inotify_event *)&inotify.buffer[i];

        if (!event->len) continue;
        if (!(event->mask & IN_MODIFY)) continue;

        if (event->wd == inotify.wd1){
            printf("Pathname1 '%s' is modified\n",event->name);
            continue;
        }

        if (event->wd == inotify.wd2){
            printf("Pathname2 '%s' is modified\n",event->name);
            continue;
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?