Unix5中IO模型的图解分析和比较。

来源:互联网 发布:phoneshop软件 编辑:程序博客网 时间:2024/04/30 07:19

Unix下共有五种I/O模型

阻塞I/O

非阻塞I/O

I/O复用(select和poll)

信号驱动I/O(SIGIO)

异步I/O(Posix.1的aio_系列函数)

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

 

 untitled1


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

 untitled2


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

untitled3
 

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

untitled4
 

f.异步I/O模型
调用aio_read函数,告诉内核描述字,缓冲区指针,缓冲区大小,文件偏移以及通知的方式,然后立即返回。当内核将数据拷贝到缓冲区后,再通知应用程序。

untitled5
 

2.几种I/O模型的比较
前四种模型的区别是第一阶段基本相同,第二阶段基本相同,都是将数据从内核拷贝到调用者的缓冲区。而异步I/O的两个阶段都不同于前四个模型。

untitled6
 

3.同步I/O和异步I/O
a.同步I/O操作引起请求进程阻塞,直到I/O操作完成。
异步I/O操作不引起请求进程阻塞。
b.我们的前四个模型都是同步I/O,只有最后一个异步I/O模型是异步I/O。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/blueboy2000/archive/2009/08/26/4485874.aspx

 



Unix下分5种基本的I/O模型:

1.阻塞I/O
2.非阻塞I/O
3.I/O复用(select和poll)
4.信号驱动I/O(SIGIO)
5.异步I/O(POSIX.1的aio_系列函数)

Unix中一个输入操作一般有两个不同的阶段:
1.等待数据准备好。
2.从内核到进程拷贝数据。
对于一个sockt上的输入操作,第一步一般是等待数据到达网络,当分组到达时,它被拷贝到内核中的某个缓冲区,第二步是将数据从内核缓冲区拷贝到应用程序缓冲区。

    下面分别介绍上面提到的5类I/O模型

本文中我们用UDP来进行举例,并且我们将函数recvfrom视为系统调用,这样让我们的注意力都集中在I/O模型上。

阻塞I/O模型
   
    最流行的I/O模型是阻塞I/O模型,缺省时,所有sockt都是阻塞的,这意味着当一个sockt调用不能立即完成时,进程进入睡眠状态,等待操作完成。如图:

 

图1 阻塞I/O模型

    在图1中,进程调用recvfrom,此调用直到数据报到达且拷贝到应用缓冲区或是出错才返回。最常见的错误是系统调用被信号中断,我们所说进程阻塞的整段时间是指从调用recvfrom开始到它返回的这段时间,当进程返回成功指示时,应用进程开始处理数据报。

    下面是一个采用阻塞I/O模型编写的简单服务器端代码(本代码来至于Unix网络编程),这段代码的功能是把客户端发来的数据再回射到客户端,本例子中进程阻塞于recvfrom.

#include     "unp.h" 

void   dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen);

int
main(int argc, char **argv)
 {
     int     sockfd;
     struct sockaddr_in servaddr, cliaddr;
     sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
     bzero(&servaddr, sizeof(servaddr));
     servaddr.sin_family = AF_INET;
     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
     servaddr.sin_port = htons(SERV_PORT);
     Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
     dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
 }

void   dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
  {
      int     n;
      socklen_t len;
      char    mesg[MAXLINE];
      for ( ; ; ) {
         len = clilen;
         n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
         Sendto(sockfd, mesg, n, 0, pcliaddr, len);
     }
 }

非阻塞I/O模型 
      
   当我们把一个sockt设置成非阻塞放式时,即通知内核:当请求的I/O操作非得让进程睡眠才能完成时,不要让进程睡眠,而应该返回一个错误。如图:

 

图2 非阻塞I/O模型

    如图2所示,前3次调用recvfrom时仍无数据返回,因此内核立即返回一个EWOULDBLOCK错误。第4次调用recvfrom时,数据报已经准备好了,被拷贝到应用缓冲区,recvfrom返回成功指示,接着就是我们处理数据报。
    当一个应用进程像这样对一个非阻塞sockt循环调用recvfrom时,我们称此过程为轮询(polling).应用进程连续不断的查询内核,看看某操作是否准备好,这对CPU是极大的浪费,但这种模型只是偶尔才遇到。

I/O复用模型

    I/O复用能让一个或多个I/O条件满足(例如,输入已经准备好被读,或者描述字可以承接更多的输出)时,我们就被通知到。I/O复用由select和poll支持,较新的Posix.1g也支持(pselect)。
I/O复用典型地用在下列网络应用场合:
    1.当客户处理多个描述字时,必须使用。
    2.一个客户同时处理多个sockt.
    3.如果一个服务器既要处理监听sockt,又要处理连接sockt,一般也用到。
   
4.如果一个服务器既要处理TCP,又要处理UDP,一般也用到。
    5.如果一个服务器要处理多个服务或多个协议(例如inetd守护进程),一般也用到。

    I/O复用并非限于网络编程,许多正是应用程序也需要使用这项技术。

    有了I/O复用,我们就可以调用select或poll,在这两个系统调用中的某一个上阻塞,而不阻塞于真正的I/O系统调用。图3是I/O复用模型的一个小结。



图3 I/O复用模型

    我们阻塞于select调用,等待数据报socket可读,当select返回socket可读条件时,我们调用recvfrom将数据报拷贝到应用缓存区中。
    将图3与图1比较,似乎没有显示什么优越性,实际上因使用了select,要求2此系统调用而不是一次,好像变的还有点差,但是select的好处在于我们可以等待多个描述字准备好。

信号驱动 I/O模型

   

    信号驱动 I/O模型能在描述字准备好时用信号SIGIO通知我们,下图给出例子:




来源:(http://blog.sina.com.cn/s/blog_5f4344bf0100cklt.html) - 5种基本I/O模型(Unix网络编程)_飞奔一生_新浪博客


图4 信号驱动 I/O模型

    首先我们允许sockt进行信号驱动 I/O,并通过系统调用sigaction安装一个信号处理程序。此系统调用立即返回,进程继续工作,它是非阻塞的。当数据报准备好被读时,就为该进程生成个SIGIO信号。我们随即可以在信号处理程序中调用recvfrom来读取数据报,并通知主循环数据已准备好被处理,也可以通知主循环,让它来处理数据报。

    无论我们如何处理SIGIO信号,这种模型的好处是当等待数据报到达时,可以不阻塞。主循环可以继续执行,只是等待信号处理程序的通知:或者数据报已准备好被处理,或者数据报已准备好被读取。


异步I/O模型

    异步I/O模型是Posix.1的1993版本中的新内容。我们让内核启动操作,并在整个操作完成后(包括将数据报从内核拷贝到我们自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别在于:信号驱动I/O是有内核通知我们何时可以启动一个I/O操作,而异步I/O模型是由内核通知我们I/O操作何时完成。图5给出了一个例子。

 

 

 

 

图5 异步I/O模型

    我们调用aio_red(Posix异步I/O函数以aio_或lio_开头),给内核传递描述字、缓冲区指针、缓冲区大小(与red相同的3个参数)、文件偏移(与lseek类似),并高书内核当整个操作完成时如何通知我们。此系统调用立即返回,我们的进程不阻塞于等待I/O操作的完成。在此例子中,我们假设要求内核在操作完成时产生一个信号,此信号直到数据已拷贝到应用程序缓冲区才产生,这一点是于信号驱动I/O模型不同的。

各种I/O模型的比较



图6 各类I/O模型的比较


    图6给出了上述5中I/O模型的比较。它表明:前4种模型的区别都在第1阶段,因为前4种模型的第2阶段基本相同:在数据从内核拷贝到调用者的缓冲区时,进程阻塞于recvfrom调用。然而异步I/O处理的两个阶段都不同于前4个模型。
   
    同步I/O与异步I/O

    Posix.1定义这两个术语如下:

    1.同步I/O操作引起请求进程阻塞,直到I/O操作完成。

    2.异步 I/O操作不引起请求进程阻塞。

    根据上述定义,我们的前四个I/O模型都是同步I/O模型,因为真正的I/O操作(recvfrom)阻塞进程,只有异步I/O模型与异步I/O的定义相符合。

 



同步I/O和异步I/O;阻塞I/O和非阻塞I/O

 

当read数据时,如果这时没有数据可读,阻塞I/O会一直等待有数据读,数据从kernel copy 到socket的buffer后返回;非阻塞I/O会立即返回,但如果有数据可读,非阻塞I/O也是等数据从kernel copy 到socket的buffer后返回。



以上是阻塞与非阻塞I/O的区别,但以上两个例子的read操作都是同步的,是不是很奇怪?点解称其为同步?看看同步的定义就会很明确:

  • A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.

  • An asynchronous I/O operation does not cause the requesting process to be blocked.



现在举一个异步的例子就会很明白了,也是对read这个操作,当有数据可读时,异步I/O立即返回,kernel copy数据到socket buffer后,会以事件等通知程序read操作完成。


关键就在于将数据从kernel copy到socket buffer这段时期,read操作是否阻塞,阻塞I/O是阻塞的,而异步I/O是不阻塞的。

原创粉丝点击