目录
IO 多路复用概述
I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。
在IO多路复用技术描述前,先讲解下同步,异步,阻塞,非阻塞的概念。
网络IO模型
linux网络IO中涉及到的模型如下:
(1)阻塞式IO
(2)非阻塞式IO
(3)IO多路复用
(4)信号驱动IO
(5)异步IO
今天不谈信号驱动IO,略过..
同步/异步
在学习IO模型的时候,我们必须明确一个概念,处理 IO 的时候,阻塞和非阻塞都是同步 IO。
只有使用了特殊的 API 才是异步 IO,例如Linux网络中的AIO。
再看下POSIX对同步和异步这两个术语的定义:
- 同步IO操作:导致请求进程阻塞,直到I/O操作完成;
- 异步IO操作:不导致请求进程阻塞;
通俗的理解下同步和异步
-
同步:当执行系统调用read时,需要用户等待内核完成从内核缓冲区到用户缓冲区的数据拷贝。
-
异步:当执行异步IO操作例如
aio_read
时,用户不需要等待,只需要接收内核完成操作的通知,由内核来完成数据的读取。
阻塞/非阻塞
在知晓阻塞和非阻塞都是同步 IO后,阻塞和非阻塞就很好理解了
阻塞IO:由系统调用read,导致线程一直等待数据返回。
非阻塞IO:系统调用read后立即返回一个状态,当数据达到内核缓冲区之前都是非阻塞的,即返回一个系统调用状态。
ps:闪客的动图做的非常的形象,上述gif动图来源「低并发编程」
IO多路复用
IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;
select
select 是操作系统提供的系统调用函数,select()用来等待文件描述词(普通文件、终端、伪终端、管道、FIFO、套接字及其他类型的字符型)状态的改变。是一个轮循函数,循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行。
通过select,我们可以把一个文件描述符的数组发给操作系统, 让操作系统去遍历,确定哪个文件描述符可以读写, 然后告诉我们去处理:
头文件
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
select调用
拥塞函数,拥塞等待文件描述符事件的到来
int select(int maxfdp
, fd_set *readset
, fd_set *writeset
, fd_set *exceptset
,struct timeval *timeout);
参数说明:
maxfdp:被监听的文件描述符的最大值,它比所有文件描述符集合中的文件描述符的最大值大1,因为文件描述符是从0开始计数的;
readfds、writefds、exceptset:分别指向可读、可写和异常等事件对应的描述符集合。
timeout:用于设置select函数的超时时间,即告诉内核select等待多长时间之后就放弃等待。timeout == NULL 表示等待无限长的时间,timeout == 0,select立即返回
timeval结构体
struct timeval
{
long tv_sec; /*秒 */
long tv_usec; /*微秒 */
};
select置位
int FD_ZERO(int fd, fd_set *fdset); //一个 fd_set类型变量的所有位都设为 0
int FD_CLR(int fd, fd_set *fdset); //清除某个位时可以使用
int FD_SET(int fd, fd_set *fd_set); //设置变量的某个位置位
int FD_ISSET(int fd, fd_set *fdset); //测试某个位是否被置位
当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零
调用 select函数,拥塞等待文件描述符事件的到来 ;如果超过设定的时间,则不再等待,继续往下执行
select返回后,用FD_ISSET测试给定位是否置位:
if(FD_ISSET(fd, &rset)
{
...
//do something
}
fd_set结构体
fd_set其实这是一