Socket学习系列之五种I/O模型

[TOC]

一、Linux下的五种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模型

简介

进程会一直阻塞,直到数据拷贝完成

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

模型图

在调用recv()/recvfrom()函数时,发生在内核中等待数据和复制数据的过程。

当进程调用recv()函数,其系统调用直到数据报到达且被复制到应用进程的缓冲区中或者发生错误才会返回。最常见的错误就是系统调用被信号中断。进程从调用recv开始到它返回的整段时间内是被阻塞的。

调用recv()函数后,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接字应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态(调用者所处的进程会被操作系统切换到阻塞态)。

非阻塞IO

简介

非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回);在数据拷贝的过程中,进程是阻塞的;

进程把一个SOCKET接口设置为非阻塞就是通知内核,当所请求的I/O操作无法完成时,不要将本进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间

模型图

前2次调用recvfrom时没有数据可返回,因此内核间转而立即返回一个EWOULDBLOCK错误。第3次调用recvfrom时已经有一个数据报准备好,它被复制到应用的进程缓冲区,于是recvfrom成功返回。我们接着处理数据。

当一个应用进程像这样对一个非阻塞描述符循环调用recvfrom时,我们称之为轮询,应用进程持续轮询内核,以查看某个操作是否就绪,这样往往消耗大量CPU时间。

IO复用模型

简介

IO复用模型主要是select、poll和epoll;对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听;

I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。

存在2阶段阻塞,一是调用select、poll等函数系统调用、二是对select返回的可读/可写fd读写(如recvfrom)时的系统调用

模型图

信号驱动IO

简介

我们可以用信号让内核在描述符就绪时发出SIGIO信号通知我们。

信号驱动存在两次调用,两次返回;首先我们开启套接字信号驱动I/O功能,并通过sigaction系统调用安装一个信号处理函数。该系统调用将立即返回,我们的进程继续运行并不阻塞。当数据报准备好读取时,内核就会为该进程产生一个SIGIO信号,进程会收到一个SIGIO信号,我们随后就可以在信号处理函数中调用I/O操作函数处理数据。

模型图

异步IO

简介

数据拷贝的时候进程无需阻塞。

当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作

异步IO告诉内核启动某个操作,并在内核在整个操作(包括将数据从内核复制到进程的缓冲区)完成后通知进程。

异步IO与信号驱动IO的主要区别在于:信号驱动IO是内核通知进程何时可以启动一个IO操作,而异步IO是由内核通知进程IO什么时候完成。

模型图

对比

同步IO和异步IO定义:

  • 同步IO(synchronous IO operation):导致请求进程阻塞,直到IO操作完成。
  • 异步IO(asynchronous IO operation):不导致请求进程阻塞

我们前面列出的5种IO模型,前四种都是同步,只有最后一种才是异步IO。

前面4种模型的主要区别在与第一阶段(等待数据),因为它们第二阶段都需要调用系统调用函数(如:read)进行读操作,真正的IO操作(recvfrom)将阻塞进程。最后一种模型是操作系统已经将数据从内核空间拷贝到了用户空间,不需要再次调用系统调用read

同步IO会引起进程阻塞,直至IO操作完成 ,IO复用是先通过select调用阻塞。 异步IO不会引起进程阻塞。

参考

发表评论