linux inotify的一些坑

news/2024/7/21 5:51:13 标签: linux, excel, 运维

linux_inotify_0">linux inotify的一些坑

inotify使用场景

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

和poll结合使用

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

遇到的坑

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

#include <iostream>
#include <poll.h>
#include <string.h>
#include <string>
#include <sys/inotify.h>
#include <unistd.h>

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 <iostream>
#include <poll.h>
#include <string.h>
#include <string>
#include <sys/inotify.h>
#include <unistd.h>

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

http://www.niftyadmin.cn/n/4925991.html

相关文章

Django 初级指南:创建你的第一个 Django 项目

Django 是一个强大的 Python Web 框架&#xff0c;它采用了“模型-视图-控制器”&#xff08;MVC&#xff09;的设计模式&#xff0c;能够帮助开发者快速、简洁地创建高质量的 Web 应用。这篇文章将引导你创建你的第一个 Django 项目。 一、安装 Django 首先&#xff0c;你需…

VR内容定制 | VR内容中控管理平台可以带来哪些价值?

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐渗透到各个领域&#xff0c;其中教育领域也不例外。通过VR技术&#xff0c;学生可以身临其境地参与到各种场景中&#xff0c;获得更加直观、生动的学习体验。为了让教师更好地进行VR教学的设计和管理&#xff0c;提高教…

python去掉列表数据中的最大最小值

python去掉列表数据中的最大最小值 有一个列表数据为: data = [1,29,3,3,4,5,1,3,4,5,6,7,80,9,9,9,4]现在需要去掉列表中的最大值、最小值。 步骤如下: 1、先获取最大值 max_value = max(data)2、然后获取最小值 min_value = min(data)3、使用filter进行过滤,满足条件的…

【云原生】Kubernetes节点亲和性分配 Pod

目录 1 给节点添加标签 2 根据选择节点标签指派 pod 到指定节点[nodeSelector] 3 根据节点名称指派 pod 到指定节点[nodeName] 4 根据 亲和性和反亲和性 指派 pod 到指定节点 5 节点亲和性权重 6 pod 间亲和性和反亲和性及权重 7 污点和容忍度 8 Pod 拓扑分布约束 官方…

ASL国产CS5213 转VGA信号输出音频 替代AG6200安格芯片 HDMI to VGA(带音频)方案设计原理图

CS5213功能&#xff1a;HDMI转VGA带音频输出&#xff0c;专注于设计HDMI转VGA带音频输出。可替代AG6200 AG6201。 CS5213芯片是一个HDMI&#xff08;高清多媒体接口&#xff09;到VGA桥接芯片。 它将HDMI信号转换为标准VGA信号它可以在适配器、智能电缆等设备中设计。 Capst…

Mysql下载及其安装的详细步骤

1.下载压缩包 官网地址&#xff1a;www.mysql.com 2.环境配置 1.先解压压缩包 2.配置环境变量 添加环境变量&#xff1a;我的电脑--->属性-->高级-->环境变量-->系统变量-->path 3.在mysql安装目录下新建my.ini文件并&#xff0c;编辑my.ini文件 编辑内容如…

SpringBoot WebSocket配合react 使用消息通信

引入websocket依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>配置websocket import org.springframework.context.annotation.Bean; import org.spr…

Java 中 Set集合常用方法

.add() 添加元素 对象名.add() 向Set集合中添加元素 &#xff08;但不能添加重复元素&#xff0c;Set集合中不允许元素重复&#xff09; Set<String> s new HashSet<String>(); // 添加数据 s.add("aaa"); s.add("bbb"); addAll(Collectio…