《unix高级环境编程》高级 I/O—— I/O 多路转接

来源:互联网 发布:谜一样的双眼知乎 编辑:程序博客网 时间:2024/06/08 03:17

 当我们想要多次对描述符进行 read  时,多路转接技术能够满足该要求。I/O 多路转接技术首先构造一张有关描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行 I/O 时,该函数才返回,返回时,告诉进程哪些描述符已经准备好可以进程 I/O 操作。

select 和 pselect 函数


[cpp] view plaincopy
  1. /* IO多路转接*/  
  2.   
  3. /* 
  4.  * 函数功能: 
  5.  * 返回值:准备就绪的描述符数,若超时则返回0,出错则返回-1; 
  6.  * 函数原型: 
  7.  */  
  8. #include <sys/select.h>  
  9. int select(int maxfdpl, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tvptr);  
  10. /* 
  11.  * 说明: 
  12.  * 参数maxfdpl是“最大描述符加1”; 
  13.  * 参数readfds、writefds、exceptfds是指向描述符集的指针,即描述符可读、可写或处于异常条件的; 
  14.  * 时间参数有三种取值: 
  15.  * tvptr == NULL; 
  16.  *      永远等待;若捕获到信号则中断此无限期等待;当所指定的描述符中的一个已准备好或捕获到信号则返回; 
  17.  *      若捕获到信号,则select返回-1,errno设置为EINTR; 
  18.  * 
  19.  * tvptr->tv_sec == 0 && tvptr->tv_usec == 0; 
  20.  *      完全不等待;测试所有描述符并立即返回,这是得到多个描述符的状态而不阻塞select函数的轮回方法; 
  21.  * 
  22.  * tvptr->sec != 0 || tvptr->usec != 0; 
  23.  *      等待指定的秒数和微妙数;当指定的描述符已准备好,或超过指定的时间立即返回; 
  24.  *      若超过指定的时间还没有描述符准备好,则返回0; 
  25.  * 
  26.  * tvptr的结构如下: 
  27.  */  
  28. struct timeval  
  29. {  
  30.     long tv_sec;    /* seconds */  
  31.     long tv_usec;   /* and microseconds */  
  32. };  
        在上面的函数当中,存在着 fd_set 的数据结构,我们可以通过以下函数对该数据结构进行处理:

[cpp] view plaincopy
  1. #include <sys/select.h>  
  2. int  FD_ISSET(int fd, fd_set *fdset); //测试描述符fd是否在描述符集中设置;若fd在描述符集中则返回非0值,否则返回0  
  3. void FD_CLR(int fd, fd_set *fdset); //清除在fdset中指定的位fd;  
  4. void FD_SET(int fd, fd_set *fdset); //设置fd在fdset中指定的位;  
  5. void FD_ZERO(fd_set *fdset); //清除整个fdset;即所有描述符位都为0;  

       声明了一个描述符集后,必须使用 FD_ZERO 清空其所有位达到初始化,然后才可以设置各个位;从 select 返回时,使用 FD_ISSET 测试该集中的一个给定位是否仍旧设置;

      select 函数有三个可能的返回值:

  1. 返回值-1表示出错。这种情况下,将不修改其中任何描述符集。
  2. 返回值0表示没有描述符准备好。若指定的描述符都没有准备好,而且指定的时间已经超过,则发生这种情况。此时描述符集都被清0.
  3. 正返回值表示已经准备好的描述符数,该值是三个描述符集中已准备好的描述符之和。三个描述符集中仍旧打开的位对应与已准备好的描述符。
      对于准备好的意思要做一些更具体的说明:
  1. 若对读集 readfds 中的一个描述符的 read 操作将不会阻塞,则此描述符是准备好的。
  2. 若对写集 writefds 中的一个描述符的 write 操作将不会阻塞,则此描述符是准备好的。
  3. 若异常状态集 exceptfds 中的一个描述符有一个未决异常状态,则此描述符时准备好的。
  4. 对于读、写和异常状态,普通文件描述符总是返回准备好的。
[cpp] view plaincopy
  1. /* 
  2.  * 函数功能:获取准备好的描述符数; 
  3.  * 返回值:准备就绪的描述符数,若超时则返回0,出错则返回-1; 
  4.  * 函数原型: 
  5.  */  
  6. #include <sys/select.h>  
  7. int pselect(int maxfdpl, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *tsptr,  
  8.         const sigset_t *sigmask);  

       pselect与select有以下几个不同:
  1. select 的超时用 timeval 结构指定,pselect 使用 timespec 结构。
  2. pselect 的超时值被申明为 const,这保证了调用 pselect 不会改变此值。
  3. 对于 pselect 可使用一个可选择的信号屏蔽字。若 sigmask 为空,那么在于信号有关的方面,pselect 的运行状况和 select 相同。否则,sigmask指向一个信号屏蔽字,在调用pselect时,以原子操作的方式安装该信号屏蔽字,在返回时回复以前的信号屏蔽字。

poll 函数

        该函数与 select 函数类似,只是程序员接口不同。该函数不是为每个状态构造描述符集,而是构造一个 pollfd 结构数组,每个数组元素指定一个描述符编号以及对其所关心的状态。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * 函数功能:和select函数类似; 
  3.  * 函数原型: 
  4.  */  
  5. #include <poll.h>  
  6. int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);  
  7. /* 
  8.  * 说明: 
  9.  * timeout == -1;   永远等待。 
  10.  * timeout == 0;    不等待,测试所有的描述符并立即返回。 
  11.  * timeout > 0;     等待timeout毫秒,当指定的描述符之一已经准备好,或指定的时间值已经超过时立即返回。 
  12.  */  

pollfd 结构数组如下:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. struct pollfd{  
  2. int fd; /* file descriptor to check,or <0 to ignore */  
  3. short events; /* events of interest on fd */  
  4. short revents;  /* events that occurred on fd */  
  5. };   

测试程序:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include "apue.h"  
  2. #include <sys/select.h>  
  3. #include <sys/types.h>  
  4.   
  5. int main(void)  
  6. {  
  7.     char rbuf[1024];  
  8.     fd_set rd_fds;  
  9.     int ret,len;  
  10.     struct timeval tv;  
  11.   
  12.     for(; ;)  
  13.     {  
  14.         FD_ZERO(&rd_fds);  
  15.         FD_SET(STDIN_FILENO,&rd_fds);  
  16.         tv.tv_sec = 5;  
  17.         tv.tv_usec = 0;  
  18.         ret = select(1,&rd_fds,NULL,NULL,&tv);  
  19.         if(ret < 0)  
  20.         {  
  21.             err_sys("select error");  
  22.             break;  
  23.         }  
  24.         else if(ret  == 0)  
  25.             printf("timeout,waiting next loop\n");  
  26.         else  
  27.         {  
  28.             printf("ret = %d\n",ret);  
  29.             if(FD_ISSET(STDIN_FILENO,&rd_fds))  
  30.             {  
  31.                 len =read(STDIN_FILENO,rbuf,1023);  
  32.                 rbuf[len] = '\0';  
  33.                 printf("Read buf are: %s\n",rbuf);  
  34.             }  
  35.         }  
  36.     }  
  37.     exit(0);  
  38. }  

该函数是实现每个5秒钟,从标准输入读取数据;若超过5秒才写数据,则会返回0;输出结果:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ff  
  2. ret = 1  
  3. Read buf are: ff  
  4.   
  5. ffh  
  6. ret = 1  
  7. Read buf are: ffh  
  8.   
  9. timeout,waiting next loop  
  10. timeout,waiting next loop  
  11. ^C  
0 0
原创粉丝点击