前言:從 dnotify 到 inotify
早期 Linux 採用 dnotify 監控目錄變更,但存在嚴重缺陷:需為每個目錄打開 fd(易耗盡 fd 上限)、僅支援目錄級事件(需自行比對 stat 判斷具體檔案)、介面基於信號(難用)。inotify 為替代方案,提供檔案/目錄級事件、不佔用 fd(單一實例 fd 監控多目標)、標準 read/select/poll 介面、可移動裝置自動清理 watch。
用戶空間 API 三部曲
// 1. 建立 inotify 實例
int fd = inotify_init(); // 返回檔案描述符,對應內核排序事件隊列
// 2. 新增/修改監控目標
int wd = inotify_add_watch(fd, "/path/to/watch", IN_MODIFY | IN_CREATE | IN_DELETE);
// 3. 讀取事件
char buf[4096];
ssize_t len = read(fd, buf, sizeof(buf));
struct inotify_event *event = (struct inotify_event *)buf;
while ((char *)event - buf < len) {
// 處理 event->mask, event->name, event->wd...
event = (struct inotify_event *)((char *)event + sizeof(*event) + event->len);
}
// 4. 移除監控 / 關閉實例
inotify_rm_watch(fd, wd);
close(fd); // 自動清理所有 watch
支援的事件類型(部分)
| 事件常數 | 觸發條件 |
IN_ACCESS | 檔案被讀取 |
IN_MODIFY | 檔案被寫入 |
IN_ATTRIB | 屬性變更 (chmod/chown/touch) |
IN_CREATE / IN_DELETE | 目錄下建立/刪除檔案 |
IN_MOVED_FROM / IN_MOVED_TO | 檔案移入/移出目錄 |
IN_DELETE_SELF | 監控目標本身被刪除 |
IN_UNMOUNT | 檔案系統卸載 |
內核實現核心資料結構
inotify_device — 每個實例一個
struct inotify_device {
wait_queue_head_t wq; // read 等待隊列
struct idr idr; // wd -> watch 映射
struct list_head events; // 待讀事件隊列
struct list_head watches; // 該實例所有 watch
atomic_t count; // 參考計數
// ... 队列大小限制、最大事件數等
};
inotify_watch — 每個監控目標一個
struct inotify_watch {
struct list_head d_list; // 掛在 device->watches
struct list_head i_list; // 掛在 inode->inotify_watches
struct inotify_device *dev;
struct inode *inode; // 被監控的 inode
s32 wd; // watch descriptor
u32 mask; // 關注的事件掩碼
};
inode 擴展欄位
struct inode {
// ...
struct list_head inotify_watches; // 監控此 inode 的所有 watch
struct semaphore inotify_sem; // 保護 watches 列表
};
事件產生與投遞流程
- 檔案系統操作 (VFS 層) 觸發對應
fsnotify_* 函式(如 fsnotify_modify、fsnotify_create)。
- 最終呼叫
inotify_inode_queue_event:檢查 inode->inotify_watches,遍歷匹配 mask 的 watch。
- 呼叫
inotify_dev_queue_event:去重(避免重複事件)、檢查隊列溢位(產生 IN_Q_OVERFLOW)、建立 inotify_kernel_event 掛入 device->events。
- 喚醒
device->wq 等待隊列上的進程。
- 用戶態
read 從 events 列表複製資料到用戶緩衝區。
實戰應用場景
- 桌面搜尋引擎 (Beagle, Tracker):即時索引檔案變更。
- 開發工具熱重載:
webpack --watch、nodemon、IDE 檔案同步。
- 配置管理:Consul Template、Confd 監控配置檔變更自動重載服務。
- 安全審計:監控敏感目錄 (
/etc, /ssh) 異常變更。
- 日誌輪轉:
logrotate 監控日誌檔建立/變更觸發切割。
高效能使用建議
- 使用
inotify_init1(IN_NONBLOCK | IN_CLOEXEC) 避免阻塞與 fd 洩漏。
- 單一
read 緩衝區建議 4KB~64KB,一次讀取多個事件減少系統呼叫。
- 搭配
epoll 監控 inotify fd,整合進主事件迴圈。
- 注意
/proc/sys/fs/inotify/max_user_watches 系統限制(預設 8192),大規模監控需調大。
- 處理
IN_MOVE_SELF/IN_DELETE_SELF 事件時需重新 add_watch(舊 wd 失效)。
總結
inotify 以優雅的「單一 fd 管理多目標、標準 I/O 介面、內核級事件過濾」設計,成為 Linux 檔案系統事件監控的標準基礎設施。掌握其 API 與內核流程,對開發即時檔案處理、配置熱載入、安全審計等系統級應用至關重要。
- 上一篇文章: Linux 並發 I/O 三大方案深度對比:非阻塞 I/O、I/O 多路復用、異步 I/O
- 下一篇文章: C 語言計算輸入數字中所有偶數位之和
Copyright ©
程式碼之家 All Rights Reserved