当我们想要多次对描述符进行 read 时,多路转接技术能够满足该要求。I/O 多路转接技术首先构造一张有关描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行 I/O 时,该函数才返回,返回时,告诉进程哪些描述符已经准备好可以进行 I/O 操作。
/* IO多路转接*/ /* * 函数功能: * 返回值:准备就绪的描述符数,若超时则返回0,出错则返回-1; * 函数原型: */ #include <sys/select.h> int select(int maxfdpl, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tvptr); /* * 说明: * 参数maxfdpl是“最大描述符加1”; * 参数readfds、writefds、exceptfds是指向描述符集的指针,即描述符可读、可写或处于异常条件的; * 时间参数有三种取值: * tvptr == NULL; * 永远等待;若捕获到信号则中断此无限期等待;当所指定的描述符中的一个已准备好或捕获到信号则返回; * 若捕获到信号,则select返回-1,errno设置为EINTR; * * tvptr->tv_sec == 0 && tvptr->tv_usec == 0; * 完全不等待;测试所有描述符并立即返回,这是得到多个描述符的状态而不阻塞select函数的轮回方法; * * tvptr->sec != 0 || tvptr->usec != 0; * 等待指定的秒数和微妙数;当指定的描述符已准备好,或超过指定的时间立即返回; * 若超过指定的时间还没有描述符准备好,则返回0; * * tvptr的结构如下: */ struct timeval { long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ };在上面的函数当中,存在着 fd_set 的数据结构,我们可以通过以下函数对该数据结构进行处理:
#include <sys/select.h> int FD_ISSET(int fd, fd_set *fdset); //测试描述符fd是否在描述符集中设置;若fd在描述符集中则返回非0值,否则返回0 void FD_CLR(int fd, fd_set *fdset); //清除在fdset中指定的位fd; void FD_SET(int fd, fd_set *fdset); //设置fd在fdset中指定的位; void FD_ZERO(fd_set *fdset); //清除整个fdset;即所有描述符位都为0;
select 函数有三个可能的返回值:
/* * 函数功能:获取准备好的描述符数; * 返回值:准备就绪的描述符数,若超时则返回0,出错则返回-1; * 函数原型: */ #include <sys/select.h> int pselect(int maxfdpl, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *tsptr, const sigset_t *sigmask);
该函数与 select 函数类似,只是程序员接口不同。该函数不是为每个状态构造描述符集,而是构造一个 pollfd 结构数组,每个数组元素指定一个描述符编号以及对其所关心的状态。
/* * 函数功能:和select函数类似; * 函数原型: */ #include <poll.h> int poll(struct pollfd fdarray[], nfds_t nfds, int timeout); /* * 说明: * timeout == -1; 永远等待。 * timeout == 0; 不等待,测试所有的描述符并立即返回。 * timeout > 0; 等待timeout毫秒,当指定的描述符之一已经准备好,或指定的时间值已经超过时立即返回。 */
struct pollfd{ int fd; /* file descriptor to check,or <0 to ignore */ short events; /* events of interest on fd */ short revents; /* events that occurred on fd */ };
#include "apue.h" #include <sys/select.h> #include <sys/types.h> int main(void) { char rbuf[1024]; fd_set rd_fds; int ret,len; struct timeval tv; for(; ;) { FD_ZERO(&rd_fds); FD_SET(STDIN_FILENO,&rd_fds); tv.tv_sec = 5; tv.tv_usec = 0; ret = select(1,&rd_fds,NULL,NULL,&tv); if(ret < 0) { err_sys("select error"); break; } else if(ret == 0) printf("timeout,waiting next loop\n"); else { printf("ret = %d\n",ret); if(FD_ISSET(STDIN_FILENO,&rd_fds)) { len =read(STDIN_FILENO,rbuf,1023); rbuf[len] = '\0'; printf("Read buf are: %s\n",rbuf); } } } exit(0); }
ff ret = 1 Read buf are: ff ffh ret = 1 Read buf are: ffh timeout,waiting next loop timeout,waiting next loop ^C
《UNIX高级环境编程》