【實(shí)戰(zhàn)技巧】使用inotify實(shí)現(xiàn)實(shí)時(shí)文件監(jiān)控
開篇
? 之前閱讀《Linux系統(tǒng)編程》時(shí),留意到了一個(gè)Linux原生接口inotify
杭措。它能夠監(jiān)控文件的移動(dòng)、讀取、寫入和刪除等操作午阵。今天利用空閑時(shí)間,簡(jiǎn)單研究了一下如何使用這個(gè)接口,并在這里記錄下來底桂,方便將來需要查詢和參考植袍。
概述
? inotify
是 Linux 內(nèi)核提供的一種文件系統(tǒng)事件監(jiān)控機(jī)制,允許用戶空間程序監(jiān)視文件或目錄的各種操作籽懦。
? 通過注冊(cè)監(jiān)聽事件于个,程序可以實(shí)時(shí)捕獲文件的創(chuàng)建、刪除暮顺、移動(dòng)和修改等操作厅篓,無需輪詢,極大地提高了效率和實(shí)時(shí)性捶码。inotify
提供了一種高效的方式來構(gòu)建實(shí)時(shí)文件同步羽氮、監(jiān)控日志變更和自動(dòng)化任務(wù)等應(yīng)用,是現(xiàn)代 Linux 系統(tǒng)編程中不可或缺的一部分惫恼。
基礎(chǔ)接口
- int inotify_init(void) / int inotify_init1(int flags)
原型 | int inotify_init(void) |
---|---|
功能 | 初始化一個(gè)新的 inotify 實(shí)例档押,并返回一個(gè)新的文件描述符,用于后續(xù)的操作祈纯。 |
參數(shù) |
flags : ? IN_CLOEXEC: 在執(zhí)行 execve() 后關(guān)閉文件描述符(FD_CLOEXEC)令宿。 ? IN_NONBLOCK:在新的文件描述符上設(shè)置 O_NONBLOCK 文件狀態(tài)標(biāo)志。使用這個(gè)標(biāo)志可以避免額外調(diào)用 fcntl(2) 來達(dá)到同樣的效果腕窥。 |
返回值 | 成功:返回一個(gè)新的文件描述符粒没,用于標(biāo)識(shí) inotify 實(shí)例。 失斢桶骸:返回 -1革娄,并設(shè)置 errno 來指示錯(cuò)誤類型。 |
- int inotify_add_watch(int fd, const char *pathname, uint32_t mask)
原型 | int inotify_add_watch(int fd, const char *pathname, uint32_t mask) |
---|---|
功能 | 向指定的 inotify 實(shí)例中添加一個(gè)新的文件或目錄的監(jiān)視冕碟,并指定要監(jiān)視的事件類型拦惋。 |
參數(shù) |
fd :? inotify_init 返回的文件描述符,標(biāo)識(shí)要添加監(jiān)視的 inotify 實(shí)例安寺。 pathname :要監(jiān)視的文件或目錄的路徑名厕妖。mask :要監(jiān)視的事件類型的位掩碼,可以是以下之一或多個(gè)值的按位或組合:? IN_ACCESS:文件被訪問挑庶。 ? IN_MODIFY:文件被修改言秸。 ? IN_ATTRIB:文件屬性被修改。 ? IN_CLOSE_WRITE:可寫文件被關(guān)閉迎捺。 ? IN_CLOSE_NOWRITE:不可寫文件被關(guān)閉等举畸。 |
返回值 | 成功:返回一個(gè)新的監(jiān)視描述符,用于標(biāo)識(shí)此次監(jiān)視凳枝。 失敵凇:返回 -1跋核,并設(shè)置 errno 來指示錯(cuò)誤類型。 |
- int inotify_rm_watch(int fd, int wd)
原型 | int inotify_rm_watch(int fd, int wd) |
---|---|
功能 | 從 inotify 實(shí)例中移除先前添加的監(jiān)視叛买。 |
參數(shù) |
fd :inotify_init 返回的文件描述符砂代,標(biāo)識(shí)要移除監(jiān)視的 inotify 實(shí)例。wd :要移除的監(jiān)視描述符率挣,即調(diào)用 inotify_add_watch 返回的監(jiān)視描述符刻伊。 |
返回值 | 成功:返回 0。 失斀饭Α:返回 -1捶箱,并設(shè)置 errno 來指示錯(cuò)誤類型。 |
源碼示例
- 需求: 實(shí)現(xiàn)監(jiān)聽指定路徑下文件的修改事件蛾茉,在事件發(fā)生時(shí)實(shí)時(shí)打印讼呢。
-
實(shí)現(xiàn):
inotify
接口使用起來比較簡(jiǎn)單撩鹿,大致分為三個(gè)步驟:
①inotify
初始化
② 增加監(jiān)視對(duì)象以及監(jiān)聽類型
③ 等待事件觸發(fā)谦炬,可以結(jié)合I/O復(fù)用。
整理流程比較簡(jiǎn)單节沦,這里簡(jiǎn)單列舉一下事件觸發(fā)響應(yīng)和測(cè)試流程示例:
-
事件觸發(fā)響應(yīng)流程
簡(jiǎn)單展示inotify
阻塞模式的使用示例键思,阻塞等待事件觸發(fā),并及時(shí)處理甫贯。
int InotifyManager::WaitForEvents() {
const int bufferSize = 1024; // 根據(jù)實(shí)際需求調(diào)整緩沖區(qū)大小
char buffer[bufferSize];
ssize_t numRead = read(inotifyFd, buffer, bufferSize);
if (numRead == -1) {
if (errno != EAGAIN) {
std::cerr << "Error reading from inotify: " << strerror(errno) << std::endl;
return -1;
}
return 0;
}
// 處理所有事件
int offset = 0;
while (offset < numRead) {
struct inotify_event* event = reinterpret_cast<struct inotify_event*>(&buffer[offset]);
if (event->len > 0) {
std::cout << "Inotify event: " << event->name;
if (event->mask & IN_CREATE)
std::cout << " created";
if (event->mask & IN_DELETE)
std::cout << " deleted";
std::cout << std::endl;
}
offset += sizeof(struct inotify_event) + event->len;
}
return 0;
}
-
測(cè)試代碼
循環(huán)等待inotify
事件吼鳞,由于是阻塞模式,不會(huì)導(dǎo)致空轉(zhuǎn)叫搁。
int main() {
InotifyManager inotifyManager;
// 監(jiān)聽當(dāng)前目錄下的文件創(chuàng)建和刪除事件
inotifyManager.AddWatch(".", IN_CREATE | IN_DELETE);
while (true) {
// 等待并處理事件
inotifyManager.WaitForEvents();
}
return 0;
}
效果驗(yàn)證
-
驗(yàn)證流程:
① 左邊窗口執(zhí)行監(jiān)聽程序赔桌。
② 右邊窗口先后執(zhí)行創(chuàng)建和刪除hello文件。 -
驗(yàn)證效果:
? 監(jiān)聽程序能夠及時(shí)打印出對(duì)應(yīng)創(chuàng)建和刪除事件渴逻。
驗(yàn)證.png
總結(jié)
- 從使用流程來看疾党,
inotify
用起來比較簡(jiǎn)單,只需要初始化惨奕、添加事件和讀取事件即可雪位。 -
inotify
句柄也可以通過 I/O 多路復(fù)用(如 epoll/select/poll)進(jìn)行監(jiān)聽,實(shí)現(xiàn)事件的及時(shí)響應(yīng)梨撞。 -
inotify
使用場(chǎng)景也比較多雹洗,例如界面實(shí)時(shí)刷新顯示文件;監(jiān)聽配置文件變更動(dòng)態(tài)加載配置卧波;作為調(diào)試手段时肿,模擬外部事件觸發(fā)內(nèi)部響應(yīng)以及記錄一些敏感文件的修改記錄等等。 - 了解這種原生接口港粱,以后如果有需求螃成,能夠讓實(shí)現(xiàn)多一種方式,同時(shí)減少開發(fā)中的復(fù)雜性和時(shí)間成本,或許也能夠少走一些“彎路”锈颗。