个人能力有限,文中有不当和错误给您带来的不便还请谅解。
用inotify可以检测文件系统中文件和目录发生的变化,而epoll可以同时检测多个文件。这里将epoll和inotify结合起来使用,测试这样一种情况:指定相应目录,当目录中有文件创建或者删除时有相应的通知信息并把文件添加或者移除epoll的检测机制,当文件可读时,通过epoll机制将指定的文件的内容读取出来。
基于这个功能实现的例子如下所示:
#include <stdio.h> #include <sys/inotify.h> #include <sys/epoll.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #define BUFFER_SIZE 512 // 定义缓冲区的大小 #define ARRAY_LENGTH 128 // 定义数组的长度 #define NAME_LENGTH 128 // 定义文件名长度的限制 /* 定义epoll最大监听的文件数量 */ #define EPOLL_MAX_EVENTS 32 /* 定义一个结构体用来存放一个文件对应的文件描述符和文件名 */ struct file_name_fd_desc { int fd; // 文件的描述符 char name[32]; // 文件名 char base_name[NAME_LENGTH]; // 带绝对路径的文件名 }; /* 定义一个epoll事件的数组,用来存放监听文件的信息 */ static struct epoll_event g_PendingEventItems[EPOLL_MAX_EVENTS]; /* 定义一个数组用来存放对应文件的文件描述符和文件名 */ static struct file_name_fd_desc g_file_name_fd_desc[ARRAY_LENGTH]; static int array_index = 0; /* 定义一个存放基目录册指针 */ static char *base_dir; /* * 向epoll当中添加一个要监视的文件 */ static int add_to_epoll(int epoll_fd, int fd) { int result; struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; // epoll检测相应的文件的读操作将被唤醒 eventItem.data.fd = fd; result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &eventItem); return result; } /* * 将指定文件从epoll中删除 */ static void remove_from_epoll(int epoll_fd, int fd) { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); } /* * 通过文件描述符获得文件名 */ static int get_name_from_fd(int fd, char **name) { int i; /* 对整个数组进行遍历 */ for(i = 0; i < ARRAY_LENGTH; i++) { if(fd == g_file_name_fd_desc[i].fd) { *name = g_file_name_fd_desc[i].name; return 0; } } return -1; } /* inotify 监听事件的消息处理机制: * a、当创建一个文件时,把这个文件添加到epoll的监测文件当中 * b、当删除一个文件时,把这个文件从epoll的监测文件当中移除 */ static int inotify_ctl_info(int inotify_fd, int epoll_fd) { char event_buf[BUFFER_SIZE]; int event_pos = 0; int event_size; struct inotify_event *event; int result; int tmp_fd; int i; /* 读取inotify的监视事件的信息 */ memset(event_buf, 0, BUFFER_SIZE); result = read(inotify_fd, event_buf, sizeof(event_buf)); if(result < (int)sizeof(*event)) { printf("could not get event!\n"); return -1; } /* 将获得的inotify信息打印出来 */ while(result >= (int)sizeof(*event)) { event = (struct inotify_event *)(event_buf + event_pos); if(event->len) { if(event->mask & IN_CREATE) // 创建一个文件时 { /* 将文件名name关联起来 */ sprintf(g_file_name_fd_desc[array_index].name, "%s", event->name); sprintf(g_file_name_fd_desc[array_index].base_name, "%s/%s", base_dir, event->name); tmp_fd = open(g_file_name_fd_desc[array_index].base_name, O_RDWR); if(-1 == tmp_fd) { printf("inotify_ctl_info open error!\n"); return -1; } add_to_epoll(epoll_fd, tmp_fd); // 将创建的文件加入到epoll当中 /* 将文件描述符fd关联起来 */ g_file_name_fd_desc[array_index].fd = tmp_fd; if(ARRAY_LENGTH == array_index) { array_index = 0; } array_index += 1; printf("add file to epoll : %s\n", event->name); } else // 删除一个文件时 { for(i = 0; i < ARRAY_LENGTH; i++) { if(!strcmp(g_file_name_fd_desc[i].name, event->name)) { remove_from_epoll(epoll_fd, g_file_name_fd_desc[i].fd); // 将指定的文件从epoll中删除 /* 将数组中指定的文件名和文件描述符清空 */ g_file_name_fd_desc[i].fd = 0; memset(g_file_name_fd_desc[i].name, 0, sizeof(g_file_name_fd_desc[i].name)); memset(g_file_name_fd_desc[i].base_name, 0, sizeof(g_file_name_fd_desc[i].base_name)); printf("remove file from epoll : %s\n", event->name); break; } } } } /* 更新位置信息,以便获得下一个 inotify_event 对象的首地址*/ event_size = sizeof(*event) + event->len; result -= event_size; event_pos += event_size; } return 0; } /* 检测指定目录 : 有文件被创建或者删除,有文件可以读出数据 * a. 当在指定目录下创建文件时, 会立刻监测到,并且使用epoll监测该文件 * b. 当文件有数据时,读出数据 * c. 当指定目录下文件被删除时,会立刻监测到,并且把它从epoll中移除不再监测 * Usage : inotify_epoll <dir> */ int main(int argc, char *argv[]) { int inotify_fd; int epoll_fd; int result; int i; char readbuf[BUFFER_SIZE]; int readlen; char *tmp_name; if(2 != argc) { printf("Usage : %s <dir>\n", argv[0]); return -1; } /* 将传入的参数作为基目录 */ base_dir = argv[1]; /* 打开一个epoll文件的文件描述符 */ epoll_fd = epoll_create(1); // 传入的参数可以随意,只要大于0即可,没有什么具体意思 if(-1 == epoll_fd) { printf("epoll_create error!\n"); return -1; } /* 初始化一个inotify的实例,获得一个该实例的文件描述符 */ inotify_fd = inotify_init(); /* 添加一个用于监视的目录:主要监视该目录中文件的添加和移除 */ result = inotify_add_watch(inotify_fd, base_dir, IN_DELETE | IN_CREATE); if(-1 == result) { printf("inotify_add_watch error!\n"); return -1; } /* 将inotify_fd将入到epoll当中 */ add_to_epoll(epoll_fd, inotify_fd); /* 通过循环来处理不同情况 */ while(1) { /* 调用epoll_wait来监听事件 */ result = epoll_wait(epoll_fd, g_PendingEventItems, EPOLL_MAX_EVENTS, -1); if(-1 == result) // 发生错误 { printf("epoll wait error!\n"); return -1; } else { for(i = 0; i < result; i++) // 对监听到的事件进行遍历 { if(g_PendingEventItems[i].data.fd == inotify_fd) // inotify事件发生的情况下 { if(-1 == inotify_ctl_info(inotify_fd, epoll_fd)) { printf("inotify_ctl_info error!\n"); return -1; } } else // 非inotify事件,则把文件的内容读取出来 { if(!get_name_from_fd(g_PendingEventItems[i].data.fd, &tmp_name)) { readlen = read(g_PendingEventItems[i].data.fd, readbuf, BUFFER_SIZE); readbuf[readlen] = '\0'; printf("read data from %s : %s\n", tmp_name, readbuf); } } } } } return 0; } 编译并运行,测试结果如下: