`
laughingchs
  • 浏览: 67932 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

IO模式

 
阅读更多

I/O模式概述
 阻塞I/O 非阻塞I/O I/O多路复用 信号驱动I/O 异步I/O
 五程I/O模式区别图:


1.阻塞I/O --- 最普遍使用的I/O模式。缺省的,一个套接字建立后所处于的模式即是阻塞I/O模式。
 图例如下:


 上图中,一个进程调用recvfrom 若没有数据报到达本地系统,刚阻塞。直接有数据报到达,才返回。
 
2.非阻塞I/O --- 设置套接字为非阻塞模式,相当于告诉系统:当请求的I/O操作不能马上完成,不进行休眠等待,马上返回一个错误.
 图例如下:


 上图中,三次recvfrom调用,仅一次正常返回,其余返回一个EWOULDBLOCK的错误。
 当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环不停的测试是否一个文件描述符有数

 据可读(称做polling).
 应用程序不停的polling来检查是否I/O操作已经就绪。这将浪费CPU资源,所有此模式不常用。

 

3.I/O多路复用
 调用select()函数和poll()函数,调用时阻塞,而不是来调用recv的时候阻塞。
 当调用select函数阻塞时,select函数等待数据报套接字进入读就绪状态。当select函数返回时,即套接字可以读取数据的时候,就可以调用recvfrom函数来接收数据(将数据拷贝到程序缓冲区)。
 多路复用优点:同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()就可以返回。
 图例如下:


 应用:假设运行一个网络客户端程序,要同时处理套接字传来的网络数据,又要处理本地标准输入输出。当程序处于阻塞状态等待标准输入的数据时,假如服务器端的程序被kill,那么服务器端的TCP协议会给客户端的TCP协议发送一个FIN数据代表终止连接。但我们这端的程序阻塞等待标准输入的数据上,在它读取套接字数据之前,它不会看见结束标志。
 IO多路技术一般在下面情况中应用:
 当一个客户端需要同时处理多个文件描述符的输入输出操作时。
 当程序需要同时进行多个套接字的操作时
 TCP服务程序同时处理正在侦听网络连接和已经连接好的套接字时。
 一个服务器程序同时使用TCP和UDP协议。
 一个服务器同时用多种服务并且服务协议不同如:inetd

 

4.信号驱动I/O模式
 使用信号,让内核在文件描述符就绪的时候使用SIGIO信号来通知我们的模式。
 首先需要允许套接字使用信号驱动I/O,安装SIGIO的函数.
 此程模式,系统调用立即返回,然后程序做其它事情,当数据准备就绪时,系统向进程发送SIGIO信号。在信号处理函数中进行I/O操作。

 图例如下:

 


 信号I/O可以使内核在某个文件描述符发生改变的时候发信号通知我们的程序。异步I/O可以提高我们程序进行I/O读写效率。通过使用它,当我们的程序进行I/O操作的时候,内核可以在初始化I/O操作后立即返回,在进行I/O操作的同时,我们的程序可以做其它事情,真到I/O结束,内核给我们的程序发送消息通知。
 应用实例:如果一个正在进行读写操作的TCP套接字处于信号驱动I/O状态下,那么每当新数据到达本地的时候,将会产生一个SIGIO信号,每当本地套接字发出的数据被远程确认后,也会产生一个SIGIO信号。对于我们的程序来讲,是无法区分这二个SIGIO有什么区别。在此种情况下使用SIGIO,TCP套接字应当被设置为无阻塞模式来阻止一个阻塞的read/write/recv/send操作。所以我们考虑一个只进行监听网络连接操作的套按字上使用异步I/O。

5.异步I/O模式
 当我们运行在异步I/O模式下,如果想进行I/O操作,只需要告诉内核我们要进行I/O操作,然后内核马上返回。具体的I/O和数据拷贝全部由内核来完成,我们程序继续执行。当内核完成所有的I/O操作,内核将通知我们程序。
 异步I/O和信号驱动I/O区别:
 信号驱动I/O模式下,内核在操作可以被操作的时候通知我们的应用程序发送SIGIO信号
 异步I/O模式下,内核在所有I/O操作的已经被内核操作结束后才会通知我们的应用程序。
 图例如下:


 上例中:当我们进行一个IO操作时,传递给内核的文件描述符,我们的缓存区指针和缓存区的大小,一

 个偏移量offset,以及在内核结束所有操作后和我们联系的方法。这种调用也是立即返回的,我们的程序

 不需要阻塞来等待数据的就绪。我们可以要求系统内核所有的操作结束后(包括从网络上读取信息,然后

 拷贝到我们提供给内核的缓存区中)给我们发一个消息。

 

套接字选项select()函数 <sys/time.h><sys/types.h><unistd.h>
int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,

 struct timeval * timeout); 用来等待文件描述词状态的改变。
 参数n代表readfds,writefds,exceptfds中fd集合中文件描述符中最大的文件描述符数字加1
 参数readfds 中的fd集合将由select来监视是否可以读取
 writefds 中的...是否可以写入
 exceptfds 中的...是否有例外发生
 如果你想知道是否可以从标准输入和一些套接字sockfd中读取数据,你就可以把文件描述符和sockfd加入readfds中。n的值设成readfds中文件描述符中最大的那个数字加一,也就是sockfd+1(因为标准输入的文件描述符值为0,所以其它任何的文件描述符都会比0值大)
 当select()函数返回时,readfds将会被修改用来告诉你哪一个文件描述符你可以用来读取数据。
 使用FD_ISSET()宏,你可以选出select()函数执行的结果。
 FD_CLR(inr fd, fd_set* set);用来清除描述词组set中相关fd 的位
 FD_ISSET(int fd, fd_set *set);用来测试描述词组set中相关fd的位是否为真
 FD_SET(int fd, fd_set*set);用来设置描述词组set中相关fd的位
 FD_ZERO(fd_set *set); 用来清除描述词组set的全部位
 参数timeout为结构timeval,用来设置select()的等待时间,其结构定义如下
 struct timeval{
 time_t tv_sec;
 time_t tv_usec;
 };
 在调用select()函数中,如果时间超过timeval参数所设置的时间长度,而还没有文件描述满足你的要

 求,那么select()函数将返回,允许继续程序下面的操作。
 当select()函数返回时,timeval中的时间将会被设置为执行为select()后还剩下的时间。
 注:如果timeval 设置为0, select立即返回,同时返回在你集合中的文件描述符状态。
 如果timeout参数为NULL,select函数进入阻塞状态,除了文件描述符的状态变化,否则select()

 不会返回。
 如果你的套接字描述符正在通过listen()函数侦听一个外来的网络连接,则你可以使用select函数来测

 试是否存在一个未经处理的新连接。
 返回值如果参数timeout设为NULL则表示select()没有timeout。
 错误代码执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过

 timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds

 和timeout的值变成不可预测。
 EBADF 文件描述词为无效的或该文件已关闭
 EINTR 此调用被信号所中断
 EINVAL 参数n 为负值。
 ENOMEM 核心内存不足
 范例常见的程序片段:
 fs_set readset;
 FD_ZERO(&readset);
 FD_SET(fd,&readset);
 select(fd+1,&readset,NULL,NULL,NULL);
 if(FD_ISSET(fd,readset){……}
 代码演示select
 #include<stdio.h><sys/time.h><sys/types.h><unistd.h>
 #define STDIN 0
 int main()
 {
 struct timeval tv;
 fd_set readfds;
 tv.tv_sec = 2;
 tv.tv_usec = 500000;
 FD_ZERO(&readfds);
 FD_SET(STDIN, &readfds);
 select(STDIN+1, &readfds, NULL, NULL, &tv);
 if(FD_ISSET(STDIN, &readfds)){
 printf("A key was pressed!\n");
 }
 else{
 printf("Timed out.\n");
 }
 }

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics