linux inotify的一些坑

linux inotify的一些坑

inotify使用场景

linux提供了一种机制,可以动态感知文件的变化:inotify。
使用inotify可以感知到某个目录或者某个文件所有的动态操作。

和poll结合使用

可以在poll中监听inotify_fd,来达到动态监听文件变更的目的。

遇到的坑

不过最近在使用的时候,遇到了一些坑。
最初是想要直接监听某个json文件的变更,结果发现vim文件后,收到了一堆events,并且监听也失效了,如下代码:

#include 
#include 
#include 
#include 
#include 
#include 

void display_event(struct inotify_event *event) {
  std::string operate;

  if (event->mask & IN_ACCESS)
    operate = "ACCESS";
  if (event->mask & IN_ATTRIB)
    operate = "ATTRIB";
  if (event->mask & IN_CLOSE_WRITE)
    operate = "CLOSE_WRITE";
  if (event->mask & IN_CLOSE_NOWRITE)
    operate = "CLOSE_NOWRITE";
  if (event->mask & IN_CREATE)
    operate = "CREATE";
  if (event->mask & IN_DELETE_SELF)
    operate = "DELETE_SELF";
  if (event->mask & IN_MODIFY)
    operate = "MODIFY";
  if (event->mask & IN_MOVE_SELF)
    operate = "MOVE_SELF";
  if (event->mask & IN_MOVED_FROM)
    operate = "MOVED_FROM";
  if (event->mask & IN_MOVED_TO)
    operate = "MOVED_TO";
  if (event->mask & IN_OPEN)
    operate = "OPEN";
  if (event->mask & IN_IGNORED)
    operate = "IGNORED";
  if (event->mask & IN_DELETE)
    operate = "DELETE";
  if (event->mask & IN_UNMOUNT)
    operate = "UNMOUNT";

  std::cout << "event name:" << event->name << " operate:" << operate
            << std::endl;
}

int main() {
  int inotify_fd = -1;
  int inotify_watch = -1;
  std::string file_path("/home/minipc/test/test.json");

  inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
  inotify_watch =
      inotify_add_watch(inotify_fd, file_path.c_str(), IN_ALL_EVENTS);
  std::cout << "inotify_fd:" << inotify_fd << " inotify_watch:" << inotify_watch
            << " file path:" << file_path << std::endl;

  while (true) {
    constexpr int nfds = 1;
    struct pollfd fds[nfds];

    int16_t events = 0;
    fds[0] = {inotify_fd, POLLIN, 0};
    int nready = ::poll(fds, nfds, -1);
    if (nready < 0) {
      std::cout << "poll failed, nready:" << nready
                << " err:" << strerror(errno) << std::endl;
      continue;
    }

    for (int i = 0; i < nfds; ++i) {
      int fd = fds[i].fd;
      int revents = fds[i].revents;

      if (fd == inotify_fd) {
        if (revents & POLLIN) {
          char events[4096];
          struct inotify_event *event;
          int nbytes, offset;
          nbytes = ::read(fd, events, sizeof(events));
          for (offset = 0; offset < nbytes;) {
            event = (struct inotify_event *)&events[offset];
            display_event(event);
            offset += sizeof(struct inotify_event) + event->len;
          }
        }
      }
    }
  }

  return 0;
}
  • 这里第一个坑是inotify_init是阻塞式的。如果想要非阻塞,需要使用inotify_init1接口。
  • 第二个坑就是直接监听文件,如果通过vim修改文件,会上来一堆事件,并且并没有截获IN_MODIFY事件,如下:
event name: operate:OPEN
event name: operate:CLOSE_NOWRITE
event name: operate:OPEN
event name: operate:CLOSE_NOWRITE
event name: operate:MOVE_SELF
event name: operate:ATTRIB
event name: operate:DELETE_SELF
event name: operate:IGNORED

并且再次通过vim修改test.json,也不会上来event事件了。

这里原因如下:

  • 上来很多事件是因为vim编辑时会先将文件保存为a.swap,等编辑完毕后再移动回来或者删除,具体移动回来还是删除,取决于是退出vim时时保存操作,还是取消操作。所以这里根本没有触发IN_MODIFY事件,实际上触发的是IN_MOVE_TO事件(a文件被移出),以及IN_IGNORED事件等。
  • 再次修改不会上来event事件,也是因为vim修改文件后,本质文件fd已经变了。

修改方式:

上面两个问题,可能有人会这么处理,poll轮询的时候每次都重新生成inotify_fd,再重新监听,但是这种操作比较麻烦,代码写的也不是很简洁,所以这里可以换种思路,直接监听上层目录,这样fd就不会变,也不用反复重新监听。

修改后代码如下:

#include 
#include 
#include 
#include 
#include 
#include 

void display_event(struct inotify_event *event) {
  std::string operate;

  if (event->mask & IN_ACCESS)
    operate = "ACCESS";
  if (event->mask & IN_ATTRIB)
    operate = "ATTRIB";
  if (event->mask & IN_CLOSE_WRITE)
    operate = "CLOSE_WRITE";
  if (event->mask & IN_CLOSE_NOWRITE)
    operate = "CLOSE_NOWRITE";
  if (event->mask & IN_CREATE)
    operate = "CREATE";
  if (event->mask & IN_DELETE_SELF)
    operate = "DELETE_SELF";
  if (event->mask & IN_MODIFY)
    operate = "MODIFY";
  if (event->mask & IN_MOVE_SELF)
    operate = "MOVE_SELF";
  if (event->mask & IN_MOVED_FROM)
    operate = "MOVED_FROM";
  if (event->mask & IN_MOVED_TO)
    operate = "MOVED_TO";
  if (event->mask & IN_OPEN)
    operate = "OPEN";
  if (event->mask & IN_IGNORED)
    operate = "IGNORED";
  if (event->mask & IN_DELETE)
    operate = "DELETE";
  if (event->mask & IN_UNMOUNT)
    operate = "UNMOUNT";
    
  if ((event->mask & IN_MODIFY) && (0 == strcmp("test.json", event->name))) {
    std::cout << "event name:" << event->name << " operate:" << operate
              << std::endl;
  }
}

int main() {
  int inotify_fd = -1;
  int inotify_watch = -1;
  std::string file_path("/home/minipc/test");

  inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
  inotify_watch =
      inotify_add_watch(inotify_fd, file_path.c_str(), IN_ALL_EVENTS);
  std::cout << "inotify_fd:" << inotify_fd << " inotify_watch:" << inotify_watch
            << " file path:" << file_path << std::endl;

  while (true) {
    constexpr int nfds = 1;
    struct pollfd fds[nfds];

    int16_t events = 0;
    fds[0] = {inotify_fd, POLLIN, 0};
    int nready = ::poll(fds, nfds, -1);
    if (nready < 0) {
      std::cout << "poll failed, nready:" << nready
                << " err:" << strerror(errno) << std::endl;
      continue;
    }

    for (int i = 0; i < nfds; ++i) {
      int fd = fds[i].fd;
      int revents = fds[i].revents;

      if (fd == inotify_fd) {
        if (revents & POLLIN) {
          char events[4096];
          struct inotify_event *event;
          int nbytes, offset;
          nbytes = ::read(fd, events, sizeof(events));
          for (offset = 0; offset < nbytes;) {
            event = (struct inotify_event *)&events[offset];
            display_event(event);
            offset += sizeof(struct inotify_event) + event->len;
          }
        }
      }
    }
  }

  return 0;
}

修改后结果如下:

event name:test.json operate:MODIFY

你可能感兴趣的:(linux,excel,运维)