如何阻止inotify结果的重复出现?

1 人关注

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

Giving result

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

Expected result

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);
    
c
linux
inotify
Sky wifibrand
Sky wifibrand
发布于 2021-02-06
3 个回答
Rachid K.
Rachid K.
发布于 2021-02-06
已采纳
0 人赞同

一些评论。

  • You should not "break" as you go out of the inside "while" loop and call read() again
  • The IN_MODIFY event does not fill the event->name field (i.e. event->len = 0)
  • The number of IN_MODIFY events depends on how you modify the files ("echo xxx >> file" = write only = 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
    ====== 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
    read() = 16
    event->len = 0
    Pathname1 is modified
    ====== Upon "echo qwerty >> /tmp/foo": 1 event (write of "qwerty" at the end of the file)
    read() = 16
    event->len = 0
    Pathname1 is modified
        
    @richardk.对不起,但它仍然给我同样的结果。
    我更新了我的答案,因为IN_MODIFY在写操作时产生了2个事件
    @richardk.我不希望收到两个事件通知。 我只想一次收到一个。但它显示的是 Pathname1 is modified 和再次 Pathname1 is modified 。我只希望Pathnam1只被修改一次。
    我更新了代码,有了预期的结果。请检查它。
    我只想让写操作出现。
    Glärbo
    Glärbo
    发布于 2021-02-06
    0 人赞同

    如果这两个路径名指的是同一个节点,那么 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);
    

    both机构会被执行;但输出会不同(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, 0, 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->directory, 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",
              .modified  = report_modified,
              .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的IN_MODIFY事件(作为 "已修改")和IN_CLOSE_WRITE及IN_MOVED_TO事件(作为 "已完成")。 要退出程序,请按Ctrl+C.

    (注意,我们也许应该增加第三个事件类型,"删除",对应于IN_DELETEIN_MOVED_FROM事件)。

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

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

    运行后,只显示 fprintf ,之后就不显示监控了。
    @Skywifibrand:你需要在一个终端窗口中运行它。 打开另一个终端窗口,改变( cd )到同一工作目录,然后运行 touch file1 date > file2 mv file2 file1 cp file1 file2 。 每一个都会被报告。 (我已经验证了代码的正确性。)
    wildplasser
    wildplasser
    发布于 2021-02-06
    0 人赞同

    while() 的循环改写为 for() 的循环,并将 if() 平移出来"

    while(1){
        int i ;
        struct inotify_event *event ;
        inotify.length = read(inotify.fd, inotify.buffer, 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);