理解IO模型

前言

在谈到IO模型之前,我们先来了解下Liunx里面的几个概念:

User space(用户空间)和 Kernel space(内核空间)。Linux里面这么设计的目的主要是为了安全,即使用户空间崩溃了,内核也不受影响。所以在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。

此外还要理解阻塞,非阻塞,同步,异步这几个概念,这里不再详细介绍,我之前的文章中非常详细的介绍过。

说下目前Liunx的5种IO模型:

blocking IO - 阻塞IO

nonblocking IO - 非阻塞IO

IO multiplexing - IO多路复用

signal-driven IO - 信号驱动式IO(异步阻塞)

asynchronous IO - 异步IO

其中前面三种都可以归纳为同步IO,最后一种为异步IO,在linux里面一次io操作会涉及两个系统对象:用户进程,内核空间。其中用户进程发起io的读写操作后,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说当一个read操作发生后,它会经历两个阶段:

第一阶段:等待数据准备 (Waiting for the data to be ready)。.

第二阶段:将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)。

对于socket流而言:

第一步:通常涉及等待网络上的数据分组到达,然后被复制到内核的某个缓冲区.

第二步:把数据从内核缓冲区复制到应用进程缓冲区。

同步阻塞IO

同步阻塞 IO 模型是最常用的一个模型,也是最简单的模型。在linux中,默认情况下所有的socket都是blocking。它符合人们最常见的思考逻辑。

在这个IO模型中,用户空间的应用程序执行一个系统调用(recvform),这会导致应用程序阻塞,什么也不干,直到数据准备好,并且将数据从内核复制到用户进程,最后进程再处理数据,在等待数据到处理数据的两个阶段,整个进程都被阻塞。不能处理别的网络IO。直到kernel返回结果,用户进程才解除block的状态,重新运行起来。

 

可以看出来,这两个阶段都block住了。

同步非阻塞IO

在这种模式下,用户进程发出请求后,并不会阻塞,内核会里面返回一个error状态,然后用户进程需要轮询不断的check状态,在轮询期间可以干点别的事,最终直到内核把数据准备好了,然后通知用户进程,把数据从内核空间拷贝到用户所在的进程进行处理。

 

IO多路复用 multiplexing

IO多路复用,指的是由转门的一个进程负责轮询检查IO操作的状态,而不用每个用户进程都得自己负责轮询,这样就大大节省了线程资源。那么这就是所谓的 “IO 多路复用”。UNIX/Linux 下的 select、poll、epoll 就是干这个的(epoll 比 poll、select 效率高,做的事情是一样的)

 

。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。 多路复用的特点是通过一种机制一个进程能同时等待IO文件描述符,内核监视这些文件描述符(套接字描述符),其中的任意一个进入读就绪状态,select, poll,epoll函数就可以返回

信号驱动IO

信号驱动式I/O:首先我们允许Socket进行信号驱动IO,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据

 

异步非阻塞IO

相对于同步IO,异步IO不是顺序执行。用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO两个阶段,进程都是非阻塞的。

Linux提供了AIO库函数实现异步,但是用的很少。目前有很多开源的异步IO库,例如libevent、libev、libuv。异步过程如下图所示:

 

IO模型整理

各个IO模型的比较图如下:

通过上面的图片,可以发现non-blocking IO和asynchronous IO的区别还是很明显的。在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

IO模型对比

从左向右,可以看到用户线程的阻塞是越来越少的,理论上说阻塞越少,其执行效率就越高。

下面我们来看下select,poll,epoll,kqueue,iocp分别属于那种模型:

select,poll属于第三种IO复用模型,iocp属于第5种异步io模型,那么epoll和kqueue呢?

其实与select和poll一样,都属于第三种模型,只是更高级一些,可以看做拥有了第四种模型的某些特性,比如callback的回调机制。

那么epoll,kqueue为什么比select和poll高级呢? 下面我们来分析一下:

首先他们都属于IO复用模型,I/O多路复用模型就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

select主要缺陷是,对单个进程打开的文件描述是有一定限制的,它由FD_SETSIZE设置,默认值是1024,虽然可以通过编译内核改变,但相对麻烦,另外在检查数组中是否有文件描述需要读写时,采用的是线性扫描的方法,即不管这些socket是不是活跃的,我都轮询一遍,所以效率比较低。

poll本质和select没有区别,但其采用链表存储,解决了select最大连接数存在限制的问题,但其也是采用遍历的方式来判断是否有设备就绪,所以效率比较低,另外一个问题是大量的fd数组在用户空间和内核空间之间来回复制传递,也浪费了不少性能。

epoll和kqueue是更先进的IO复用模型,其也没有最大连接数的限制(1G内存,可以打开约10万左右的连接),并且仅仅使用一个文件描述符,就可以管理多个文件描述符,并且将用户关系的文件描述符的事件存放到内核的一个事件表中(底层采用的是mmap的方式),这样在用户空间和内核空间的copy只需一次。另外这种模型里面,采用了类似事件驱动的回调机制或者叫通知机制,在注册fd时加入特定的状态,一旦fd就绪就会主动通知内核。这样以来就避免了前面说的无脑遍历socket的方法,这种模式下仅仅是活跃的socket连接才会主动通知内核,所以直接将时间复杂度降为O(1)。

最后来聊聊windows的iocp的异步IO模型,目前很少有支持asynchronous I/O的系统,即使windows上的iocp非常出色,但由于其系统本身的局限性和微软的之前的闭源策略,导致主流市场大部分用的还是unix系统,与mac系统的kqueue和linux系统的epoll相比,iocp做到了真正的纯异步io的概念,即在io操作的第二阶段也不阻塞应用程序,但性能好坏,其实取决于copy数据的大小,如果数据包本来就很小,其实这种优化无足轻重,而kqueue与epoll已经做得很优秀了,所以这可能也是unix或者mac系统至今都没有实现纯异步的io模型主要原因。

IO设计模式

从上面的几种io机制可以看出来,不同的平台实现的io模型可能都不一样,实际上不管哪一种模型,这中间都可以抽象一层API出来,提供一致的接口,目的是为了更好的支持跨平台编程语言的调用,屏蔽操作系统的差异性。这其中广为人知的有C++的ACE,Libevent这些,他们都是跨平台的,而且他们自动选择最优的I/O复用机制,用户只需调用接口即可。IO模型的抽象,总得来说有两种设计模式,分别是Reactor and Proactor模式,这个我们在下篇文章里面专门讨论,这里不在细说。在Java里面,io版本经历了bio,nio,aio的演变,这个我在上篇文章已经介绍过,其实对应io模型,分别是阻塞io,非阻塞io,异步io,这里需要注意的是异步io仅仅在windows上支持,在linux上还是基于epoll的实现的,并非纯异步。

总结

本篇文章结合了io的五种模型,分析了各个主流操作系统的io实现机制并对比了其优缺点,编程语言的io接口,其实是依赖底层的操作系统的实现,为了兼容不同平台的io调用,这里面出现了两种关于高性能io的设计模式,分别是Reactor and Proactor,其都是采用多路复用的思想,来设计抽象IO接口,这个我们在下篇文章会介绍。

本文分享自微信公众号 - 我是攻城师(woshigcs),作者:攻城师在路上

理解Linux里面的IO模型 - 云+社区 - 腾讯云

各种IO复用模式之select,poll,epoll,kqueue,iocp分析 - 云+社区 - 腾讯云

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值