I/O复用之epoll

操作函数

int epoll_create(int size);

参数size可以是任何一个大于0的数字
创建一个epoll实例,返回这个实例的文件描述符

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

对epoll实例进行增删改操作

  • epfd
    epoll实例对应的文件描述符

  • op
    对应操作
    EPOLL_CTL_ADD:add,往epoll中添加结点
    EPOLL_CTL_MOD:modify,修改 epoll 中的结点
    EPOLL_CTL_DEL:delete,删除 epoll 中的结点

  • fd
    结点中的文件描述符

  • event
    是个结构体,内部包含 events和 data

    • events
      要检测的事件类型,类型间用 "|" 连接表示同时检测多种事件类型
      EPOLLIN:读事件,表示这个文件描述符可以读
      EPOLLOUT:写事件,表示这个文件描述符可以写
      EPOLLET:将 epoll 设置为边沿触发模式 ET
      EPOLLRDHUP:对端关闭socket,即关闭连接时会触发
      EPOLLONESHOT:仅监听此fd上的一次事件,若要再次监听这个fd,需要使用 epoll_ctl() 再次注册

    • data
      通常储存第三个参数,即结点中的文件描述符。所以是以 struct epoll_event 形式往 epoll 中添加结点

  • 返回值
    失败返回-1,成功返回0

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

检测 epoll 中有无结点触发了事件,即读就绪/写就绪。如果没有就绪结点,函数会阻塞在这里

  • epfd:同上

  • events:是 epoll_event 类型的数组,储存了检测到就绪的结点

  • maxevents:修饰第二个参数,规定了数组的最大容量

  • timeout: 函数阻塞的时长

  • 返回值:返回就绪的描述符数目。0表示已超时。-1表示有错误发生,需要检查 errno错误码

LT & ET

LT 水平触发

检测到事件触发通知应用程序,程序可以不立即处理。每次调用epoll_wait时都会向应用程序报告,直至程序处理。

ET 边沿触发

检测到事件触发通知应用程序,程序必须立即处理。

EPOLLONESHOT

我们设想这样一个情况,客户端发来信息,触发epoll上的读就绪事件,分配一个线程去处理报文,但此时客户端发来的报文不完整,线程就阻塞在这等待,下一次客户端发来信息再次触发读事件,可能被分配给另一个线程处理,这样就导致一份报文被两个线程处理,这不是我们所希望的。
所以我们可以把事件注册成 EPOLLONESHOT 类型,即使出现上述报文不完整的情况,客户端再次发来信息,仍不会触发事件,也就不会指派线程处理,就保证了当一个线程正在处理某个连接时,处理完成前别的线程没有机会操作。


每个socket被创建时,会被分配两个缓冲区:读缓冲区、写缓冲区。
当有信息发送来的时候,会先存放在读缓冲区中,会触发 epoll 上的文件描述符的读就绪事件。对于写就绪事件同理

posted @ 2023-08-05 22:15  悲伤鳄鱼吃面包  阅读(33)  评论(0编辑  收藏  举报