在Linux中,epoll
是一种高效的I/O多路复用机制,支持两种工作模式:LT(Level Triggered,水平触发)和ET(Edge Triggered,边缘触发)。
概念和原理
LT 模式(Level Triggered)
LT 模式是 epoll
的默认模式。在 LT 模式下,当某个文件描述符就绪时,epoll_wait
函数会立即返回,通知应用程序有事件发生。即使应用程序没有立即处理完这些事件,下次调用 epoll_wait
时仍会再次返回这些就绪的文件描述符。
ET 模式(Edge Triggered)
ET 模式要求应用程序在处理文件描述符的就绪事件时,必须确保将其处理完毕,否则 epoll_wait
将不会重复通知该文件描述符的就绪状态。ET 模式通过设置 epoll_event
结构体中的 EPOLLET
标志来启用。
区别
-
通知机制:
- LT 模式:每当文件描述符就绪时,
epoll_wait
将通知应用程序,即使应用程序没有处理完该事件。 - ET 模式:只有在文件描述符状态发生变化时,
epoll_wait
才会通知应用程序。如果应用程序没有处理完事件,将不会重复通知该文件描述符的就绪状态。
- LT 模式:每当文件描述符就绪时,
-
效率:
- LT 模式:由于每次文件描述符就绪时都会通知应用程序,因此可能会引起频繁的上下文切换,影响效率。
- ET 模式:只在状态变化时通知应用程序,可以减少不必要的上下文切换,提高效率,特别适合处理大量事件和高并发的场景。
适用场景
-
LT 模式适用场景:
- 对实时性要求不是非常高的应用,例如普通的网络服务器或者需要周期性处理数据的情况。
- 适合处理一般的数据读取、写入等操作。
-
ET 模式适用场景:
- 对事件响应速度要求较高的应用,例如高性能网络服务器,需要快速处理大量连接或数据的情况。
- 适合处理大数据流、高并发请求等场景,可以减少因为频繁通知而引起的性能开销。
2.如何设置epoll的ET模式
在 epoll
中设置成 ET(Edge Triggered,边缘触发)模式,需要在使用 epoll_ctl
函数添加或修改事件时,设置 struct epoll_event
结构体中的 EPOLLET
标志位。
2.1 创建 epoll 实例:
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
2.2 准备事件结构体:
struct epoll_event event;
event.events = EPOLLIN | EPOLLET; // 使用 ET 模式
event.data.fd = sockfd; // sockfd 是需要监听的文件描述符
2.3 添加或修改事件:
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &event) == -1) {
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
非阻塞模式:
- 使用 ET 模式通常需要将套接字设置为非阻塞模式,以充分发挥 ET 模式的优势。
- 可以通过
fcntl
函数设置套接字的非阻塞属性,如:
int flags = fcntl(sockfd, F_GETFL, 0);
if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl");
exit(EXIT_FAILURE);
}
3.两种模式的比较
示例代码:
epoll的ET模式下的回显服务器
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>
#