7. epoll edge trigger and reactor

epoll edge trigger

1. epoll event model:

? epoll monitors file descriptors and can also monitor inter-process communication events.

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>

#defineMAXLINE 10

int main(int argc, char *argv[])
{<!-- -->
    int efd, i;
    int pfd[2];
    pid_t pid;
    char buf[MAXLINE], ch = 'a';

    pipe(pfd);//Create a pipe
    pid = fork();//Create a main process

    if (pid == 0)
    //pid = 0 indicates that it is a child process, and the child process completes the write operation
    {<!-- -->
        close(pfd[0]);//Close the child process reading end
        while (1)
        {<!-- -->
            //aaaa\

            for (i = 0; i < MAXLINE/2; i + + )
                buf[i] = ch;
            buf[i-1] = '\
';
            ch + + ;
            //bbbb\

            for (; i < MAXLINE; i + + )
                buf[i] = ch;
            buf[i-1] = '\
';
            ch + + ;
            //aaaa\
bbbb\

            write(pfd[1], buf, sizeof(buf));
            //Write buf into the pipe
            sleep(5);
        }
        close(pfd[1]);
    }
    else if (pid > 0)
    {<!-- -->
        struct epoll_event event;
        struct epoll_event resevent[10]; //epoll_wait is ready to return event
        int res, len;

        close(pfd[1]);
        efd = epoll_create(10);//Create a file descriptor to monitor cfd

        event.events = EPOLLIN | EPOLLET; // ET edge trigger
       // event.events = EPOLLIN; // LT level trigger (default)
        event.data.fd = pfd[0];
        epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], & amp;event);
        /*
        event.events = EPOLLIN | EPOLLET;: This line of code sets the events field of the event structure, indicating that the associated file descriptor (pfd[0]) should monitor readable events (Edge Triggered, ET) mode ( EPOLLIN). In ET mode, the event is only triggered when the status on the file descriptor changes from unreadable to readable, so you need to ensure that after processing the readable event, all available data is read until EAGAIN or EWOULDBLOCK is returned again.
event.data.fd = pfd[0];: This line of code sets the data.fd field of the event structure and associates the file descriptor pfd[0] with this event.
epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], & event);: This line of code adds the event defined in the event structure to the epoll instance (represented by efd). This will tell epoll to start monitoring readable events on pfd[0] and notify the application when that event occurs.
        */
        //LT is a horizontal trigger, which will trigger as long as there is something in the buffer.
        //ET is edge triggered and will only be triggered when the content in the buffer increases.
        while (1)
        {<!-- -->
            res = epoll_wait(efd, resevent, 10, -1);
            //epoll_wait function to wait for the event to occur. efd is the file descriptor of the epoll instance, resevent is an array used to store //storage ready events, 10 represents the size of the resevent array, -1 represents infinite waiting time
            printf("res %d\
", res);
            if (resevent[0].data.fd == pfd[0])
          //Check whether the first event in the ready event array is related to file descriptor pfd[0]. The data.fd field stores the file descriptor // related to the event.
            {<!-- -->
                len = read(pfd[0], buf, MAXLINE/2);
                write(STDOUT_FILENO, buf, len);
            }
        }

        close(pfd[0]);
        close(efd);

    } else {<!-- -->
        perror("fork");
        exit(-1);
    }

    return 0;
}

ET mode: edge triggered

? The remaining unread data in the buffer will not cause epoll_wait to return. New events will be triggered only when they are met.

LT mode: horizontal trigger (default)

? Unread data in the buffer will cause epoll_wait to return

2. ET non-blocking mode in epoll

readn: Read a certain number of bytes before returning

**Only three lines of code are required:** File descriptor settings are non-blocking

flag = fcntl(cfd,F_GETFL);
flag |= 0_NOBLOCK;
fcntl(cfd,F_SETFL,flag);

Disadvantages: Not cross-platform, can only be used on Linux

epoll reactor model

1. Overview:

? If there are n clients, the server will have n connections (n clients and connections).











































client









Connector









epoll detects whether the event is readable or writable









server







? n: There are readable events (including lfd) and writable events.

/*An event can be seen as three members of the following structure*/
struct event
{<!-- -->
    int cfd;
    read_cb();
    write_cb();
}

? Reactor: One IO corresponds to multiple events.

2. Code implementation (implementation of detection business)

lfd uses accept processing
cfd uses recv and send processing
#define EVENT_LENGTH 1024
unsigned char buf[];
int init_server
{<!-- -->

}
struct item
{<!-- -->
int cfd;
unsigned char rbuffer[1024];//read buffer
int rlen;
unsigned char wbuffer[4096];
int wlen;//The length to be written
int wsize;//The length that has been written
};
struct item *get_item_by_clientfd(int clientfd)
{<!-- -->
//Find the item array through cfd
};


int main()
{<!-- -->
int epfd = epoll_create(1);
int sockfd = init_server();
//At the beginning, the red-black tree only had one lfd
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd, & amp;ev);
struct epoll_event events[EVENT_LENGTH] = {<!-- -->0};
while(1)
{<!-- -->
int nready = epoll_wait(epfd,events,EVENT_LENGTH,-1);
int i = 0;
for(i = 0; i < nready; i + + )
{<!-- -->
//The first layer of if is used to determine whether it is lfd or cfd
//The second level if is used to determine whether to read cfd or write cfd
if(events[i].data.fd == sockfd)
{<!-- -->
struct sockaddr_in client;
socklen_t len = sizeof(client);
int cfd = accept(sockfd,(struct sockaddr*) & amp;client,len);
//Let a client connect to a server
struct epoll_event ev = {<!-- -->0};
ev.event = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd, & amp;ev);
}
else
{<!-- -->
struct item *it = get_item_by_clientfd(events[i].data.fd);
if(event[i].event & amp; EPOLLIN)
{<!-- -->
//read client
\t\t\t\t\t
it->len = recv(events[i].data.fd,buffer,1024,0);//Only read half of it, not all
}
if(event[i].event & amp; EPOLLOUT)
{<!-- -->
//write client
send(events[i].data.fd,it->wbuffer + it->wsize,it->wlen-it.size,0);
//When the writing is not completed at one time, it should be placed in the write buffer.
//If the reading is not completed at one time, it should be placed in the reading buffer.
//The buffer here refers to the kernel buffer
it->wsize + = ret;
\t\t\t\t\t
}
}
}
}
}

So for the above code, where is the buffer processing business?

For data, there are three levels of operations:

































Detect IO events









read/write









parse







? Why can’t the parsing code be placed directly behind recv and send? Because if you write it like this, the reusability of the code will be poor. So use callback function.

? The reactor is mainly used to detect whether fd is read or written.

Mistake: reactor is epoll plus a callback function: reactor is used to describe whether an event is triggered

Which of the two packaging methods for the following structure is better:

struct reactor
{<!-- -->
int epfd;
struct item *items;
int count;
int ucount;
};

Packaging method one:

struct reactor *Init_reactor(int size)
{<!-- -->
struct reactor *t = malloc(sizeof(struct reactor));
\t
}
struct reactor *del_reactor(struct reactor *r)
{<!-- -->
free(r);
}

Packaging method two:

struct reactor *Init_reactor(struct reactor *r)
{<!-- -->
r->epfd = epoll_create(1);
r->items = malloc(1024*sizeof(struct reactor *r));
r->count = 1024;
r->ucount = 0;
}
struct reactor *Init_reactor(struct reactor *r)
{<!-- -->
close(r->epfd);
}

Encapsulation method 2 is better because it avoids return values

The following functions need to be encapsulated

int accept_callback(int fd, int events, void *arg)
{<!-- -->
}
int recv_callback(int fd, int events, void *arg)
{<!-- -->
}
int send_callback(int fd, int events, void *arg)
{<!-- -->
}
int set_events(int fd, int events, void *arg)
{<!-- -->
}