Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2123949
  • 博文数量: 229
  • 博客积分: 7217
  • 博客等级: 上校
  • 技术积分: 3224
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-19 17:23
个人简介

个人主页https://xugaoxiang.com,微信公众号: Dev_Club 或者搜索 程序员Club

文章分类

全部博文(229)

文章存档

2017年(1)

2016年(20)

2015年(23)

2013年(1)

2012年(23)

2011年(68)

2010年(62)

2009年(31)

分类: LINUX

2012-06-21 09:55:58

IO模型种类
    1、阻塞IO
    2、非阻塞IO
    3、IO多路复用
    4、信号驱动IO
    5、异步IO(AIO)

这里有必要知道,在socket编程中网络数据是如何被处理及传输的,以read操作为例,首先应用程序会告知内核要开始进行读操作,内核接到命令后会先创建文件描述符(file descriptor),等数据从网络传输到本地,经过网络层的包解析后得到真正的数据,此时内核会将数据缓存在文件描述符的读缓冲区中,因为文件描述符由内核创建,所以这里的缓冲区也是在内核中,最后将该缓冲区中的数据复制到应用程序的缓冲区。write操作也类似。

阻塞IO
    缺省状态下,一个套接字建立以后所处的模式就是阻塞IO模式。这种模式比较好理解,直到有数据到达本地系统调用才返回。

非阻塞IO
    和阻塞IO相对应,不管有没有数据到达系统调用都立即返回。当应用程序采用非阻塞IO模式时,需要使用一个循环操作来不停地测试文件描述符是否可读,这是一个极度浪费CPU资源的操作。

IO多路复用
    IO多路复用是通过调用select或poll函数来实现。和阻塞IO模式相比,把阻塞操作提前,即在调用select或poll时阻塞,后面的真正数据操作并不阻塞。多数复用的高明之处在于它能同时等待多个文件描述符,当文件描述符集中有一个进入就绪状态时,select函数就可以返回。

信号驱动IO
    当文件描述符就绪时,内核使用SIGIO信号通知, 这种模式就是信号驱动模式。在这种模式下,系统调用会立即返回,当数据就绪时,系统会发送SIGIO信号。 实现信号驱动IO需要三步:
    1、一个信号处理函数来进行IO操作(必须先于2和3操作)
    2、设定套接字的拥有者,通过fcntl函数的F_SETOWN参数来实现即fcntl(fd,F_SETOWN,getpid())
    3、套接字允许使用异步IO,通过fcntl函数的F_SETFL命令,O_ASYNC为参数来实现即fcntl(fd,F_SETFL,O_ASYNC)

异步IO
    在AIO下,只需要告诉内核要进行IO操作,内核会立刻返回,具体的IO操作全部由内核来完成,程序会继续往下执行,当IO操作结束后内核会通知程序。与信号驱动IO相比,内核通知的时间点不同,信号驱动IO是在数据就绪时通知,而AIO是IO操作结束时通知。

fcntl函数
    函数声明(manipulate file descriptor)

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <fcntl.h>

  3. int fcntl(int fd, int cmd, ... /* arg */ ); /*第二个参数是操作命令,如之前提到的设置阻塞非阻塞的命令F_SETFL,设置套接字属主的命令F_SETOWN,第三个参数是命令的参数,如前边提到的O_ASYNC*/
 
select函数
    函数声明

点击(此处)折叠或打开

  1. #include <sys/select.h>
  2. #include <sys/time.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>

  5. int select(int nfds, fd_set *readfds, fd_set *writefds,
  6.            fd_set *exceptfds, struct timeval *timeout);

  7. void FD_CLR(int fd, fd_set *set);  /*将文件描述符fd从集合set中删除*/
  8. int FD_ISSET(int fd, fd_set *set); /*判断文件描述符fd是否存在于集合set中*/
  9. void FD_SET(int fd, fd_set *set);  /*将fd加入set中*/
  10. void FD_ZERO(fd_set *set);         /*将集合清0*/
    select函数可以同时监视多个套接字,通过它可以知道哪个套接字可以读取数据,哪个可以写入数据,哪个出现错误。正常返回就绪描述符的数目,0为超时,-1为出错。

    nfds是readfds、writefds、exceptfds集合中文件描述符中最大的数字加1
    readfds为是否可读的fd集合
    writefds为是否可写的fd集合
    exceptfds为是否有例外发生的fd集合

    结构timeval

点击(此处)折叠或打开

  1. struct timeval
  2. {
  3.     int tv_sec;  /*秒数*/
  4.     int tv_usec; /*微秒数*/
  5. }
    当select函数返回时,timeval中的时间被设置为执行select后剩下的时间。
    有几点注意:
        1、linux的最小时间片是100微秒,如果tv_usec设得过小则毫无意义。
        2、timeval为0,select会立即返回
        3、timeval为NULL,select进入阻塞状态

    select代码示例

点击(此处)折叠或打开

  1. /*
  2.  * =====================================================================================
  3.  *
  4.  * Filename: test.c
  5.  * Description: 从stdin等待输入,等待时间1.5秒
  6.  * Version: 1.0
  7.  * Created: 06/21/2012 01:48:47 PM
  8.  * Revision: none
  9.  * Compiler: gcc
  10.  *
  11.  * Author: djstava , djstava@gmail.com
  12.  * Company: ABC Inc
  13.  *
  14.  * =====================================================================================
  15.  */
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <unistd.h>
  19. #include <sys/types.h>
  20. #include <sys/time.h>

  21. #define STDIN 0

  22. int main(int argc,char **argv)
  23. {
  24.     struct timeval tv;
  25.     fd_set readfds;

  26.     tv.tv_sec = 1;
  27.     tv.tv_usec = 500000;

  28.     FD_ZERO(&readfds);
  29.     FD_SET(STDIN,&readfds);

  30.     select(STDIN+1,&readfds,NULL,NULL,&tv);

  31.     if(FD_ISSET(STDIN,&readfds))
  32.         printf("Input from stdin!\n");
  33.     else
  34.         printf("Timed out!\n");
  35. }

阅读(5384) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~