Chinaunix首页 | 论坛 | 博客
  • 博客访问: 928821
  • 博文数量: 403
  • 博客积分: 27
  • 博客等级: 民兵
  • 技术积分: 165
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-25 22:20
文章分类

全部博文(403)

文章存档

2016年(3)

2015年(16)

2014年(163)

2013年(222)

分类: LINUX

2013-05-12 13:11:26

原文地址:关于I/O模型的一些研究 作者:bottles

UNIX操作系统下一共有5I/O模型,他们分别是:

1.      阻塞IO (Blocking I/O Model),如下图:

这个模型是I/O模型当中最简单的一种,阻塞很简单,直接调用read函数,一般平时使用的就是这个模型,kernel会自动完成等待数据跟取得数据2个步骤,如果用这个模型,只能处理单个I/O的情况。

代码如下:

服务器代码:


点击(此处)折叠或打开

  1. #include "unp.h"
  2. int main()
  3. {
  4.     int server_sockfd, client_sockfd;
  5.     int server_len, client_len;
  6.     struct sockaddr_un server_address;
  7.     struct sockaddr_un client_address;
  8. //删除之前留有的server_socket
  9.     unlink("server_socket");
  10. //建立一个本地的socket
  11.     server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
  12.     server_address.sun_family = AF_UNIX;
  13.     strcpy(server_address.sun_path, "server_socket");
  14.     server_len = sizeof(server_address);
  15. //将socket绑定
  16.     bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
  17. //监听server_sockfd,这里的第二个参数比较注意,在kernel2.2之后,第二个参数的含义就不再是对connect有限制了
  18. //他的含义是同时能进行的握手次数,这里就一般设置为5了。具体可以参考man 2 listen。
  19.     listen(server_sockfd, 5);
  20.     while(1)
  21.     {
  22.         char ch;
  23.         printf("server waiting\n");
  24.         client_len = sizeof(client_address);
  25.         //对客户机connect上来的fd进行握手,这里有个问题就是这里返回的client_sockfd跟客户机的fd是不一样的,
  26.         //这个函数是重新创建了一个新的socket并且用一个新的file descriptor来描述那个新创建的socket
  27.         client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
  28.         printf("i think client_sockfd is %d", client_sockfd);
  29.         //blocking model 直接read block用户进程,这样的话多个用户进程就不能用时获取数据了。
  30.         read(client_sockfd, & ch, 1);
  31.         ch++;
  32.         write(client_sockfd, &ch, 1);
  33.         close(client_sockfd);
  34.     }
  35. }

客户机代码:


点击(此处)折叠或打开

  1. #include "unp.h"
  2. int main()
  3. {
  4.     int sockfd;
  5.     int len;
  6.     struct sockaddr_un address;
  7.     int result;
  8.     int maxfdp1;
  9.     fd_set rset;
  10.     char ch = 'A';
  11.     address.sun_family = AF_UNIX;
  12.     printf("created socketfd\n");
  13.     strcpy(address.sun_path, "server_socket");
  14.     len = sizeof(address);
  15.     sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
  16.     result = connect(sockfd, (struct sockaddr *)&address, len);
  17.     if (result == -1)
  18.     {
  19.         perror("oops: client1");
  20.         exit(1);
  21.     }
  22.     printf("socket client %d", sockfd);
  23.     write(sockfd, &ch, 1);
  24.     read(sockfd, &ch, 1);
  25.     close(sockfd);

  26.     printf("char from server = %c\n", ch);
  27.     exit(0);
  28. }

2.      非阻塞IO (Nonblocking I/O Model)


这个模型是说,当I/O进行操作的时候,用read函数调用,但是kernel不会阻塞这个进程,而是直接返回一个值判断是否能够读到数据。如果能读到,就把数据从kernel拷贝到用户空间。

这个模型感觉实用性不大,因为用户必须不停的去询问kernel是否准备好了,这样的话也浪费cpu的占有率。

使用该模型前,首先要用fcntl函数把fd设置为O_NONBLOCK

3.      IO多路复用 (I/O Multiplexing Model)


这种模型在多个客户机的的情况下也可以使用,所以一般的大型工程都会使用这个模型,这个模型服务器在连接好之后,会在一开始调用select函数,询问多个描述符准备情况,如果有一个符合条件,则返回。这个模型可以搭配FD_ISSET()函数使用,这个函数判断给定的描述符是否在准备好的描述符的set里面。

android的zygote这个进程里也使用这个模型来进行IPC。

如下:


点击(此处)折叠或打开

  1. private static void runSelectLoopMode() throws MethodAndArgsCaller {
  2.         ArrayList<FileDescriptor> fds = new ArrayList();
  3.         ArrayList<ZygoteConnection> peers = new ArrayList();
  4.         FileDescriptor[] fdArray = new FileDescriptor[4];

  5.         fds.add(sServerSocket.getFileDescriptor());
  6.         peers.add(null);

  7.         int loopCount = GC_LOOP_COUNT;
  8.         while (true) {
  9.             int index;

  10.             /*
  11.              * Call gc() before we block in select().
  12.              * It's work that has to be done anyway, and it's better
  13.              * to avoid making every child do it. It will also
  14.              * madvise() any free memory as a side-effect.
  15.              *
  16.              * Don't call it every time, because walking the entire
  17.              * heap is a lot of overhead to free a few hundred bytes.
  18.              */
  19.             if (loopCount <= 0) {
  20.                 gc();
  21.                 loopCount = GC_LOOP_COUNT;
  22.             } else {
  23.                 loopCount--;
  24.             }


  25.             try {
  26.                 fdArray = fds.toArray(fdArray);
  27. //这个函数内部使用select,并使用多路复用I/0模型。
  28.                 index = selectReadable(fdArray);
  29.             } catch (IOException ex) {
  30.                 throw new RuntimeException("Error in select()", ex);
  31.             }

  32.             if (index < 0) {
  33.                 throw new RuntimeException("Error in select()");
  34.             } else if (index == 0) {
  35.                 ZygoteConnection newPeer = acceptCommandPeer();
  36.                 peers.add(newPeer);
  37.                 fds.add(newPeer.getFileDesciptor());
  38.             } else {
  39.                 boolean done;
  40.                 done = peers.get(index).runOnce();

  41.                 if (done) {
  42.                     peers.remove(index);
  43.                     fds.remove(index);
  44.                 }
  45.             }
  46.         }
  47. }

代码如下:

服务器代码:


点击(此处)折叠或打开

  1. #include "unp.h"
  2. int main()
  3. {
  4.     int server_sockfd, client_sockfd;
  5.     int server_len, client_len;
  6.     struct sockaddr_un server_address;
  7.     struct sockaddr_un client_address;
  8.     int result;
  9.     fd_set readfds, testfds;

  10.     unlink("server_socket");
  11.     server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
  12.     server_address.sun_family = AF_UNIX;
  13.     strcpy(server_address.sun_path, "server_socket");
  14.     server_len = sizeof(server_address);
  15.     bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
  16.     listen(server_sockfd, 5);
  17.     FD_ZERO(&readfds);
  18.     FD_SET(server_sockfd, &readfds);
  19.     while(1)
  20.     {
  21.         char ch;
  22.         int fd;
  23.         int nread;

  24.         testfds = readfds;
  25.         printf("server waiting\n");
  26.         result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, NULL);
  27.         if (result < 1)
  28.         {
  29.             perror("server 2");
  30.             exit(1);
  31.         }
  32.         for (fd = 0; fd < FD_SETSIZE; fd++)
  33.         {
  34.             //这个FD_ISSET函数的含义是判断fd是不是testfds这个集合里面select激活的那几个fd中的一个。
  35.             if (FD_ISSET(fd, &testfds))
  36.             {
  37.                 if ( fd == server_sockfd)
  38.                 {
  39.                     client_len = sizeof(client_address);
  40.                     client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
  41.                     FD_SET(client_sockfd, &readfds);
  42.                     printf("adding client on fd %d\n", client_sockfd);
  43.                 }
  44.                 else
  45.                 {
  46.                     ioctl(fd, FIONREAD, &nread);
  47.                     if ( nread == 0)
  48.                     {
  49.                         close(fd);
  50.                         FD_CLR(fd, &readfds);
  51.                         printf("removing client on fd %d\n", fd);
  52.                     }
  53.                     else
  54.                     {
  55.                         read(fd, &ch, 1);
  56.                         sleep(5);
  57.                         printf("serving client on fd %d\n", fd);
  58.                         ch++;
  59.                         write(fd, &ch, 1);
  60.                     }
  61.                 }
  62.             }
  63.         }
  64.     }
  65. }

客户机代码由于跟阻塞模型一致,所以就不写出了。

4.      信号驱动IO (Signal-Driven I/O Model)

信号驱动也不是真正的异步模型,他首先建立了一个SIGIO signal ,然后kernel会在数据准备好之后返回一个信号,这个时候用户进程调用read将从kernel的数据拷贝到用户进程当中。

严格来讲必须进行下列3步骤:(from unix network programming)

1. A signal handler must be established for the SIGIO signal.

2. The socket owner must be set, normally with the F_SETOWN command of fcntl.

3. Signal-driven I/O must be enabled for the socket, normally with the F_SETFL command of fcntl to turn on the O_ASYNC flag.

5.      异步IO (Asynchronous I/O Model)

异步IO这个模型是真正的异步模型,他直接调用了一个系统函数,然后等待kernel的数据准备好之后直接返回一个信号。用户进程接收到这个信号之后得到从kernel发来的数据。

这个模型不会让用户进程等待,所以他是真正的异步模型。

参考资料:网络


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