The difference between LT and ET in Linux_linux

       select, poll and epoll are all system call functions in linux. In comparison, the efficiency of epoll is relatively high, but it cannot be generalized. As a Linux-specific I/O multiplexing function, epoll is very different from select and poll in implementation. epoll completes tasks through a set of functions instead of a single function. In addition, epoll also has a way of processing file descriptors. The difference, there are the following two ways: LT mode (horizontal trigger) and ET mode (edge ​​trigger). The following will analyze the difference between the two.


【Simple understanding】

       LT and ET modes are the two ways epoll operates on file descriptors. LT mode is the default working mode, which is equivalent to a more efficient poll; ET mode is an efficient mode of epoll.


[Concrete realization of LT mode]

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define BUFFSIZE 10
#define MAXEVENTNUM 1024

//Register the read event in fd to the epoll kernel event table
void addfd(int epollfd, int fd)
{
	epoll_event event;
	event.events = EPOLLIN;
	event.data.fd = fd;
	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) == -1)
	{
		perror("epoll_ctl_add error!\n");
	}
}
void delfd(int epollfd, int fd)
{
    if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL) == -1)
    {
        perror("epoll_ctl_del error!\n");
    }
}

//LT mode
void lt(epoll_event* events, int num, int epollfd, int listenfd)
{
	char buff[BUFFSIZE];
	int i = 0;
	for (;i < num;++i)
	{
		//Easy to read and write
		int sockfd = events[i].data.fd;
		
		//Indicate that sockfd is a listening socket
		if (sockfd == listenfd)
		{
			struct sockaddr_in cli;
			unsigned int len = sizeof(cli);
			int connfd = accept(listenfd, (struct sockaddr* )&cli, &len);
			if (connfd < 0)
			{
				continue;
			}
			addfd(epollfd, connfd);
		}
		else if(events[i].events & EPOLLIN)
		{
			//As long as there is unread data in the socket read cache, this code is triggered
			printf("event trigger once\n");
			memset(buff, 0, BUFFSIZE);
			/*
			*  recv(int sockfd, void *buff, size_t len, int flags)
			* Return value: Return the length of the data actually read, which may be less than the actual length len;
			* Returns 0, which means that the other party closes the link; returns -1, an error occurs.
			*/
			
			int ret = recv(sockfd, buff, BUFFSIZE, 0);
			if (ret <= 0)
			{
				delfd(epollfd, sockfd);
				close(sockfd);
				continue;
			}
			printf("get %d bytes of content: %s\n",ret,buff);
			send(listenfd, "OK", 3, 0);
		}
		else
		{
			printf("something else happened!\n");
		}
	}
}

intmain()
{
	struct sockaddr_in address;
	address.sin_family = AF_INET;
	address.sin_port = htons(8000);
	address.sin_addr.s_addr = inet_htons("127.0.0.1");
	
	//create socket
	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);
	//name the socket
	ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
	assert (ret! = -1);
	//listen socket
	ret = listen(listenfd, 5);
	assert (ret! = -1);
	
	//create kernel event table
	epoll_event events[MAXEVENTNUM];
	int epollfd = epoll_create(5);
	assert(epollfd != -1);
	//Need to add multiple times, so encapsulate
	//Add descriptors and events to the kernel event table
	addfd(epollfd, listenfd);
	
	while (1)
	{
		//-1 means block forever until an event is ready
		int ret = epoll_wait(epollfd, events, MAXEVENTNUM, -1);
		if (ret < 0)
		{
			perror("epoll failure!\n");
			break;
		}
		lt(events, ret, epollfd, listenfd);
	}
		
	close(listenfd);
	return 0;
}


[The specific implementation of ET mode]

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define BUFFSIZE 10
#define MAXEVENTNUM 1024

//Set the file descriptor to non-blocking
int setnonblocking(int fd)
{
	int old_option = fcntl(fd, F_GETFL);
	int new_option = old_option | O_NONBLOCK;
	fcntl(fd, F_SETFL, new_option);
	return old_option;
}

//Register the read event in fd to the epoll kernel event table
void addfd(int epollfd, int fd)
{
	epoll_event event;
	event.events = EPOLLIN;
	event.data.fd = fd;
	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) == -1)
	{
		perror("epoll_ctl_add error!\n");
	}
	setnonblocking(fd);
}

// delete the event in the kernel event table
void delfd(int epollfd, int fd)
{
    if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL) == -1)
    {
        perror("epoll_ctl_del error!\n");
    }
}

//ET mode
void et(epoll_event* events, int num, int epollfd, int listenfd)
{
	char buff[BUFFSIZE];
	int i = 0;
	for (;i < num;++i)
	{
		int sockfd = events[i].data.fd;
		if (sockfd == listenfd)
		{
			struct sockaddr_in cli;
			unsigned int len = sizeof(cli);
			int connfd = accept(listenfd, (struct sockaddr*)&cli, &len);
			addfd(epollfd, connfd, 1);
		}
		else if(events[i].events & EPOLLIN)
		{
			printf("event trigger once!\n");
			//=========================================================================//
			//epoll_wait() In et mode, the application only wants to notify the event once (even if the data is not read), that is, call //
			//Use epoll_wait() once, so if you want to read the data in the buffer, you need to use the inner loop to read//
			//=========================================================================//
			//lt mode is always through the epoll_wait() call to remind the application to process the event (that is, in the buffer//
			//Unfinished data //
			//=========================================================================//
			while (1)
			{
				memset(buff, 0, BUFFSIZE);
				//No data, and the descriptor is non-blocking, the return value ret == -1
				int ret = recv(sockfd, buff, BUFFSIZE, 0);
				if (ret < 0)
				{
					/*
					* For non-blocking I/O, the following conditions are true and the data has been read.
					* After that, epoll can trigger the EPOLLIN event on sockfd again to drive the next
					* One read operation
					*/
					if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
					{
						printf("read later\n");
						break;
					}
					send(sockfd, "OK", 3, 0);
					break;
				}
				else if (ret == 0)
				{
				    delfd(epollfd, sockfd);
					close(sockfd);
					break;
				}
				else
				{
					printf("get %d bytes of content: %s\n",ret,buff);
				}
			}
		}
		else
		{
			printf("something else happened!\n");
		}
	}
}

intmain()
{
	struct sockaddr_in address;
	address.sin_family = AF_INET;
	address.sin_port = htons(8000);
	address.sin_addr.s_addr = inet_htons("127.0.0.1");
	
	//create socket
	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);
	//name the socket
	ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
	assert (ret! = -1);
	//listen socket
	ret = listen(listenfd, 5);
	assert (ret! = -1);
	
	//create kernel event table
	epoll_event events[MAXEVENTNUM];
	int epollfd = epoll_create(5);
	assert(epollfd != -1);
	//Need to add multiple times, so encapsulate
	//Add descriptors and events to the kernel event table
	addfd(epollfd, listenfd);
	
	while (1)
	{
		//-1 means block forever until an event is ready
		int ret = epoll_wait(epollfd, events, MAXEVENTNUM, -1);
		if (ret < 0)
		{
			perror("epoll failure!\n");
			break;
		}
		lt(events, ret, epollfd, listenfd);
	}
		
	close(listenfd);
	return 0;
}

【The difference between the two】

    Regarding the difference between LT and ET mode, the textbook reads: "For a file descriptor that adopts the LT working mode, when epoll_wait detects that an event occurs on it and notifies the application of this event, the application can not immediately Handle the event. When the application calls epoll_wait next time, epoll_wait will also notify the application of this event until the event is processed. For the file descriptor that adopts the ET working mode, when epoll_wait detects that there is an event on it After notifying the application, the application must process the event immediately, because the subsequent epoll_wait will not notify the application of this event. It can be seen that epoll_wait greatly reduces the number of times the same epoll event is triggered, so it is more efficient .”

    But such expressions are somewhat obscure. However, we can see from the above code that when the program starts to execute and receives an event, it will enter the while(1) loop of the main function and call the corresponding lt function or et function, but we find that lt In mode, when the data in the buffer has not been read, the lt function will exit and continue to call epoll_wait(). If epoll_wait() finds that there is still data in it, it will continue to make round-trip calls until the data is read and written. But in et mode, when epoll_wait detects a ready event for the first time, it will call the et function; there is a while(1) loop inside the et function, until the data is read, return and epoll_wait, there is no data at this time, So in order not to block epoll_wait, you should set the file descriptor to be non-blocking, that is, call the setnonblocking() function.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325714310&siteId=291194637