【1】什么是epoll的LT和ET模式
epoll对文件描述符的操作有两种模式
LT(LevelTigger)电平触发模式和ET (EdgeTrigger, 边沿触发)模式。
LT模式是默认的工作模式,这种模式下epoll相当于一 个效率较高的poll.
当往epoll内核事件表中注册一个文件描述符上的EPOLET事件时,epoll将以ET模式来操作该文件描述符。ET模式是epoll的高效工作模式。
对于采用LT工作模式的文件描述符,当epollwait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。这样,当应用程序下一次调用epoll wait时,epoll wait 还会再次向应用程序通告此事件,直到该事件被处理。
对于采用ET工作模式的文件描述符,当epollwait检测到其上有事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,因为后续的epollwait调用将不再向应用程序通知这一事件。
可见,ET模式在很大程度上降低了同一个epoll事件被重复触发的次数。因此ET模式效率要比LT模式高。
poll的LT模式代码可以参考我的前一个博客
【I/O多路复用】epoll系统调用(默认LT模式)
【2】ET的阻塞模式代码示例
我们通过代码来看ET模式下阻塞模式的缺点
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#define MAXFD 10
//向内核事件表epfd 中添加 新事件的文件描述符fd
void epoll_add(int epfd, int fd)
{
// 设置epoll_event的结构成员
struct epoll_event ev;
//=========================================
ev.events = EPOLLIN|EPOLLET;//或上EPOLLET
//=========================================
ev.data.fd = fd;
//EPOLL_CTL_ADD添加新事件及描述符到内核事件表
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)
{
perror("epoll_ctl add error\n");
}
}
void epoll_del(int epfd, int fd)
{
//从内核事件表中移除fd
if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1)
{
perror("epoll_ctl del error\n");
}
}
int create_sockfd();
int main()
{
int sockfd = create_sockfd();
assert(sockfd != -1);
//创建内核事件表
int epfd = epoll_create(MAXFD);
assert(epfd != -1);
// 设置epoll_event的结构成员
struct epoll_event ev;
ev.data.fd = sockfd;
ev.events = EPOLLIN;
epoll_add(epfd, sockfd);
// 定义events数组存放就绪描述符
struct epoll_event events[MAXFD];
while (1)
{
/*
epoll_wait返回的是前n个已经全就绪的文件描述符,
那么我们不用全部遍历,只遍历前n个就可以
超时时间设置为5秒
*/
int n = epoll_wait(epfd, events, MAXFD, 5000);
// epoll_wait() 调用失败
if (n == -1)
{
perror("epoll wait error!\n");
}
else if (n == 0)
{
printf("time out!\n");
}
// 只遍历前n个,因为内核已告诉我们前n个有就绪事件
else
{
int i = 0;
for (; i < n; ++i)
{
int fd = events[i].data.fd;
if (fd == -1)
{
continue;
}
// events 为内核为我们返回的就绪事件
if (events[i].events & EPOLLIN)
{
if (fd == sockfd)
{
struct sockaddr_in caddr;
int len = sizeof(caddr);