Linux network programming multiplexing -- epoll

The previous blog talked about select and poll, but every time you call, you need to copy the fd collection from user space to the kernel. In order to check which FDS satisfy the event, you need to traverse all FDS, which is inefficient. In this way, we have epoll. Epoll only needs to pay attention to the fd we are concerned about.

epoll's system call has three functions:

1. Create epoll fd function

int epoll_create(int size);

Create a file descriptor of epoll and create the data structure of epoll in the kernel. It contains a red black tree (the data structure of the file descriptor to be monitored) and a ready two-way linked list. Size indicates the number of nodes in the red black tree of the kernel. Since Linux version 2.6.8, the size value is useless, but it should be greater than 0. Because the kernel can dynamically allocate the size, there is no need for the size prompt.

All events in epoll establish a callback relationship with the network card driver. When the corresponding event occurs, the event will be added to the ready list through this callback function

2. Register event function

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

Epoll < CTL function is used to operate the red black tree of epoll.
epfd is the file descriptor of epoll.
op is the operation epoll? CTL? Add, epoll? CTL? Del, epoll? CTL? Mod.
fd is the file descriptor to listen to.
Event is an event that tells the kernel to listen for.

           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 can be set to: EPOLLIN,EPOLLOUT,EPOLLERR, etc., indicating that the corresponding descriptor is readable, writable, and there is an error in the corresponding descriptor.

3. Listening event function

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

To check whether there is an event, you only need to check whether there is data in the ready bidirectional linked list in the kernel. If the linked list is not empty, the event will be copied to the user space (i.e. the events array) and the number of events will be returned.
epfd: epoll's file descriptor
Events: outgoing parameter. The kernel will copy the events to the events array
maxevents: tells the kernel the total number of elements in the events array
Timeout: timeout (milliseconds), - 1 is blocking, 0 is non blocking, and > 0 sets the timeout

The basic usage of epoll is illustrated in the following code:

  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 }

Where wrap.h This blog Illustrated in

Published 19 original articles, won praise 2, visited 636
Private letter follow

Tags: Linux network socket

Posted on Mon, 09 Mar 2020 21:08:28 -0700 by CONTEMAN