在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;
}
编译并运行结果如下: