IO多路转接epoll函数解析复习

函数原型:

  1. open an epoll file descriptor: 打开一个epoll文件系统
#include <sys/epoll.h>
int epoll_create(int size);

参数:
在内核中开辟一块内存,创建一个epoll文件系统(也叫内核事件表、是一棵红黑树),参数size是对内核的一个建议,但是一定要比0大,不一定起作用,表示监听的文件描述符个数,也就是基础红黑树的建议大小。

返回值:
ret > 0 返回一个可用,合法的文件描述符,表示创建的epoll文件系统,指向红黑树树根
ret < 0 出错,根据全局变量errno去判断具体错误是什么

  1. control interface for an epoll file descriptor,向epoll file descriptor增加,删除,修改事件
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数:
epfd:
即epoll fd, epoll_create()大于0的返回值,表示我们将要操作哪一个epoll文件系统

op:
EPOLL_CTL_ADD :向epoll文件系统添加一个文件描述符
EPOLL_CTL_MOD:修改epoll文件系统一个文件描述符
EPOLL_CTL_DEL: 删除epoll文件系统一个文件描述符

fd:
操作的文件描述符

event : 重点理解

typedef union epoll_data {
    void        *ptr;  
    //泛型指针,可以携带任何数据进去,如自定义的结构体,函数指针都可以,比如
    /*
		struct st{
			int fd;
			void* args;
			void (*callback)(int fd,void* arg);//事件发生时直接调用该函数即可,联合体成员int fd功能更多
		};
	*/
     int          fd;  
     //文件描述符,跟上面参数的fd一一对应,events[i].data.fd.用于我们获取当前事件对应fd
     //可以满足大多数情况,有的时候可能需要获取更多信息,那就需要使用void        *ptr联合体成员
     uint32_t     u32;
     uint64_t     u64;
} epoll_data_t;

struct epoll_event {
       uint32_t     events;      //需要监听该文件描述符上的事件的类型,如EPOLLIN EPOLLOUT EPOLLRDHUP EPOLLERR等
       epoll_data_t data;        /* User data variable */
};

注意事项:
为什么参数有一个fd,但是struct epoll_event *event 中还要再记录一次?
实际上这也就说明了为什么epoll_wait()返回后,不仅知道发生fd的个数,包括具体哪个fd发生了都能知道,就是因为epoll_data中的fd和参数中fd是一一对应的。

返回值:
ret == 0 表示操作成功
ret < 0,表示操作失败,可以查看errno具体错误信息

  1. wait for an I/O event on an epoll file descriptor,监听发生的时间
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

参数:
epfd:
epoll fd, epoll_create()大于0的返回值,表示我们将要监听哪一个epoll文件系统

events: 传出参数,是一个数组,将来存放的所有发生事件的文件描述符,

maxevents: 数组的容量

timeout:
timeout == -1 表示阻塞等待,直到事件发生
timeout == 0 无论有无就绪事件,立即返回
timeout > 0 单位是毫秒,最多等待多少毫秒

返回值:

> 0 表示本次函数返回就绪事件的个数
< 0 出错,可根据errno判断错误信息
=0 在等待时间内,没有事件发生

int ready = epoll_wait(......);
for(int i = 0;i<ready;++i){
	if(events[i].events & EPOLLOUT){
		out_call_back(event[i].data.fd);
	}
	
}

在这里插入图片描述

基本应用的流程:

  • 创建epoll文件系统,创建listenfd
  • 调用epoll_ctl将listenfd添加到epoll文件系统中,然后调用epoll_wait进行监听
  • 当listenfd有事件发生,一般是客户端发送了连接请求,调用该事件的处理函数accept(),给客户分配一个fdn,作为客户和服务端连接的纽带
  • 然后再将该文件描述符添加到epoll文件系统中,监听起来
  • 当fdn有事件发生,判断事件类型,做出相应类型事件的对应反应,当然还要考虑ET LT标志位

ET:边沿触发,有变化才出发

模拟的是电平 0到1或者1到0的变化,有变化(客户端有新的数据过来)才触发
添加事件时,给对应的事件fd添加上EPOLLET,event.events |= EPOLLET
此时,假设客户端发送来100B,我们读取了50B,fd缓冲区中还剩余数据没有读完,epoll也不会再通知了

LT:水平触发,epoll默认工作方式

每次都触发,只要有数据没读完,epoll就会触发,告诉我们当前缓冲区有数据没有读完,需要继续读,不读完会一直触发

实际上,我觉得读数据这不应该是epoll做的,而应该是我们程序员去做,epoll只需要告诉我们哪个文件描述符上发生了哪些事件,即可,但是存在就有他的道理,使用场景不同。

ET模式从理论的角度看 ,降低了同一个fd 的事件被重复触发的次数,效率较高。但是这不是绝对的,因为LT有LT的应用场景,ET有ET的应用场景,谁效率高,可说不准呢,不能一昧的就认为ET效率一定比LT高。

ET使用场景:

ET:比如客户端每次给我发送来大量的数据,10000B,但是我先要查看数据的前100B来判断剩下的数据还有没有必要继续读取,如果我对100B数据进行校验,发现后面的数据不是我想要的,那么此时ET的高效性体现出来了,我把缓冲区剩余的90000多B的数据清空即可;

以前学习EPOLL时候记录的博客,里面有代码,感兴趣可以看看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值