图解Linux poll机制,终于集齐IO复用三剑客(精华篇)

前言:

前面几篇文章已经详细讲解了Linux select和epoll机制,select和epoll两种IO复用方式用的人比较多,就像一家人一样,epoll相当于大哥,select相当于弟弟,而poll相当于二哥,家里的老二通常是很容易被忽视的对象,poll这种IO复用方式也很容易被忽视。

epoll机制效率高,适用于高并发场景,所以epoll机制广泛用于各种开源项目。

select机制编程简单,如果对高并发没有需求,那么很多人会选择select机制做多路IO请求处理。

poll机制既没有高并发能力,编程也并不简单,所以经常吃灰。

这篇文章通过图文详解Linux poll实现原理,目的是让大家能够从本质了解poll机制,以及对三种IO多路复用方式有一个全面的了解,最后根据自己的业务需求,选择合适的IO复用机制。

1.poll简介

poll是一种I/O多路复用机制,它可以同时监视多个文件描述符,当其中任意一个文件描述符就绪时,就会通知程序进行相应的读写操作。

poll机制与select机制类似,但是poll没有最大文件描述符数量的限制,因此在文件描述符数量较大时,poll的效率会更高。

poll机制的使用需要调用系统调用poll()函数,该函数会阻塞进程直到有文件描述符就绪或者超时。

poll()函数的参数是一个pollfd结构体数组,每个结构体中包含了一个文件描述符和该文件描述符所关注的事件类型。

2.poll实现原理

poll实现原理重要活动如上图,内容有点多,但是很重要,请仔细分析确保真正理解:

  • 用户将想要监听的socket文件绑定struct pollfd对象,并注册监听事件至struct pollfd对象events成员,监听多个socket文件使用struct pollfd数组。
  • 用户通过struct pollfd数组注册poll事件至poll_list链表,poll_list链表单个元素可以存储固定数量的struct pollfd对象。
  • poll系统调用采用轮询方式获取socket事件信息,一次poll调用需完成整个poll_list链表轮询工作,轮询socket的过程中会创建socket等待队列项,并加入socket等待队列(用于socket唤醒进程)。如果检测到socket处于就绪状态,将socket事件保存在struct pollfd对象的revents成员。
  • poll系统调用完成一次轮询后,如果检测到有socket处于就绪状态,则将poll_list链表所有的struct pollfd通过copy_to_user拷贝至用户struct pollfd数组。如果未检测到有socket处于就绪状态,根据超时时间确定是否返回或者阻塞进程。
  • socket检测到读,写,异常事件后,会通过注册到socket等待队列的回调函数poll_wake将进程唤醒,唤醒的进程将再次轮询poll_list链表。

3.poll编程

3.1 poll函数原型

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

功能:poll函数是Linux系统中的一种I/O多路复用机制,它可以同时监视多个文件描述符。

参数:

fds:监听事件结构体数组。

nfds:监听事件结构体数组长度。

timeout:

等于-1:一直阻塞。

等于0:立即返回。

大于0:等待超时时间,单位毫秒。

返回值:

成功:返回检测到的文件描述符数量。

失败:返回-1,设置errno。

超时:返回0。


3.2 poll事件定义

#define POLLIN 0x0001

#define POLLPRI 0x0002

#define POLLOUT 0x0004

#define POLLERR 0x0008

#define POLLHUP 0x0010

#define POLLNVAL 0x0020

#define POLLRDNORM 0x0040

#define POLLRDBAND 0x0080

#define POLLWRNORM 0x0100

#define POLLWRBAND 0x0200

#define POLLMSG 0x0400

#define POLLREMOVE 0x1000

#define POLLRDHUP 0x2000

#define POLLFREE 0x4000

#define POLL_BUSY_LOOP 0x8000


3.3 struct pollfd 结构体

struct pollfd {

int fd;

short events;

short revents;

};

fd: 监听文件描述符。

events:监听事件集合,用于注册监听事件。

revents:返回事件集合,用于存储返回事件。


3.4 events和revents对poll事件支持情况


3.5 poll编程模型


=============================

文章没看懂没关系,每篇文章都有视频详解

获取方式私信博主(视频免费分享)

=============================


4.poll常见问题?

问题1:poll的优缺点?

优点:

  • poll没有1024最大文件描述符限制。
  • poll监视事件(events)和返回事件(revents)分离,简化编程。

缺点:

  • 采用轮询方式获取就绪文件描述符,效率低,和select一样。
  • 每次调用poll都需要把所有文件描述符从内核空间复制到用户空间。
  • 虽然poll没有1024最大文件描述符限制,但是注册的文件描述符越多,poll效率越低。


问题2:poll和select的区别?

poll和select底层实现非常相似,分析poll和select内核源码会发现二者之间很多地方都复用了相同的代码。

poll可以说是select的加强版,poll优化了select一些设计缺陷:

  • poll不受1024最大文件描述符限制,poll采用poll_list链表方式存储输入和输出事件,理论上可以不受最大文件描述符限制。
  • poll传入的是struct pollfd数组,并指定了数组长度,可以减少无效的轮询,提高轮询效率。
  • poll监视事件(events)和返回事件(revents)分离,每次调用poll不需要重新设置struct pollfd对象。
  • poll返回时不会返回剩余超时时间,用户不需要当心超时出现异常

不过很可惜,即使poll对select做了很多优化,依然没有改变轮询方式,也没有改变selec执行效率低的本质问题。

5.select,poll,epoll对比

物联网心球长期分享嵌入式软件开发干货图文知识,

C/C++,Linux Bug修复秘籍,Linux基础,Linux环境编程,Linux网络编程,高性能服务器,音视频开发,网络开发,Qt GUI开发。

原文链接:,转发请注明来源!