poll 模型

一、poll 模型简介

在这里插入图片描述
  从字面含义来看,poll 就是对集合中的文件描述符进行调查。

1.没有文件描述符数量限制

  poll 和 select 在本质上没有差别,管理多个文件描述符也是进行轮询,根据描述符的状态进行处理,但是 poll 没有最大文件描述符数量的限制

2.fdset 采用数组

  select 的 fdset(存放文件描述符的集合)采用 bitmap,select 默认大小为 1024,可以修改但是没有必要;在这里插入图片描述
poll 采用了数组,这个数组的大小根据业务的需求去定义大小。那么意思就是可以定义为无穷大吗?最多能打开多少,要查看系统的参数,不同的配置可以打开的最大文件数不一样。
在这里插入图片描述
ulimit -n
在这里插入图片描述

在这里插入图片描述
open files :最大的打开的文件数量,Linux下一切皆文件。
在这里插入图片描述

3.文件描述符数量增大,开销增大

  poll 和 select 同样存在一个缺点就是,文件描述符的数组被从用户态整体复制到内核态的地址空间;无论这些文件描述符是否有事件,它的开销随着文件描述符数量的增大而线性增大。

poll 也要遍历整个描述符数组才能得到有事件的描述符。

二、poll 函数和参数

1.poll 函数

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

(1)struct pollfd *fds :结构体数组
(2)nfds_t nfds :数组里面有效的元素的个数,相当于select 里面的最大有效文件描述符个数,或者说需要监视的描述符的个数。
(3)int timeout :超时机制

2. 文件描述符数组

(1)int fd :文件描述符

(2)short events :让内核去监视的事件,就像是 select 中让内核去监视读事件,写事件,异常事件

(3)short revents :返回的事件。调用 poll 函数,当监视的文件描述符有事件发生,内核修改的是 revents,而不修改 events 和 fd,这样做events 和 fd 就可以重用;poll 返回的是 revents 。

struct pollfd
{
	int fd; 
	short events;
	short revents;
};

3.初始化文件描述符数组

  初始化文件描述符数组时,是把所有的文件描述符置为 -1;
在这里插入图片描述
为什么呢?去查看一下帮助:当把文件描述符数组交给 poll 的时候,当文件描述符的值为 -1 时,poll 就会忽略。也就是说 poll 会忽略值为 -1 的文件描述符。

在这里插入图片描述

4.pollin 和 pollout

pollin :表示有数据可读,就相当于读事件

pollout:表示可写,写事件
在这里插入图片描述
pollin 的使用:
  下面代码的意思就是,将监听的socket 放到 poll 监视的数组中,当监听的socket 有事件发生时,发生的是读事件;表示 poll 有数据可以读了或者说poll 的处理的方式先读出来。
在这里插入图片描述
  就相当于 select 中将文件描述符放到读集合中,当这个文件描述符有事件发送,select 就去这个文件描述读取数据。那么为什么不是写呢,因为已经把事件分类了,放到了读集合中,那么对应这个集合中发生的事件,select 的操作都是读。
在这里插入图片描述

来到 poll 中也是一样,相当于将事件分类了,将文件描述符发生的事件归类与读事件,那么poll 只对它进行读。这个有点像学校的食堂,食堂将窗口都分类了,有的窗口卖奶茶,有的卖面,有的卖米饭。你去到了奶茶的窗口,人家只会卖给你奶茶,不会卖米饭和面给你。

5.revents

  (1)在select 函数中,每次调用它,会改变 socket 集合的内存,所以要把 socket 集合保存下来或者说备份,传一个临时的 socket 给 select 操作。
在这里插入图片描述
  (2)在poll 中不再对 文件描述符集合进行备份,直接对这个集合进行操作。会发现这里数组的下标直接用 socket 的值。
在这里插入图片描述
因为 poll 修改的是结构体中的 revents ,返回的也是revents 。其他的两个不变,所以不备份了,这里效率就提高了一点点。

struct pollfd
{
	int fd; 
	short events;
	short revents;
};

6.调用 poll 的错误返回值

  这个和 select 差不多:给了错误的文件描述符,信号中断,系统内存满了。

7.poll 的超时机制

  select 采用的是时间结构体:
在这里插入图片描述

poll 用的是整数,单位是毫秒
在这里插入图片描述

8.使用流程

在这里插入图片描述

8.1

  遍历整个数组,如果文件描述符的值为 -1 就表示没有事件发生,判断下一个,
在这里插入图片描述

8.2

  如果文件描述符的值不为-1,表示有事件发生;接着就判断发生的时间是不是读事件。如果不是读事件,下一个
在这里插入图片描述

8.3

  有读事件发生,那么就先把 revents 清空(初始化),因为接下来要修改它。就像是使用一个数组前,先清空。
在这里插入图片描述

8.4

  如果发生事件的 监听的socket ,那么就表示有新的客户端连接上来,也要把新的客户端的socket连接,加入数组中。因为客户端是发数据过来的,暂时把它的事件都分为读事件。
在这里插入图片描述
将新来的socket加入数组中,也将事件置为可读。同时要修改监视的数量。
在这里插入图片描述

8.5

  发生了错误
在这里插入图片描述
忽略了一个socket,就要重新计算监视的数量。
在这里插入图片描述

8.6

读取数据
在这里插入图片描述

三、文件描述符数组补充

1.监视的数量变化问题

  当文件描述符的数量有变化时,设置的监视的文件描述符的数量可以变也可以不变。比如说当数组里面只有4个文件描述符的时候,你可以设置监视的范围为4或者大于4都可以。
在这里插入图片描述
为什么可以大于4呢,因为其他的文件描述符的值我可以置为 -1 ,-1的会被poll忽略。这样和填4没有什么区别。反而会更加容易编程。这个怎么应用呢,当客户端关闭了socket之后,理应来说文件描述符数组中要将这个socket去掉,接着重排数组,然后重新计算要监视的数量。但是现在我将这个socket文件描述符置为-1,poll 会忽略掉它。
在这里插入图片描述

在这里插入图片描述

那么问题又来了,这样操作会不会导致数组里面有很多-1呢?其实不会的,系统分配socket 的值时候,不是随便分的,而是从小到大分配空闲的数(比如说从1-10分配给socket)分配。分配socket的值的时候,系统会看哪个数是空闲的,没有socket在使用。前面的0,1,2都被使用了,如果3被置为-1,那么代表是空闲的,就将3分配给一个socket,这样就不会出现很多-1了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值