inotify允许应用程序监控文件事件。
inotify API有以下关键步骤:
- 应用程序使用inotify_init()来创建一inotify实例,返回的文件描述符用于在后续操作中指代该实例。
- 应用程序使用inotify_add_watch()向inotify实例(由步骤1创建)的监控列表添加条目,并指定监控的事件集合。
- 应用程序针对inotify文件描述符执行read()操作获得事件通知。每次read都会返回一个或多个inotify_event结构,其中各自记录了处于inotify实例监控之下的某一路径名所发生的事件。
知道了具体监控流程之后,我们来做道题,巩固学习的知识!
练习题
题目描述
编写一程序,针对其命令行参数所指定的目录,记录所有的文件创建、删除和改名操作。该程序所能够监控指定目录下所有子目录中的事件。获得所有子目录的列表需使用nftw()。当在目录树下添加或删除了子目录时,受监控的子目录集合应能保持同步更新。
解答
要想子目录添加或删除文件,能让监控子目录更新。即在子目录下添加文件,这个文件就要添加到监控列表中。这需要用一个数据结构来记录。这里我用的链表来记录监控的文件和对应监控描述符,看具体代码:
#define _XOPEN_SOURCE 600
#include<sys/inotify.h>
#include<stdio.h>
#include<limits.h>
#include<string.h>
#include<ftw.h>
#include<unistd.h>
#include<sys/stat.h>
#include<malloc.h>
#define BOOL short
#define FALSE 0
#define TRUE 1
/*链表结点包含监控文件与对应监控描述符,随着监控的更新来更新该链表*/
typedef struct WatchdiscriptorFile
{
int wd;
char pathname[256];
BOOL head;
struct WatchdiscriptorFile* next;
struct WatchdiscriptorFile* pre;
}wdfnode;
/*插入结点操作,每次添加目录或文件,将其纳入监控中,并尾插到链表中*/
void PushNode(wdfnode* head,int wd,const char* pathname)
{
if(head == NULL || head->head == FALSE)
{
printf("Push node failed !\n");
return;
}
wdfnode* node =(wdfnode*)malloc(sizeof(wdfnode));
node->head = FALSE;
node->wd = wd;
memcpy(node->pathname,pathname,strlen(pathname));
node->pre = head->pre;
node->next = head;
head->pre->next = node;
head->pre = node;
}
/*查找结点,根据监控描述符查找*/
wdfnode* FindNode(wdfnode* head,int wd)
{
if(head == NULL)return NULL;
wdfnode* node = head->next;
while ((node->head != TRUE) && (node->wd != wd))
{
node = node->next;
}
return node->head == TRUE ? NULL : node;
}
/*文件或目录删除,则删除对应结点*/
void DelNode(wdfnode* head,wdfnode* node)
{
if((head == NULL) || (node == NULL))return;
if(head->next == node)
{
free(node);
head->next = head;
head->pre = head;
}else{
node->next->pre = node->pre;
node->pre->next = node->next;
}
}
static wdfnode* wdf = NULL;//链表头结点
static int inotifyFd;//监控文件描述符
/*显示受监控文件或目录的事件:打开、创建、删除、移动*/
static void displayInotifyEvent(struct inotify_event *i)
{
printf(" wd = %2d; ",i->wd);
if(i->cookie > 0)
{
printf("cookie =%4d;",i->cookie);
}
if (i->mask & IN_CREATE)printf("mask = IN_CREATE\n");
if (i->mask & IN_DELETE)printf("mask = IN_DELETE\n");
if (i->mask & IN_DELETE_SELF)printf("mask = IN_DELETE_SELF\n");
if (i->mask & IN_MODIFY)printf("mask = IN_MODIFY\n");
if (i->mask & IN_MOVE_SELF)printf("mask = IN_MOVE_SELF\n");
if (i->mask & IN_MOVED_FROM)printf("mask = IN_MOVED_FROM\n");
if (i->mask & IN_MOVED_TO)printf("mask = IN_MOVED_TO\n");
if (i->mask & IN_OPEN)printf("mask = IN_OPEN\n");
if(i->len > 0)printf("name = %s\n",i->name);
}
#define BUF_LEN (10*sizeof(struct inotify_event)+NAME_MAX+1)
struct stat file_stat;
uint32_t mask = IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN;
/*nftw()函数用到的回调函数,迭代访问所有文件和目录,并一一对其执行该函数*/
int func_oprate(const char* pathname,const struct stat* statbuf,int typeflag,struct FTW* ftwbuf)
{
switch (statbuf->st_mode & S_IFMT)
{
int wd;
case (S_IFREG)://file or dir
if((wd = inotify_add_watch(inotifyFd,pathname,mask)) != -1)
{
PushNode(wdf,wd,pathname);
printf("Watch %s using wd %d\n",pathname,wd);
}
break;
case (S_IFDIR):
if((wd = inotify_add_watch(inotifyFd,pathname,mask)) != -1)
{
PushNode(wdf,wd,pathname);
printf("Watch %s using wd %d\n",pathname,wd);
}
break;
default:
printf("statbuf->st_mode is %d\n",statbuf->st_mode & S_IFMT);
break;
}
return 0;
}
int main(int argc, char const *argv[])
{
int wd,j;
char buf[BUF_LEN];
ssize_t numRead;
char *p;
struct inotify_event *event;
wdf = (wdfnode*)malloc(sizeof(wdfnode));
wdf->next = wdf;
wdf->pre = wdf;
wdf->head = TRUE;
if(argc < 2 || strcmp(argv[1],"--help") ==0)
{
printf("%s pathname... \n",argv[0]);
return 0;
}
if(-1 == (inotifyFd = inotify_init()))
{
printf("error inotify_init");
return 0;
}
printf("inotifyFd is %d\n",inotifyFd);
for (j = 1; j < argc; j++)
{
int nopenfd = 10;
int flags = 0;
if(-1 == nftw(argv[j],func_oprate,nopenfd,flags))
{
printf("walk file return error\n");
perror("walk file return error");
}
}
printf("nftw finished \n");
/*执行死循环,不断读取inotify_event,根据inotify_event来获知增删的文件,并更新链表*/
for(;;)
{
numRead = read(inotifyFd,buf,BUF_LEN);
if(numRead == 0)
{
printf("read() from inotify fd returned 0!");
}
if(numRead == -1)
{
printf("read() from inotify fd returned -1!");
}
printf("Read %ld bytes from inotify fd\n",(long)numRead);
for(p = buf;p<buf+numRead;)
{
event = (struct inotify_event*)p;
displayInotifyEvent(event);
if (event->mask & IN_CREATE)
{
wdfnode* n = FindNode(wdf,event->wd);
char filename[256];
memset(filename,'\0',256);
if(n == NULL){printf("not find Node !\n");}
else{
memcpy(filename,n->pathname,strlen(n->pathname));
strcat(filename,"/");
strcat(filename,event->name);
}
int wd = inotify_add_watch(inotifyFd,filename,mask);
if(wd == -1)
{
printf("not inotify_add_watch %s \n",event->name);
}else{
PushNode(wdf,wd,filename);
printf("inotify_add_watch %s , using wd : %d\n",event->name,wd);
}
}
if (event->mask & IN_DELETE_SELF)
{
wdfnode* n = FindNode(wdf,event->wd);
printf("remove watch wd : %d , pathname is %s\n",n->wd,n->pathname);
DelNode(wdf,n);
}
p += sizeof(struct inotify_event) + event->len;
}
}
return 0;
}
结果
先创建一个测试目录,如下,testdir目录下有一个文件和六个目录,其中c目录下有一个目录c_t:

接着,执行监控程序,


本文通过编写C语言程序,利用inotify API实现对指定目录及其子目录的文件创建、删除和修改事件监控,利用链表维护受监控的子目录和文件。程序利用nftw遍历目录树,确保子目录变化时监控列表同步更新。


被折叠的 条评论
为什么被折叠?



