IO(四)

Linux中有5种IO模型:

阻塞式IO
非阻塞式IO
IO复用
信号驱动式IO(SIGIO)
异步IO(Posix的aio_系列函数)

阻塞型IO:阻塞式IO是Linux中最基本、最常用的IO模型,指的是可能会使进程永远阻塞的函数,一般表现为:进程或线程调用某个函数,该函数需要满足特定条件才能向下执行
如果条件不满足,则会使调用进程或线程阻塞,让出CPU控制权,并一直持续到条件满足为止,在Linux中,阻塞式IO一般作为默认属性出现,如mq_receive、sem_wait、sem_post等
在默认情况下,所有的套接字都是阻塞的,我们以UDP套接字为例来展示阻塞式IO模型。

 进程调用recvfrom接收数据,但由于内核还未准备好,进程就会阻塞;直到内核准备好数据,recvfrom完成数据复制工作,进程才能解除阻塞状态。


非阻塞型IO:顾名思义,非阻塞式IO不会使调用进程或线程永远阻塞,具体表现为:如果IO操作不能完成,则立即出错返回,调用进程或线程继续向下执行。对于一个给定的描述符,有两种将其指定为非阻塞式IO的方法:调用open创建打开文件时指定O_NONBLOCK标志对于一个已经打开的描述符,调用fcntl改变其属性,为其设置O_NONBLOCK标志。
#include 
int fcntl(int fd, int cmd, ... /* int arg */);

IO多路复用(I/O multiplexing):多个进程使用同一个IO流,一旦发现进程指定的一个或者多个描述符可进行无阻塞IO访问时,它就通知该进程。

多个Sock复用一个IO线程这个功能是在内核+驱动层实现的。

 I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流. 发明它的原因,是尽量多的提高服务器的吞吐能力。

类似:ngnix会有很多链接进来, epoll会把他们都监视起来,然后像拨开关一样,谁有数据就拨向谁,然后调用相应的代码处理。

 select, poll, epoll 都是I/O多路复用的具体的实现,他们出现是有先后顺序的。

select在1983年BSD UNIX里面实现的

 select流程伪代码如下:

{
	select(socket);
	while(1) 
	{
		sockets = select();
		for(socket in sockets) 
		{
			if(can_read(socket)) 
			{
				read(socket, buffer);
				process(buffer);
			}
		}
	}
}

 具体的API :

int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);

select 被实现以后,很快就暴露出了很多问题。

· select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。

· select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但是并不会告诉你是那个sock上有数据,于是你只能自己一个一个的找,10几个sock可能还好,要是几万的sock每次都找一遍。

· select 只能监视1024个链接,linux 定义在头文件中的,参见FD_SETSIZE。

· select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现,尼玛,这个sock不用,要收回。对不起,这个select 不支持的,如果你丧心病狂的竟然关掉这个sock, select的标准行为是。。呃。。不可预测的, 这个可是写在文档中的哦.

于是14年以后(1997年)一帮人又实现了poll, poll 修复了select的很多问题,比如

· poll 去掉了1024个链接的限制,于是要多少链接呢, 主人你开心就好。

· poll 从设计上来说,不再修改传入数组,不过这个要看你的平台了

epoll 可以说是I/O 多路复用最新的一个实现,epoll 修复了poll 和select绝大部分问题, 比如:

· epoll 现在是线程安全的。 

· epoll 现在不仅告诉你sock组里面数据,还会告诉你具体哪个sock有数据,你不用自己去找了。

在这种模型下,如果请求的I/O操作阻塞,且它不是真正阻塞I/O,而是让其中的一个函数等待,在此期间,I/O还能进行其他操作。


信号驱动I/O模型:在这种模型下,进程要定义一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动I/O。这是由内核通知用户何时可以启动一个I/O操作决定的。


异步I/O模型:信号驱动I/O是由内核通知我们何时可以启动一个I/O操作,而异步I/O模型是由内核通知进程I/O操作何时完成的。现在,并不是所有的系统都支持这种模型。

smartcat.994
原文地址:https://www.cnblogs.com/SmartCat994/p/14028811.html