layout: post title: inotify監控檔案實作動態更新
Linux kernel中的inotify模組提供了很好的檔案監控支援
組合poll或select可以寫出簡潔高效的程式
但要從inotify的watching list中新增或刪除對象時
就需要先主動從poll的等待狀態中離開
使用固定時間timedout的話,可以在poll時順帶傳入timedout數
缺點是浪費時間,程式複雜
而Signal雖然也能達到中斷效果,但在有多執行緒的程式中
除非為所有執行緒設定signal mask,否則無法保證哪個執行緒會收到Signal
搭配pselect就顯得不可靠
最後決定在程式內使用pipe,並將pipe的read end加入poll中
當有新的檔案需要被監控或移除時,只要往write end寫入資料
就能達到提醒poll的thread要更新watching list的效果
/*
`A /home`
`R 1`
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#include <sys/inotify.h>
void serve(int fd)
{
struct pollfd fds[2];
int inotify_fd = inotify_init();
fds[0].fd = inotify_fd;
fds[0].events = POLLIN;
fds[1].fd = fd;
fds[1].events = POLLIN;
int rc = 0;
while((rc = poll(fds, 2, -1)) > 0) // should handle EINTR
{
if(fds[1].revents & POLLIN) {
char line[1024] = {};
read(fd, line, 1024);
if(line[0] == 'A') {
int wd = inotify_add_watch(inotify_fd, line+2, IN_ALL_EVENTS); // should handle error
if(wd >= 0)
printf("%s has been added with wd %d\n", line+2, wd);
else
printf("add failed %s\n", strerror(errno));
} else if(line[0] == 'R') {
int wd = atoi(line+2);
int rc = inotify_rm_watch(inotify_fd, wd); // should handle error
if(rc < 0)
{
perror("rm_watch");
exit(1);
}
}
}
if(fds[0].revents & POLLIN) {
char buff[1024] = {};
read(inotify_fd, buff, 1024);
// handle inotify events
}
}
}
int main()
{
pid_t pid;
int fds[2];
pipe(fds);
if(pid = fork()) {
// parent
while(1) {
char line[1024] = {};
if(!fgets(line, 1024, stdin))break;
int len = strlen(line);
line[len-1] = 0;
write(fds[1], line, len-1);
}
} else {
serve(fds[0]);
}
return 0;
}