IO复用:select、pselect、poll学习笔记

本文概述了Unix系统中的五种IO模型,包括阻塞IO、非阻塞IO、IO多路复用、信号驱动IO和异步IO,详细解释了每种模型的工作原理和应用场景,以及select、pselect和poll函数的作用和用法。

概述

当进程中用到多个IO,希望内核一旦发现指定的IO条件就绪,就通知进程,这个能力称为IO复用

Unix下可用的5中IO模型

5种IO模型包括:阻塞式IO、非阻塞式IO、IO复用、信号驱动式IO、异步IO

1.阻塞式IO模型

recvfrom为例,进程等待内核准备好数据后,并复制到用户进程的缓冲区或者发生错误才返回。进程在调用recvfrom直到返回这段期间,都是处于等待状态。

2.非阻塞式IO模型

如果把一个套接字设置成非阻塞式,会通知内核,当请求IO的操作不能及时完成时,不要置进程休眠,而是返回一个错误。

3.IO复用模型

调用select、poll或epoll,进程阻塞在这几个函数调用上,而不是阻塞在某个特定的IO上,当关注的任何一个或多个IO准备就绪,这几个函数返回,进程执行下一步操作。

和IO复用模型一个类似的模型,就是针对每一个描述符,创建一个线程,阻塞的等待描述符就绪。

4.信号驱动式IO

让内核在描述符准备就绪的时候,发送SIGIO信号给我们的进程,在此期间进程可以做任何事。

5.异步IO模型

异步IO的工作机制是,告诉内核启动某个操作,并在内核完成数据等待、操作完成之后通知到进程。它和信号驱动式IO的区别在于,前者是内核操作完指定函数之后通知我们,后者是内核在IO就绪的时候通知我们,随后需要进程自己去做具体操作。

POSIX中定义,真正的IO操作由进程来完成的,称为同步IO操作,反之称为异步IO操作。根据这个定义,前四种IO模型属于同步IO操作,最后一种模型属于异步IO模型

select函数

函数说明

我们调用select函数,告诉内核进程对哪些描述符的读、写、异常事件感兴趣,并指定愿意等待的时长,等待期间进程阻塞在select函数上,直到有描述符就绪,返回就绪的描述符数目。描述符不局限于套接字,可以是任何描述符。

#include <sys/select.h>
int select (int maxfdpl, fd_set * restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout);
返回值:若有描述符准备就绪,返回描述符数目,若超时返回0,若出错返回-1

我们感兴趣的可读、可写、发生异常的描述符,分别通过readfds、writefds、exceptfds参数传递给select。它们的数据结构是fd_set,本质是一个整数数组,感兴趣的fd集合记录在这个数组当中,可以设置为NULL。我们无需关心fd_set的具体实现,只需要调用相应的宏对fd_set进程增删改查:

void FD_ZERO(fd_set *fdset);//初始化fd_set
void FD_SET(int fd, fd_set *fdset);//将fd加入fdset
void FD_CLR(int fd, fd_set *fdset);//将fd从fdset中移除
int FD_ISSET(int fd, fd_set *fdset);//判断fd是否在fdset中

其中fdset会被select动态的修改,如select返回的时候,fdset中仅仅保留了已经准备就绪的fd,此时可以调用FD_ISSET来判断哪些fd准备就绪。

maxfdp1:传入的最大描述符内容+1,如我们传入描述符1、4、5,那么maxfdp1等于6

timeout:参数用于表示进程等待IO的超时时间

NULL:一直等待,直到有fd就绪
0:不等待
大于0的常数:等待指定时间

套接字描述符就绪

普通文件描述符可读、可写就绪的概念比较好理解,网络套接字描述符的就绪是什么概念?

如果网络套接字,接收缓存收到很少的数据,或只有很小的发送缓存,就给应用程序发送就绪信号,应用程序拿到这些数据可能没有办法执行业务,所以有必要对触发就绪设置一个最小值,如设置接收最小值和发送最小值。在接收数据大于接收最小值,触发select返回对应读fd,在发送缓存大于最小值,触发select返回对应写fd。接收最小值通过SO_RCVLOWAT设置,发送最小值通过SO_SNDLOWAT设置

(1)满足以下几个条件之一,网络套接字读就绪:

  1. 接收缓存中的数据大于等于接收最小值
  2. 监听套接字调用accept,且已完成连接队列中的连接数大于0
  3. 收到对侧发FIN包,sockfd触发可读,此时返回0
  4. 套接字有错误待处理,返回-1

(2)满足以下几个条件之一,网络套接字写就绪

  1. 发送缓存可用空间大于发送最小值
  2. TCP写方向关闭
  3. 套接字有错误待处理,返回-1

(3)exceptfds对应的连接有带外数据,select会返回

pselect函数

#include <sys/select.h>
int pselect(int maxfdpl, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, const struct timespec *restrict tsptr, const sigset_t *restrict sigmask)

pselect与select的区别在于

  1. 使用timespec指定超时时间,时间颗粒度更加精细
  2. 指定信号屏蔽字,并通过原子操作设定屏蔽字,可对select期间的未决信号进行屏蔽,select结束后恢复屏蔽字。

poll函数

poll函数和select函数功能类似,函数原型如下所示:

#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
- 返回值:就绪的fd数目,超时返回0,出错返回-1
- fdarray:指向struct pollfd结构体数组的指针
- nfds:fdarray结构体数组的数目
- timeout:超时时间,INFTIM永远等待,0不阻塞,>0指定等待的毫秒数

其中struct pollfd结构体定义如下所示:

struct pollfd {
  int fd;//感兴趣的fd
  short events;//该fd感兴趣的事件
  short revents;//fd实际发生的事件
}

events是对fd感兴趣的事件,revents是fd实际发生的事件,两者区别开来的原因是,一些错误事件不可作为events传入poll,却可以通过revents返回给调用者。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值