最近还是无心写啥文章,说好的写几篇关于 Raft 的论文也因为一些事 delay 了。但是想了想还是准备写点什么,于是写个小的水文来记录下关于今天碰到的一个 Linux 内核参数的问题, 顺便做个笔记
开始
我是一个不太喜欢 Mac 的人,所以我自己在家使用的开发环境是 Manjaro(这里打个广告,非常棒的发行版,堪称开箱即用,广告五毛一条)。然后代码工具就是 Jetbrains 的全家桶和 VSCode 搭配使用。
今天打开 Goland 的时候,发现 IDE 给了这样一个 Warning ,External file changes sync may be slow: The current inotify(7) watch limit is too low.
于是大家知道,我是个看着这些 warning 有强迫症的人,于是我就去查了查
简单聊聊
我们平常经常会有需求,去监控一个文件或者一个目录下的变化,比如创建文件,删除文件等。我们常规的做法可能是一个直接暴力轮询的方式来做
但是这样的性能会极差。那么我们有没有什么手段来处理一下这个事么?
有的! Linux 提供了对应的 API 来处理这事,这就是我们今天要聊到的 inotify
按照官方的说法,inotify
其实很简单,
The inotify API provides a mechanism for monitoring file system events. Inotify can be used to monitor individual files, or to monitor directories. When a directory is monitored, inotify will return events for the directory itself, and for files inside the directory.
大意就是说 inotify
是用来监控文件系统事件的。可以使用在单个文件或者目录上。被监听的文件目录本身的变化或者内部文件的变化都在监听范围内。
在监听了对应的文件后,inotify
将返回如下事件
IN_ACCESS 文件可读
IN_ATTRIB 元数据变化
IN_CLOSE_WRITE File opened for writing was closed
IN_CLOSE_NOWRITE File not opened for writing was closed
IN_CREATE 被监听的目录下有文件/目录被创建
IN_DELETE 被监听的目录下有文件/目录被删除
IN_DELETE_SELF 被监听的文件/目录被删除
IN_MODIFY 文件被修改
IN_MOVE_SELF 被监听的文件/目录被移动
IN_MOVED_FROM 有文件/目录从被监听的目录中被移出
IN_MOVED_TO 有文件/目录移动至被监听的目录中
IN_OPEN 文件被打开
总共12类事件,已经能涵盖住我们常见的需求。但是 inotify
也有其自己的弊端。
不支持递归监听。举个例子,我监听 A 目录,我可以捕获到在 A 目录下创建 B 目录这个事件。但是我们没法监听到 B 目录下事件,除非将 B 目录也添加到监听队列中
Python 可用的 inotify 很少
对于第一个缺陷。常见的解决手段,是我们自行实现递归监听。当主目录下存在创建文件/目录事件的时候,我们将对应的文件/目录也添加到监听队列中。
但是这样就带来一个新的问题。如果一个非常大的项目,我们按照这样的方式去做,那么最后对应的内存损耗是很吓人的。所以在 inotify
设计之初,就通过一些内核参数做了一些限制
我们常见的有两个
/proc/sys/fs/inotify/max_queued_events 限制事件队列长度,一旦出现事件堆积,那么新的事件将被废弃
/proc/sys/fs/inotify/max_user_watches 限制每个 User ID 能够创建的 watcher 数,以免监听过多导致内存爆炸
在默认情况下 max_user_watches
的值取决于不同的 Linux 发行版,对于大多数发行版而言,其值相对较小。也就是说一旦达到限制,那么将没法添加新的 watcher。这也是 IDE 为什么会提示External file changes sync may be slow: The current inotify(7) watch limit is too low.
的原因
可以通过修改 /etc/sysctl.conf
来修改对应的参数,最后解决这个问题
最后
Linux 果然是个宝库。感觉隔三差五就会遇到自己没涉及到的东西。所以还是记录下来,当作一篇水文,顺便供自己参阅