在linux系统编程中,通常需要对多个文件的变化同时进行监测(称为多路复用)。传统的poll机制在监测文件数量较大时效率会明显的降低,epoll是对传统poll机制的扩展,不会因为监测文件数量过多而导致监测效率降低。本文通过一个小例子来简单说一下epoll的实现原理,这个小例子实现的功能是让epoll同时检测多个文件,当有任何一个文件可读时就把它的内容打印出来。
一、epoll涉及的常用API
1、创建一个epoll实例
int epoll_create(int size); size : 在linux kernel2.6 之后,这个参数就不再使用,但必须大于0;返回值:返回一个epoll的文件描述符
2、epoll设置函数
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); epfd : 前面创建epoll实例时返回的文件描述符op : 具体的操作
EPOLL_CTL_ADD : 向epoll中添加一个要监视的文件
EPOLL_CTL_DEL : 从epoll中删除一个要监视的文件
EPOLL_CTL_MOD : 改变要监视文件的相关属性,具体属性改变的设置在第四个参数event当中
fd : 要操作的文件的文件描述符
event:要操作的文件的相关事件属性设置,这个结构体的具体定义如下:
typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; 3、epoll监听文件IO事件的函数 int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); epfd :创建epoll实例的文件描述符events :表示有哪些文件发生了变化,并把相应的信息放入events数组当中
maxevents :表示最大检测的文件的数量
timeout :表示超时时间,单位是毫秒,-1表示一直等待知道指定的文件状态发生变化。 二、测试 编写一个小例子来测试epoll的用法。通过epoll来检测多个文件,当其中有文件可读时就把文件的信息读取并打印出来,具体实现如下:
#include <stdio.h> #include <sys/epoll.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define BUFFER_SIZE 256 // 定义缓冲区的大小 /* 定义epoll最大监听的文件数量 */ #define EPOLL_MAX_EVENTS 32 /* 定义一个epoll事件的数组,用来存放监听文件的信息 */ static struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS]; /* * Usage : epoll <file1> [file2] [file3] ... */ int main(int argc, char *argv[]) { int epoll_fd; int tmp_fd; int i; int result; char readbuf[BUFFER_SIZE]; int readlen; struct epoll_event eventItem; if(argc < 2) { printf("Usage : %s <file1> [file2] [file3] ...\n", argv[0]); return -1; } /* 打开一个epoll文件的文件描述符 */ epoll_fd = epoll_create(1); // 传入的参数可以随意,只要大于0即可,没有什么具体意思 if(-1 == epoll_fd) { printf("epoll_create error!\n"); return -1; } /* 将传入的文件名作为参数加入到epoll的监听事件当中 */ for(i = 1; i < argc; i++) { tmp_fd = open(argv[i], O_RDWR); // 以读写方式打开 memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; // epoll检测相应的文件的读操作将被唤醒 eventItem.data.fd = tmp_fd; result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, tmp_fd, &eventItem); if(-1 == result) { printf("epoll_ctl error!\n"); return -1; } } while(1) { /* 调用epoll_wait来监听事件 */ result = epoll_wait(epoll_fd, mPendingEventItems, EPOLL_MAX_EVENTS, -1); if(-1 == result) // 发生错误 { printf("epoll wait error!\n"); return -1; } else { for(i = 0; i < result; i++) // 对监听到的事件进行遍历,把读到的信息打印出来 { readlen = read(mPendingEventItems[i].data.fd, readbuf, BUFFER_SIZE); readbuf[readlen] = '\0'; printf("read data : %s\n", readbuf); } } } return 0; } 编译并运行结果如下: