I/O非阻塞函数实践:epoll

来源:互联网 发布:东北大学软件学院好吗 编辑:程序博客网 时间:2024/06/05 19:12

epoll函数是poll函数的改良版,同样与poll函数一样,不需要每次对监听的句柄进行赋值,同时,比poll函数更为高效,处理大量的句柄数(1000以上)效率不会降低。epoll的核心是三个函数:

1、epoll_create函数 
函数声明:int epoll_create(int size) 
该函数生成一个epoll专用的文件描述符,size用来告诉内核这个监听的句柄数目一共有多大。同时当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

2、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

第一个参数epfd就是之前create的值。

第二个参数是所要进行的操作,有三个宏1、EPOLL_CTL_ADD:注册新的fd到epfd中;2、EPOLL_CTL_MOD:修改已经注册的fd的监听事件;3、EPOLL_CTL_DEL:从epfd中删除一个fd;

第三个参数是可能会发生事件的套接词。

第四个参数是套接词所关心的发生事件。

这个函数按照我理解就是,epoll能够同时监听一个固定size的任务列表。我有一个套接词的某个事件(写、读、报错)需要监听,我就用epoll_ctl函数把这个事件挂上去,epoll可以同时帮你看着这些任务,返回发生事件的套接词。这里需要借助于struct epoll_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 */
};

epoll_event.data.fd储存需要监听的fd,epoll_event.events储存所关心的事件。这个结构体只是个暂时的载体,没有其他意义。然后通过epoll_ctl将epoll_event的内容服上去即可。

3、int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

这个就是epoll的监听函数了,但是epoll很牛逼,不会像select一样,需要你自己去轮训一遍,它的返回值就是必定有所关心事件发生的套接词数量,并且他会将发生事件的套接字和所发生的事情储存到epoll_event结构体里面,果然,这个epoll_event这个东西是来储存各种epoll内可用信息的。

第三个参数maxevents是用来储存所能够监听的最大的事件发生数,之前我一直搞不懂epoll_create中的size和这个值干嘛要并存,参考了这片文章http://hi.baidu.com/yenoeepfqabiyzq/item/ef81f4b18f7258d685dd7969。其实maxevents也就是epoll内的txlist的buff_size嘛。

4、epoll有LT和ET两种模式,这个网上有许多文章提到,貌似很重要,我这里也提一下。

LT模式:跟poll机制类似,epoll_wait中返回什么,应用程序就处理什么,epoll不断得监听这个套接词,给应用程序发送通知,然后应用程序去处理就好了。

ET模式:是更为高效的模式,只支持no-block socket。一旦某个监听的套接字有所关心的事件发生,就会通知应用程序,并且他会假定程序已经做好了要对事件的准备。通知一次之后,epoll就好把这个套接词从epfd中除去,这样可以减少监听套接词的数量,提高效率。并且不再发送任何通知给程序,这也是不能使用block socket的原因,如果socket是block的,那么程序就可能会一直阻塞着。如果这个套接词的状态改变(比如read,write的各种类型出错),那么epoll就认为这个文件描述符又空闲了,于是自动将起添加进epfd。所以应当注意有许多时候需要手动添加。


先放上一个只能处理监听套接词读事件的epoll服务器,大致知道epoll可以同时处理很多句柄,嗯。


#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/epoll.h>#define MAX_CONN 100#define MAX_NU10000void setnoneblocking(int fd);int main(int argc, char const *argv[]){int count = 0;int fd;int nready;struct sockaddr_in servaddr;int epfd;//int timeout = 10000;fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0){perror("[Error]Create socket");exit(1);}printf("Socket success!\n");setnoneblocking(fd);memset(&servaddr, '\0', sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(12345);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){perror("[Error]Bind socket");exit(1);}printf("Bind success!\n");if (listen(fd, MAX_CONN) < 0){perror("[Error]Listen socket");exit(1);}printf("Listen success!\n");//创建epoll句柄epfd = epoll_create(1000);//创建一个epoll事件,任务是监听fd是否有读事件发生struct epoll_event event;event.data.fd = fd;event.events = EPOLLIN;//将fd添加到epoll的监听队列中,相当于本来之前有1000个句柄,现在挂上了一个。epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);struct epoll_event events[100];while(1){nready = epoll_wait(epfd, events, 100, 0);int i;for (i = 0; i < nready; ++i){if (events[i].data.fd == fd){struct sockaddr_in cltaddr; int accept_fd; socklen_t length = sizeof(cltaddr); if((accept_fd = accept(fd, (struct sockaddr*)&cltaddr, &length)) < 0){ perror("[Error]Accept socket"); exit(1); } setnoneblocking(accept_fd); //event相当于只是个暂时储存事件的变量,重要的是通过ctl将他加入监听事件 event.data.fd = accept_fd;event.events = EPOLLIN;epoll_ctl(epfd, EPOLL_CTL_ADD, accept_fd, &event);count++;printf("count=%d\n", count);}//end if/*else if (events[i].events&EPOLLIN){//读入并处理数据}*/}//end for}//end whilereturn 0;}//end mainvoid setnoneblocking(int fd){int yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {        perror("[Error]setsockopt");        exit(1);    }}





0 0
原创粉丝点击