在进入今天的select模型的主题之前,我们先来简单了解1下5种I/O模型:
(1)阻塞I/O(默许采取这类方式)
在服务端socket编程中,我们常见的accpet函数、recv函数都是采取的阻塞情势。以recv为例: 当上层利用App调用recv系统调用时,如果对等方没有发送数据(Linux内核缓冲区中没有数据),上层利用Application1将阻塞;当对等方发送了数据,Linux内核recv端缓冲区数据到达,内核会把数据copy给用户空间。然后上层利用App消除阻塞,履行下1步操作。
(2)非阻塞I/O(不推荐)
上层利用如果利用非阻塞模式, 会循环调用recv函数,接受数据。若缓冲区没有数据,上层利用不会阻塞,recv返回值为⑴,毛病码是EWOULDBLOCK(图中的标记有误)。上层利用程序不断轮询有无数据到来。造成上层利用忙等待。大量消耗CPU。因此非阻塞模式很少直接用。利用范围小,1般和IO复用配合使用。
(3)信号驱动I/O模型(不常常使用)
上层利用建立SIGIO信号处理程序。当缓冲区有数据到来,内核会发送信号告知上层利用App; 当上层利用App接收到信号后,调用recv函数,因缓冲区有数据,recv函数1般不会阻塞。但是这类用于模型用的比较少,属于典型的“拉模式(上层利用被动的去Linux内核空间中拉数据)”。即:上层利用App,需要调用recv函数把数据拉进来,会有时间延迟,我们没法避免在延迟时,又有新的信号的产生,这也是他的缺点。
(4)异步I/O(不经常使用)
上层利用调用aio_read函数,同时提交1个利用层的缓冲区buf;调用终了后,不会阻塞。上层利用程序App可以继续其他任务; 当TCP/IP协议缓冲区有数据时,Linux主动的把内核数据copy到用户空间。然后再给上层利用App发送信号;告知App数据到来,需要处理!
异步IO属于典型的“推模式”, 是效力最高的1种模式,上层利用程序App有异步处理的能力(在Linux内核的支持下,处理其他任务的同时,也可支持IO通讯, 与Windows平台下的完成端口作用类似IOCP)。
(5)I/O复用的select模型(本篇的重点)
试想如果你遇到下面的问题会怎样处理?
1)server除要对外响应client的服务外,还要能够接受标准输入的命令来进行管理。
假设使用上述阻塞方式,在单线程中,accept调用和read调用一定有前后顺序,而它们都是阻塞的。比如先调用accept,后调用
read,那末如果没有客户要求时,服务器会1直阻塞在accept,没有机会调用read,也就不能响应标准输入的命令。
2) server要对外提供大量的client要求服务。
假设使用阻塞方式,在单线程中,由于accept和recev都是阻塞式的,那末当1个client被服务器accept后,它可能在send发送消息时阻塞,因此服务器就会阻塞在recev调用。即时此时有其他的client进行connect,也没法进行响应。
这时候就需要select来解决啦!select实现的是1个管理者的功能: 用select来管理多个IO, 1旦其中的1个IO或多个IO检测到我们所感兴趣的事件, select就返回, 返回值就是检测到的事件个数, 并且由第2~4个参数返回那些IO发送了事件, 这样我们就能够遍历这些事件, 进而处理这些事件。
有人说,我用多线程不就能够了吗?但是在UNIX平台下多进程模型善于处理并发长连接,但却不适用于连接频繁产生和关闭的情形。固然select其实不是最高效的,有着O(N)的时间复杂度,关于更高效的epoll我将在后面的博客中继续讲授,欢迎大家关注,