使用epoll()进行多路传输
Linux2.6之后引进的epoll解决了poll和select共有的性能瓶颈。
使用epoll的时候,应用程序通过系统调用给内核提供了一个需要监控的文件描述符列表,然后使用另外一个不同的系统调用来监控这些文件描述符。一旦这个文件列表被创建,内核根据应用程序所关心的事件而不断地监控哪些文件描述符,内核实际上每个文件上都设置了毁掉函数,当事件发生的时候,回调函数就会被调用。之后内核就会发出通知,告诉应用程序需要处理的事情发生了。
应用程序询问内核哪个文件描述准备好了可以进行下一步处理,内核只需要维护提供的这个列表即可,不用检查每个文件描述符。
poll()使用pollfd结构组成的数组来代表文件描述符的集合。
select()使用了三个不同的fd_set结构。
epoll把这些集合移到内核, 而不是在程序的空间中保存他们, 每一个文件集合都可以同epoll描述符来引用,epoll文件描述符是epoll系统调用使用的文件描述符。
epoll_create:
#include <sys/epoll.h>
int epoll_create(int size);
epoll_create() creates an epoll(7) instance. Since Linux 2.6.8, the size argument is ignored, but must be greater than zero; see NOTES below.
epoll_create() returns a file descriptor referring to the new epoll instance. This file descriptor is used for all the subsequent calls to the epoll interface. When no longer required, the file descriptor returned by epoll_create() should be closed by using close(2). When all file descriptors referring to an epoll instance have been closed, the kernel destroys the instance and releases
the associated resources for reuse.
epoll文件描述符就被这两个系统使用:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
strcut epoll_event类型:
The event argument describes the object linked to the file descriptor fd. The struct epoll_event is defined as :
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 */
};
events描述监听哪种类型的事件,描述哪种类型的事件发生了。
继续用epoll实现上述程序:
/* mpx-epoll.c - Displays input from two pipes using \codefn{epoll} */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/poll.h>
void addEvent(int epfd, char * filename) {
int fd;
struct epoll_event event;
if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) {
perror("open");
exit(1);
}
event.events = EPOLLIN;
event.data.fd = fd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event)) {
perror("epoll_ctl(ADD)");
exit(1);
}
}
int main(void) {
char buf[4096];
int i, rc;
int epfd;
struct epoll_event events[2];
int num;
int numFds;
epfd = epoll_create(2);
if (epfd < 0) {
perror("epoll_create");
return 1;
}
/* open both pipes and add them to the epoll set */
addEvent(epfd, "p1");
addEvent(epfd, "p2");
/* continue while we have one or more file descriptors to watch */
numFds = 2;
while (numFds) {
if ((num = epoll_wait(epfd, events,
sizeof(events) / sizeof(*events), -1)) <= 0) {
perror("epoll_wait");
return 1;
}
for (i = 0; i < num; i++) {
/* events[i].data.fd is ready for reading */
rc = read(events[i].data.fd, buf, sizeof(buf) - 1);
if (rc < 0) {
perror("read");
return 1;
} else if (!rc) {
/* this pipe has been closed, don't try to read from it again */
if (epoll_ctl(epfd, EPOLL_CTL_DEL,
events[i].data.fd, &events[i])) {
perror("epoll_ctl(DEL)");
return 1;
}
close(events[i].data.fd);
numFds--;
} else {
buf[rc] = '\0';
printf("read: %s", buf);
}
}
}
close(epfd);
return 0;
}