select和epoll都用于监听套接口描述字上是否有事件发生,实现I/O复用
select(轮询)
#include <sys/select.h> #include <sys/time.h> int select (int maxfdpl, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout)调用时轮询一次所有描述字,超时时再轮询一次。如果没有描述字准备好,则返回0;中途错误返回-1;有描述字准备好,则将其对应位置为1,其他描述字置为0, 返回准备好的描述字个数。
fd_set:整数数组,每个数中的每一位对应一个描述字,其具体大小有内核的FD_SETSIZE(1024)决定。
void FD_ZERO(fd_set* fdset);//clear all bits in fdset void FD_SET(int fd, fd_set* fdset);//turn on the bit for fd in fdset void FD_CLR(int fd, fd_set* fdset);//turn off the bit for fd in fdset int FD_ISSET(int fd, fd_set* fdset);//is the bit for fd on in fdset
epoll对监听的每个fd都会有回调函数,当该fd上发生事件时,会调用对应的回调函数。
系列函数:
创建函数:
创建一个epoll句柄,size-监听套接字的数。当创建好epoll句柄后,会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
#include <sys/epoll.h> int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);第一个参数是epoll_create()的返回值;
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可以是以下几个宏的集合:
使用如下:
struct epoll_event ev; ev.data.fd=listenfd; ev.events=EPOLLIN|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);等待事件的产生,参数events用来从内核得到事件集合。maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
实例
struct epoll_event ev, *events; for(;;) { nfds = epoll_wait(kdpfd, events, maxevents, -1); for(n = 0; n < nfds; ++n) { if(events[n].data.fd == listener) { client = accept(listener, (struct sockaddr *) &local, &addrlen); if(client < 0){ perror("accept"); continue; } setnonblocking(client); ev.events = EPOLLIN | EPOLLET; ev.data.fd = client; if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) { fprintf(stderr, "epoll set insertion error: fd=%d\n", client); return -1; } } else { do_use_fd(events[n].data.fd); } } }