Linux网络编程多路IO复用----epoll

前面博客讲了select和poll,但是每次调用时都要将fd集合从用户空间拷贝到内核,并且为了检查哪些fd满足事件,需要遍历所有的fd,其效率是低下的。这样就有了epoll,epoll只需要注意我们所关心的那些fd。

epoll的系统调用有三个函数:

1.创建epoll fd函数

int epoll_create(int size);

创建一个epoll的文件描述符并在内核创建epoll的数据结构,其包含一颗红黑树(需要监听的文件描述符的数据结构)和一个就绪双向链表,size提示内核红黑树的节点个数。自从Linux2.6.8版本以后,size值其实是没什么用的,不过要大于0,因为内核可以动态的分配大小,所以不需要size这个提示了。

epoll中的所有事件,都与网卡驱动程序建立回调关系,当相应的事件发生的时候,会通过这个回调函数,将发生的事件添加到就绪链表当中

2.注册事件函数

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

epoll_ctl函数用于对epoll的红黑树进行操作。
epfd为epoll的文件描述符。
op为所作的操作EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD分别是添加,删除,修改
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 */
           };

events可以设置为:EPOLLIN,EPOLLOUT,EPOLLERR等表示对应描述符可读,对应描述符可写,对应描述符发生错误。

3.监听事件函数

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

检查是否有事件发生,只需要检测内核中的就绪双向链表是否有数据,若链表不为空,则将发生的事件复制到用户空间(即events数组),并返回事件的个数。
epfd:epoll的文件描述符
events:传出参数,内核会把发生的事件复制到events数组中
maxevents:告诉内核events数组的元素总个数
timeout: 超时时间(毫秒) ,-1为阻塞,0为非阻塞,>0 设置超时时间

下面通过代码说明epoll最基本的用法:

  1 #include<sys/epoll.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<ctype.h>
  5 #include<arpa/inet.h>
  6 #include<stdio.h>
  7 #include"wrap.h"
  8 #define SERVER_PORT 4000
  9 int main(void)
 10 {
 11     int lfd,cfd,root_fd,ret;
 12     struct sockaddr_in ser_addr,clt_addr;
 13     socklen_t clt_addr_len = sizeof(clt_addr);
 14     struct epoll_event tep,ep[1024];
 15     char buf[BUFSIZ];
 16 
 17     lfd = Socket(AF_INET,SOCK_STREAM,0);
 18 
 19     ser_addr.sin_family = AF_INET;
 20     ser_addr.sin_port = htons(SERVER_PORT);
 21     ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 22 
 23     Bind(lfd,(struct sockaddr* )&ser_addr,sizeof(ser_addr));
 24 
 25     Listen(lfd,128);
 26 
 27     root_fd = epoll_create(1024);
 28     tep.events = EPOLLIN;
 29     tep.data.fd = lfd;
 30     epoll_ctl(root_fd,EPOLL_CTL_ADD,lfd,&tep);
 31     while(1)
 32     {
 33         ret = epoll_wait(root_fd,ep,1024,-1);
 34         for(int i = 0;i < ret ;i++)
 35         {
 36             if(ep[i].data.fd == lfd)
 37             {
 38                 cfd = Accept(lfd,(struct sockaddr*)&clt_addr,&clt_addr_len);
 39                 printf("port = %d go in\n",ntohs(clt_addr.sin_port));
 40 
 41                 tep.events = EPOLLIN;
 42                 tep.data.fd = cfd;
 43                 epoll_ctl(root_fd,EPOLL_CTL_ADD,cfd,&tep);
 44             }
 45             else{
 46                 int n = read(ep[i].data.fd,buf,sizeof(buf));
 47                 if(n < 0)
 48                 {
 49                     if(errno == ECONNRESET)
 50                     {
 51                         Close(ep[i].data.fd);
 52                         epoll_ctl(root_fd,EPOLL_CTL_DEL,ep[i].data.fd,NULL);
 53                     }
 54                     else perr_exit("read error");
 55                 }
 56                 else if(n == 0)
 57                 {
 58                     Close(ep[i].data.fd);
 59                     epoll_ctl(root_fd,EPOLL_CTL_DEL,ep[i].data.fd,NULL);
 60                 }
 61                 else{
 62                     for(int i = 0; i < n;i++)
 63                         buf[i] = toupper(buf[i]);
 64                     Write(ep[i].data.fd,buf,n);
 65                 }
 66             }
 67         }
 68     }
 69     return 0;
 70 }

其中wrap.h在这篇博客中有说明

发布了31 篇原创文章 · 获赞 4 · 访问量 944

猜你喜欢

转载自blog.csdn.net/qq_39781096/article/details/104768682