I/O 多路复用 以及 select, poll, epoll详解

来源:互联网 发布:淘宝两个店铺绑定 编辑:程序博客网 时间:2024/06/16 00:12

I/O 多路复用

参考资料:
http://www.zhihu.com/question/32163005
I/O多路复用通俗易懂
http://www.ibm.com/developerworks/cn/linux/l-async/
四种 I/O 模型的介绍以及AIO详解
http://blog.csdn.net/historyasamirror/article/details/5778378
I/O 模型介绍
http://blog.csdn.net/zhang_shuai_2011/article/details/7675797
IO多路复用的几种实现机制的分析
http://www.cnblogs.com/way_testlife/archive/2011/04/16/2018312.html
进程与线程之间的区别和联系
http://blog.csdn.net/dazhong159/article/details/7896070
进程与线程之间的区别和联系
http://blog.csdn.net/piaojun_pj/article/details/5991968/
浅谈select函数
http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html
socket编程详解
http://blog.csdn.net/wind19/article/details/6156339
socket编程例子
http://www.cnblogs.com/Anker/p/3265058.html
select,poll,epoll区别以及详解
http://blog.csdn.net/turkeyzhou/article/details/8504554
select,poll,epoll归纳总结区分

第一种方法是最传统的多进程并发模型 (每进来一个新的I/O流会分配一个新的进程管理。)
第二种方法是I/O多路复用 (单个线程,通过记录跟踪每个I/O流(sock)的状态,来同时管理多个I/O流 。)

I/O 多路复用
“一根网线,多个sock复用”
不管用多进程还是I/O多路复用,网线都只是一根。
多个Sock复用一根网线这个功能是在内核+驱动层实现的。

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

I/O多路复用

在同一个线程里面, 通过拨开关的方式,来同时传输多个I/O流, (学过EE的人现在可以站出来义正严辞说这个叫“时分复用”了)。

Linux I/O模型

I/O 模型的简单矩阵

同步阻塞I/O,同步非阻塞I/O,异步阻塞I/O,异步非阻塞I/O

同步
异步

  • 同步阻塞I/O模型的典型流程

同步阻塞
read调用会延续很长时间。实际上,在内核执行读操作和其他工作时,应用程序的确会被阻塞。

  • 同步非阻塞I/O模型的典型流程

同步非阻塞
同步非阻塞 I/O是同步阻塞 I/O 的一种效率稍低的变种。在这种模型中,设备是以非阻塞的形式打开的。这意味着 I/O 操作不会立即完成,read 操作可能会返回一个错误代码,说明这个命令不能立即满足(EAGAIN 或 EWOULDBLOCK)。
非阻塞的实现是 I/O 命令可能并不会立即满足,需要应用程序调用许多次来等待操作完成。这可能效率不高,因为在很多情况下,当内核执行这个命令时,应用程序必须要进行忙碌等待,直到数据可用为止,或者试图执行其他工作。正如图 3 所示的一样,这个方法可以引入 I/O 操作的延时,因为数据在内核中变为可用到用户调用 read 返回数据之间存在一定的间隔,这会导致整体数据吞吐量的降低。

  • 异步阻塞I/O模型的典型流程(select)

异步阻塞
另外一个阻塞解决方案是带有阻塞通知的非阻塞 I/O。在这种模型中,配置的是非阻塞 I/O,然后使用阻塞 select 系统调用来确定一个 I/O 描述符何时有操作。使 select 调用非常有趣的是它可以用来为多个描述符提供通知,而不仅仅为一个描述符提供通知。对于每个提示符来说,我们可以请求这个描述符可以写数据、有读数据可用以及是否发生错误的通知。
select 调用的主要问题是它的效率不是非常高。尽管这是异步通知使用的一种方便模型,但是对于高性能的 I/O 操作来说不建议使用。

  • 异步非阻塞I/O模型的典型流程

异步非阻塞
异步非阻塞 I/O 模型是一种处理与 I/O 重叠进行的模型。读请求会立即返回,说明 read 请求已经成功发起了。在后台完成读操作时,应用程序会执行其他处理操作。当 read 的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。
在一个进程中为了执行多个 I/O 请求而对计算操作和 I/O 处理进行重叠处理的能力利用了处理速度与 I/O 速度之间的差异。当一个或多个 I/O 请求挂起时,CPU 可以执行其他任务;或者更为常见的是,在发起其他 I/O 的同时对已经完成的 I/O 进行操作。

AIO通知(异步I/O)

  • 使用信号量进行异步通知
    使用信号进行进程间通信(IPC)是 UNIX 中的一种传统机制,AIO 也可以支持这种机制。在这种范例中,应用程序需要定义信号处理程序,在产生指定的信号时就会调用这个处理程序。应用程序然后配置一个异步请求将在请求完成时产生一个信号。作为信号上下文的一部分,特定的 aiocb 请求被提供用来记录多个可能会出现的请求。

  • 使用回调函数进行异步通知

select poll epoll之间的区别整理

select, poll, epoll 都是I/O多路复用的具体的实现,select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。之所以有这三个鬼存在,其实是他们出现是有先后顺序的。

但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

  • select
    select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。
    select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但是并不会告诉你是那个sock上有数据,于是你只能自己一个一个的找,10几个sock可能还好,要是几万的sock每次都找一遍,开销太大。
    select 只能监视1024个链接,linux 定义在头文件中的,参见FD_SETSIZE
    select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现,这个sock不用,要收回。对不起,这个select 不支持的,如果你丧心病狂的竟然关掉这个sock, select的标准行为是不可预测的。
    Select的函数格式:
    int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval*timeout);

  • poll
    poll 去掉了1024个链接的限制。
    poll 从设计上来说,不再修改传入数组,不过这个要看你的平台了,所以行走江湖,还是小心为妙。
    poll仍然不是线程安全的, 这就意味着,不管服务器有多强悍,你也只能在一个线程里面处理一组I/O流。

  • epoll epoll 修复了poll 和select绝大部分问题
    epoll 现在是线程安全的。
    epoll 现在不仅告诉你sock组里面数据,还会告诉你具体哪个sock有数据,你不用自己去找了。
    epoll有个致命的缺点,只有linux支持。BSD上对应实现的是kqueue

    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

    进程间的通讯方式

    (1)管道(pipe)及有名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。(2)信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。(3)消息队列(message queue):消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。(4)共享内存(shared memory):可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。(5)信号量(semaphore):主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。(6)套接字(socket):这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。

select,poll,epoll简介

类型 简介 select select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是: 1、单个进程可监视的fd数量被限制 2、需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大 3、对socket进行扫描时是线性扫描 poll poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。 epoll epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,并且只会通知一次。在前面说到的复制问题上,epoll使用mmap减少复制开销。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知

注:水平触发(level-triggered)——只要满足条件,就触发一个事件(只要有数据没有被获取,内核就不断通知你);边缘触发(edge-triggered)——每当状态变化时,触发一个事件。

select,poll,epoll区别

区别 select poll epoll 支持最大连接数 1024(x86) or 2048(x64) 无上限 无上限 IO效率 每次调用进行线性遍历,时间复杂度为O(N) 每次调用进行线性遍历,时间复杂度为O(N) 使用“事件”通知方式,每当fd就绪,系统注册的回调函数就会被调用,将就绪fd放到rdllist里面,这样epoll_wait返回的时候我们就拿到了就绪的fd。时间发复杂度O(1) fd拷贝 每次select都拷贝 每次poll都拷贝 调用epoll_ctl时拷贝进内核并由内核保存,之后每次epoll_wait不拷贝

使用情景

当同时需要保持很多的长连接,而且连接的开关很频繁时,就能够发挥epoll最大的优势了。这里与服务器模型其实已经有些交集了。
同时需要保持很多的长连接,而且连接的开关很频繁,最高效的模型是非阻塞、异步IO模型。而且不要用select/poll,这两个API的有着O(N)的时间复杂度。在Linux用epoll,BSD用kqueue,Windows用IOCP,或者用libevent封装的统一接口(对于不同平台libevent实现时采用各个平台特有的API),这些平台特有的API时间复杂度为O(1)。 然而在非阻塞,异步I/O模型下的编程是非常痛苦的。由于I/O操作不再阻塞,报文的解析需要小心翼翼,并且需要亲自管理维护每个链接的状态。并且为了充分利用CPU,还应结合线程池,避免在轮询线程中处理业务逻辑。
但这种模型的效率是极高的。以知名的http服务器nginx为例,可以轻松应付上千万的空连接+少量活动链接,每个连接连接仅需要几K的内核缓冲区,想要应付更多的空连接,只需简单的增加内存(数据来源为淘宝一位工程师的一次技术讲座,并未实测)。这使得DDoS攻击者的成本大大增加,这种模型攻击者只能将服务器的带宽全部占用,才能达到目的,而两方的投入是不成比例的。
注:长连接——连接后始终不断开,然后进行报文发送和接受;短链接——每一次通讯都建立连接,通讯完成即断开连接,下次通讯再建立连接。

0 0
原创粉丝点击