常见的5种IO模型

Unix下五种I/O模型:
1)阻塞I/O(blocking I/O)
2)非阻塞I/O (nonblocking I/O)
3)I/O复用(select 和poll) (I/O multiplexing)
4)信号驱动I/O (signal driven I/O (SIGIO))
5)异步I/O (asynchronous I/O (the POSIX aio_functions))
 
前四种都是同步,只有最后一种才是真正的异步IO。
阻塞I/O:

简介:进程会一直阻塞,直到数据拷贝完成
 
描述:应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。
如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

非阻塞IO:

简介:非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回);在数据拷贝的过程中,进程是阻塞的;
 
描述:我们把一个SOCKET接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,
不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,
如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。


IO复用:

简介:主要是select和epoll;对一个IO端口,两次调用,两次返回,
比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听;
 
描述:I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,
但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,
多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。


信号驱动IO:

简介:两次调用,两次返回;
 
描述:首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。
当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。


异步IO模型

简介:数据拷贝的时候进程无需阻塞。
 
描述:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,
通过状态、通知和回调来通知调用者的输入输出操作同步IO引起进程阻塞,直至IO操作完成。
异步IO不会引起进程阻塞。IO复用是先通过select调用阻塞。


5种I/O模型的比较:

IO复用详解:
I/O复用的典型应用场景:
1.处理多个描述字时,比如同时处理套接字和磁盘IO、终端IO
2.一个客户同时处理多个套接字
3.服务器既要处理监听套接字,又要处理已连接套接字
4.既要处理TCP、也要处理UDP
5.一个服务器要处理多个服务和协议
 
I/O多路复用不局限于网络编程,也可以用于其他程序。
select、poll、epoll简介
epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现

select:

select函数进行IO复用服务器模型的原理是:
    当一个客户端连接上服务器时,服务器就将其连接的fd加入fd_set集合,
    等到这个连接准备好读或写的时候,就通知程序进行IO操作,与客户端进行数据通信。
    大部分 Unix/Linux 都支select函数,该函数用于探测多个文件句柄的状态变化。
 
这样所带来的缺点是:
    
    1、 单个进程可监视的fd数量被限制,即能监听端口的大小有限。
    
    一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.
    
    2、 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:
    
    当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。
    
    3、需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
poll:

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,
然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,
如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,
被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
 
它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:
 
1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
 
2、poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
epoll:

epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,
并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,
一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知
 
epoll的优点:
1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;
即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。
 
3、 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。

在高并发情况下, 连接活跃度不是很高, epoll比select好
在并发不高情况下, 连接活跃度高, select比epoll好

猜你喜欢

转载自blog.csdn.net/qq_40348465/article/details/88067967