【Linux】I/O复用之poll和epoll的用法

select系统调用可参见此博客

poll

1、原型:

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

2、fds:用户数组的首地址,记录所有文件描述符以及关注的事件类型,将用户空间的数据传递给内核,在poll返回时,也用于将内核检测到就绪事件通知给应用程序。

  • struct pollfd结构如下:
struct pollfd
{
	int fd;          //文件描述符
	short events;    //用户关注的事件类型能够关注的事件类型相对较多
	short revents;   //返回的事件   由内核填充,在poll返回时,将就绪事件类型通知给应用程序
};

在这里插入图片描述
3、nfds:要监视的描述符的数目。也就是第一个参数数组的长度。
4、timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间
5、返回值:

  • 大于 0就绪文件描述符的个数
  • 等于 0 超时
  • == -1出错

epoll(Linux上独有的I/O复用方式)

#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

1、epoll_create

  • int epoll_create(int size);
  • 创建一个内核事件表:用于记录用户关注的文件描述符以及其上的事件类型
  • size用来告诉内核这个监听的数目一共有多大。不同于select()中的第一个参数。
  • 当创建好epoll句柄后,它会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
  • 返回内核事件表的文件描述符

2、epoll_ctl

  • int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • 注册要监听的事件类型。
  • 第一个参数是epoll_create()的返回值
  • op 用三个宏来表示:
    EPOLL_CTL_ADD在文件描述符epfd引用的epoll实例上注册目标文件描述符fd。
    EPOLL_CTL_MOD修改已注册描述符fd关联的事件。
    EPOLL_CTL_DEL从epfd引用的epoll实例中删除(取消注册)目标文件描述符fd。该事件将被忽略,并且可以是NULL
  • 第三个参数:需要监听的fd
  • 第四个参数:告诉内核需要监听什么事
struct epoll_event 
{
	__uint32_t events;  /* Epoll 事件类型*/
	epoll_data_t data;  /* 用户数据*/
};

typedef union epoll_data 
{
	void *ptr;
	int fd;
	uint32_t u32;
	uint64_t u64;
} epoll_data_t;
  • events可以是以下几个宏的集合:
    在这里插入图片描述

3、epoll_wait

  • int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
  • 等待事件的产生
  • events : 用户数组,在epoll wait返回时,记录内核检测到的文件描述符上发生的事件
    • 分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)
    • 只记录就绪的文件描述符
  • maxevents : 告知内核这个events有多大
  • timeout : 超时时间

三种I/O复用方式的比较总结

1、3组系统调用都能同时监听多个文件描述符。它们将等待由timeout参数指定的超时时间,直到一个或者多个文件描述符上有事件发生时返回,返回值是就绪的文件描述符的数量。返回0表示没有事件发生。

事件集方面

  • select 的参数类型fd_set 没有将文件描述符和事件绑定,因此select需要提供3个这种类型的参数来分别传输可读、可写及异常等事件。这使得seleet 不能处理更多类型的事件,另一方面由于内核对fd_set 集合的在线修改,应用程序下次调用select前需要重置这3个fd_set集合
  • poll 的参数类型pollfd则把文件描述符和事件都定义其中,任何事件都被统一处理,从而使得编程接口简洁得多。并且内核每次修改的是pollfd结构体的revents成员,而events成员保持不变,因此下次调用poll时应用程序无须重置pollfd类型的事件集参数。
  • epoll不同的是,它在内核中维护一个事件表, 并提供了一个独立的系统调用epoll _ctl 来控制往其中添加、删除、修改事件。这样,每次epoll_wait调用都直接从该内核事件表中取得用户注册的事件,而无须反复从用户空间读人这些事件

最大支持文件描述符数

  • poll和epoll wait分别用nfds和maxevents参数指定最多监听多少个文件描述符和事件。这两个数值都能达到系统允许打开的最大文件描述符数目,即65 535 。而select允许监听的最大文件描述符数量通常有限制,虽然用户可以修改这个限制,但这可能导致不可预期的后果。

实现原理

  • select 和poll采用的都是轮询的方式,即每次调用都要扫描整个注册文件描述符集合,并将其中就绪的文件描述符返回给用户程序,因此它们检测就绪事件的算法的时间复杂度是0 (n)。
  • epoll_wait则不同,它采用的是回调的方式。内核检测到就绪的文件描述符时,将触发回调函数,回调函数就将该文件描述符上对应的事件插入内核就绪事件队列。内核最后在适当的时机将该就绪事件队列中的内容拷贝到用户空间。因此epoll_wait无须轮询整个文件描述符集合来检测哪些事件已经就绪,其算法时间复杂度是0 (1)。

总结

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值