LT模式&ET模式

本文详细解析了Linux系统中Epoll的LT(Listen Trigger)与ET(Edge Trigger)两种工作模式的差异,重点阐述了在ET模式下如何通过循环读取确保事件仅被触发一次的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LT:在数据到达之后,无论程序是没有接收,还是接收了,但没有接收完,下一轮epoll_wait仍然会提醒应用程序该描述符上有数据,知道数据被接收完。

ET:在数据到达之后,无论程序是没有接收,还是接收了,但是没有接收完,都只提醒一次,下一轮不再提醒应用程序该描述符上有数据。

同一事件仅仅被触发一次

ET的实现:

#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<stdio.h>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<errno.h>
#include<fcntl.h>
#define SIZE 100
void SetNonBlock(int fd)//将文件描述符设置成非阻塞的
{
	int old=fcntl(fd,F_GETFL);
	int new=old | O_NONBLOCK;
	fcntl(fd,F_SETFL,new);//将new设置到fd上
}
int Create_Socket()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd!=-1);
	struct sockaddr_in ser;
	memset(&ser,0,sizeof(ser));
	ser.sin_family=AF_INET;
	ser.sin_port=htons(6000);
	ser.sin_addr.s_addr=inet_addr("127.0.0.1");
	int res=bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
	assert(res!=-1);
	listen(sockfd,5);	
	return sockfd;
}
void et(int fd,struct epoll_event event,int epollfd)
{
	if(event.events&EPOLLRDHUP)
	{
		close(fd);
		epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,NULL);
		printf("%d client break link;\n",fd);
	}
	else if(event.events&EPOLLIN)
	{
		printf("%d S data is:\n", fd);
		while(1)
		{
			char buff[128]={0};
			int n=recv(fd,buff,5,0);
			if(n<=0)
			{
				if(errno==EAGAIN || errno==EWOULDBLOCK)
				{
					break;
				}
				close(fd);
				epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,NULL);
			}
			printf("%s",buff);
		}
		printf("\n");
		send(fd,"OK",2,0);
	}
	else
	{
	printf("error\n");
	}
}
void Deal_Link(struct epoll_event events[],int n,int sockfd,int epollfd)
{
	int i=0;
	for(;i<n;++i)
	{
		int fd=events[i].data.fd;
		if(fd==sockfd)
		{	
			struct 	sockaddr_in cli;
			int len=sizeof(cli);
			int c=accept(fd,(struct sockaddr*)&cli,&len);
			if(c<0)
			{
				printf("One client link error\n");
				continue;
			}
			SetNonBlock(c);
			struct epoll_event event;
			event.events=EPOLLIN | EPOLLRDHUP | EPOLLET;
			event.data.fd=c;
			epoll_ctl(epollfd,EPOLL_CTL_ADD,c,&event);
		}		
		else
		{
			et(fd,events[i],epollfd);
		}
	}
}

int main()
{
	int sockfd=Create_Socket();
	int epollfd=epoll_create(5);
	assert(epollfd!=-1);

	struct epoll_event event;
	event.events=EPOLLIN;
	event.data.fd=sockfd;
	epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);

	while(1)
	{
		struct epoll_event events[SIZE];
		int n=epoll_wait(epollfd,events,SIZE,-1);	
		if(n<=0)
		{
			printf("epoll wait error\n");
			continue;			
		}	

		printf("epoll wait return\n");
		Deal_Link(events,n,sockfd,epollfd);
		
	}
}

注意:

  • 文件描述符必须设置为非阻塞模式
  • 内核事件表上的文件描述符必须关注EPOLLIN事件
  • 当事件发生时,必须以循环的方式处理时间,知道事件处理完成
### LT模式与ET模式的区别及应用场景 #### 区别 1. **触发机制** - 在水平触发(LT,Level-Triggered)模式下,当`epoll_wait`检测到文件描述符上有事件发生时,即使应用程序未立即处理该事件,只要事件仍然有效,`epoll_wait`会在后续调用中继续通知该事件[^3]。 - 在边缘触发(ET,Edge-Triggered)模式下,`epoll_wait`仅在事件状态从“无事件”变为“有事件”时通知一次。如果应用程序未能完全处理该事件(例如未读取完所有数据或未接受所有连接),则需要自行负责后续的处理[^1]。 2. **事件处理要求** - 在LT模式下,应用程序可以按需读取部分数据,无需一次性将所有数据读取完毕。这允许更灵活的事件处理逻辑[^2]。 - 在ET模式下,应用程序必须确保每次事件触发后都将所有数据读取完毕,或者立即处理完所有待处理的任务(如接受连接)。否则,可能会导致数据丢失或任务延迟[^2]。 3. **性能特性** - LT模式可能因多次重复通知同一事件而增加系统开销,但其逻辑简单,适合对性能要求不高的场景[^3]。 - ET模式通过减少事件通知次数来提高性能,但需要应用程序实现更复杂的事件处理逻辑以避免遗漏未处理的数据。 --- #### 应用场景 1. **LT模式的应用场景** - 适用于对性能要求不高、但希望简化事件处理逻辑的场景。例如,简单的网络服务器或调试工具[^3]。 - 当需要频繁读取小块数据时,LT模式可以避免因未读取完所有数据而导致的复杂处理逻辑[^2]。 2. **ET模式的应用场景** - 适用于高并发、高性能的场景,如大型Web服务器、实时通信系统等。在这种场景下,减少事件通知次数可以显著降低CPU和内存开销。 - 当应用程序能够高效地批量处理事件(如一次性读取大量数据或接受多个连接)时,ET模式的优势更加明显[^2]。 --- ### 示例代码 以下是一个简单的ET模式示例,展示如何在`epoll`中处理读事件: ```python import select import socket # 创建监听socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('0.0.0.0', 8080)) server_socket.listen(100) server_socket.setblocking(False) # 初始化epoll epoll = select.epoll() epoll.register(server_socket.fileno(), select.EPOLLIN | select.EPOLLET) connections = {} try: while True: events = epoll.poll(1) for fileno, event in events: if fileno == server_socket.fileno(): # 接受新连接 client_socket, address = server_socket.accept() client_socket.setblocking(False) epoll.register(client_socket.fileno(), select.EPOLLIN | select.EPOLLET) connections[client_socket.fileno()] = client_socket elif event & select.EPOLLIN: # 处理读事件 client_socket = connections[fileno] data = b'' while True: try: chunk = client_socket.recv(4096) if not chunk: break data += chunk except BlockingIOError: break if data: print(f"Received: {data.decode()}") finally: epoll.unregister(server_socket.fileno()) epoll.close() server_socket.close() ``` --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值