常见Linux IO模型分析

来源:互联网 发布:hessenberg矩阵 编辑:程序博客网 时间:2024/04/29 20:46

LINUX常见IO模型介绍

UNIX中常见IO模型有五种:阻塞 IO、非阻塞IO、多路复用IO、异步IO、信号驱动IO。其中阻塞IO、非阻塞IO、多路复用IO、信号驱动IO都属于同步IO。

同步IO和异步IO

  1. 同步IO:应用程序主动向内核查询是否有可用数据,如果有自己负责把数据从内核copy到用户空间。

  2. 异步IO:应用程序向内核发起读数据请求需要:(1)告诉内核数据存放位置(2)注册回调函数,当内核完成数据copy后调用回调通知应用程序取数据。
  3. 同步/异步最大区别:数据从内核空间到用户空间的copy动作由应用程序自己完成。而异步IO则是注册回调函数并告知内核用户空间缓冲区存放地址,数据copy由内核完成。

下面以read操作为例简单介绍每种IO处理流程。

阻塞IO模型

                                                            图1 阻塞IO模型时序图

这种模型特点如其名,没有可用数据时read操作会被阻塞,用户线程也会阻塞在这个read操作上。这种模型对一些高速IO设备或者对于吞吐量要求高的场景比较合适。为最大限度发挥这种模型优势,需要提高每个线程处理效率,线程会长时间处于组塞状态会降低系统资源利用率。对于需要监听大量文件句柄,且每个句柄读写操作不频繁的场景,这种IO模型。不适合。比如:web聊天程序,需要保持大量长连接,但是每个连接产生数据的速度很慢。在此场景下一个线程对应一个连接,线程busy时间较少,系统利用率低,系统资源被大量浪费(文件句柄、连接句柄、线程占用的内存等宝贵资源)。这种场景多路复用IO非常合适。

非阻塞IO模型

                                                  图2 非阻塞IO模型时序图

这种IO模型和阻塞IO不同。在无数据可读时,read操作并不会被阻塞,而是返回一个错误码。应用程序取到这个错误码后可知当前并无数据可用,从而进行其它逻辑处理(可以继续尝试读取数据,也可以做其它事情)。这种IO模型在处理web聊天程序场景的效率会比阻塞IO模型好点,至少不会被阻塞住,这样可以遍历所有套接字句柄,直到找到一个有数据可读套接字,然后为这个连接服务。但这也有一个明显的缺点:用户需要多次调用read操作然后根据返回值判断当前句柄是否有数据可读,多次无用循环浪费了cpu而且无用 read调用也使程序运行效率下降。

多路复用IO模型

阻塞IO缺点:线程在无数据可用时候会被阻塞,每个线程只监听自己关心的文件句柄。

非阻塞IO缺点:read操作可能读取不到数据。多路复用IO模型可以很好解决上述缺点。

多路复用IO解决上述问题的思路:(1)用一个线程(Selector)监听所有已注册的文件句柄,(2)只有当监听的文件句柄有可用数据时才通知应用程序去读取数据。

多路复用IO常用有三种实现方式:select、pool、epoll。下面分别介绍这三种IO模型实现原理。

Select模型


图3 Select模型时序图


图4 select模型流程图

每种事件类型对应一个fd_set,比如:OP_READ、OP_WRITE、OP_EX各有一个fd_set与之对应。应用程序使用多个fd_set位图保存不同事件类型的文件句柄。

select模型有两个比较明显的缺点:
  1. fd_set采用bitmap实现(bitmap大小是1024),最大文件句柄数为1024,也就是说每种事件类型最多可以监听1024个文件句柄。

  2. 两次fd_set拷贝:调用select时把fd_set从用户空间copy到内核空间,select返回时把fd_set从内核空间copy到用户空间。

  3. Select返回后,用户需要遍历所有fd_set找出可用句柄

Poll模型

Poll是select增强版,原理和select基本一样。它使用链表结构代替了位图结构,解决了最多只能监听1024个文件句柄的问题。但是第2和第3个缺点还是没有解决。下面重点介绍Epoll,它解决了select所有问题。

EPoll模型


                                                                          图5 Epoll模型流程图

Epoll模型在初始时做了2件事

  1. 把fd_list拷贝到内核空间,只copy一次
  2. 在需要监听IO设备上注册select线程,并添加回调函数。这个回调函数的作用就是把可用fd放到就绪队列中。每当设备可用时,设备驱动调用回调函数并且唤醒处在休眠中的select线程。

信号驱动IO模型


                                                                图6 信号驱动IO序列图

进程先向内核注册一个信号处理函数,然后返回做其它事情。当有数据可用时,进程会收到信号并且调用信号处理函数完成数据拷贝和处理。数据从内核拷贝到用户空间是由应用进程完成的,所以信号驱动IO也属于同步IO。

异步IO模型


                                                                   图7 异步信号IO模型

异步io调用需要完成两件事:(1)注册信号处理函数(2)告诉内核IO缓冲区的地址。

异步IO模型和前面几种IO模型最大的区别是:当有数据可读时,内核负责把数据copy到用户空间缓冲区中。应用进程收到信号后调用信号处理函数处理,信号处理函数直接从缓冲区取数据,而不必从内核拷贝。

0 0
原创粉丝点击