I/O多路复用之-epoll

来源:互联网 发布:老外中国快递知乎 编辑:程序博客网 时间:2024/05/22 08:01

I/O多路复用之-epoll

之前的select和poll实现I/O多路复用都用一个很大的弊端,那就是在轮询的时候需要轮询对象集合中的所用文件描述符,如果对象集合中有用的文件描述符很少,那就会做很多无用的轮询,相对浪费资源。epoll函数就可以解决这个问题。

epoll与select、poll不同,首先,不用每次调用都向内核拷贝事件描述信息,在第一次调用后,事件信息就会与对应的epoll描述符关联起来。另外epoll不是通过轮询,而是通过在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。epoll返回后,该参数指向的缓冲区中即为发生的事件,对缓冲区中每个元素进行处理即可,而不需要像poll、select那样进行轮询检查。

   epoll通过epoll_create创建一个用于epoll轮询的描述符,通过epoll_ctl添加/修改/删除事件,epoll_wait检查事件,epoll_wait的第二个参数用于存放结果。

1、创建文件描述符

intepoll_create(int size);/*早期的size参数为创建的描述符允许的最大值,2001版本后的取消了该限制,为了接口兼容,仍然设置该参数,但该参数实为无用参数。*/

 

2、管理对象及事件

typedefunion epoll_data {

   void       *ptr;

   int         fd;

   uint32_t    u32;

   uint64_t    u64;

}epoll_data_t;

 

structepoll_event {

   uint32_t    events;      /* 监测事件*/

   epoll_data_t data;        /* 附加数据,常用于存储文件描述符*/

};

EPOLLIN- 读事件

EPOLLOUT-写事件

 

3、通知内核

    intepoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

intepfd :epoll文件描述符

   int op :命令码

      EPOLL_CTL_ADD:向epoll添加对象及事件

      EPOLL_CTL_MOD:修改epoll已有对象及事件

      EPOLL_CTL_DEL:删除epoll已有对象及事件

   int fd  :监测对象(等谁)

   struct epoll_event *event

 

4、等事件发生并读结果

 int epoll_wait(int epfd, struct epoll_event*events,

                      int maxevents, inttimeout);

功能:等事件发生并读结果

参数:

   int epfd - epoll文件描述符

   struct epoll_event *event

      - 保存事件发生事件的缓存首地址

   int maxevents - 想保存的数量

 

   int timeout - 超时值(单位:毫秒)

         时间值0 :非阻塞

         合理的时间值 : 时间到时不满足即超时

         -1 : 阻塞(-1的补码即是最大的整数,等待时间最长即为阻塞)

 

返回值:

失败:-1

   超时:0

   成功:实际发生的数量

 

5、轮询

for(inti = 0; i < num; i++){     /*num为epoll_wait()的返回值*/

   if(EPOLLIN & events[i].events){

      读

      read();

   }

   if(EPOLLOUT & events[i].events){

      写

      write();

   }

}

 

利用epoll()构建一个模拟并发服务器(实现多连接):

#include <stdio.h>#include <string.h>#include <sys/types.h>          #include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <sys/epoll.h>#define EMAX 1024#define MAX 1024int init_server(const char *ipstr, unsigned short port, int backlog){int s = socket(AF_INET, SOCK_STREAM, 0);if(0 > s){perror("socket");return -1;}struct sockaddr_in addr = {.sin_family = AF_INET,.sin_port   = htons(port),.sin_addr = {.s_addr = (NULL == ipstr ? INADDR_ANY : inet_addr(ipstr)),},};memset(addr.sin_zero, 0, sizeof(addr.sin_zero));socklen_t len = sizeof(addr);if(0 > bind(s, (struct sockaddr *)&addr, len)){perror("bind");return -1;}if(0 > listen(s, backlog)){perror("listen");return -1;}return s;}int add_event(int efd, int fd, int event){struct epoll_event evt = {.events = event,.data = {.fd = fd,},};return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &evt);}int del_event(int efd, int fd){return epoll_ctl(efd, EPOLL_CTL_DEL, fd, NULL);}int main(){int s = init_server(NULL, 9999, 1);if(0 > s){return -1;}printf("Wait for new comming ...\n");int efd = epoll_create(1);if(0 > efd){perror("epoll_create");return -1;}if(0 > add_event(efd, s, EPOLLIN)){perror("add_event");return -1;}struct epoll_event evts[EMAX];while(1){memset(evts, 0, sizeof(evts));int ret = epoll_wait(efd, evts, EMAX, 1000);if(0 > ret){perror("select");break;}else if(0 == ret){printf("\tTimeout.\n");continue;}for(int i = 0; i < ret; i++){if(s == evts[i].data.fd){struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));socklen_t len = sizeof(addr);int rws = accept(s, (struct sockaddr*)&addr, &len);if(0 > rws){perror("accept");return -1;}printf("a nen comming [%s:%u] \n", \ inet_ntoa(addr.sin_addr), \ ntohs(addr.sin_port));if(0 > add_event(efd, rws, EPOLLIN)){perror("add_event");close(rws);}}else{char buf[MAX];memset(buf, 0, MAX);int len = read(evts[i].data.fd, buf, MAX-1);if(0 >= len){printf("read [%d] fail .\n", evts[i].data.fd);if(0 > del_event(efd, evts[i].data.fd)){perror("del_event");}close(evts[i].data.fd);}else{printf("sock[%d], RECV[%dbytes]:%s\n", evts[i].data.fd, len, buf);}}}}close(s);}


0 0
原创粉丝点击