程序期待着来自两个源端的输入,但不知道哪个源端的输入先到。如果程序试图从源端A读入,而实际上只有来自源端B的输入可用,那么程序就会发生阻塞,怎么办?
保持阻塞状态,直到一组条件中至少有一个条件为真为止,这种方法称为 或同步(OR synchronization)。
1.监视多个文件描述符的一种方法是为每个描述符分别使用一个独立的进程
下面程序有两个命令行参数,即两个要监视的文件的名字,父进程在创建子进程之前将两个文件都打开,父进程监视第一个文件描述符,子进程监视第二个文件描述符。每个进程都将它对应的文件内容送到标准输出中去,如果监视两个命名管道,输入可用时则会出现输出。
- #include <errno.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- int main(int argc,char *argv[]){
- int bytesread;
- int childpid;
- int fd,fd1,fd2;
- if(argc!=3){
- fprintf(stderr,"Usage : %s filel file2 .\n",argv[0]);
- return 1;
- }
- if( (fd1=open(argv[1],O_RDONLY)) == -1){
- fprintf(stderr,"Failed open file %s .\n",argv[1]);
- return 1;
- }
- if( (fd2=open(argv[2],O_RDONLY)) == -1){
- fprintf(stderr,"Failed open file %s .\n",argv[2]);
- return 1;
- }
- if( (childpid=fork()) == -1){
- perror("Faile to create child process.");
- return 1;
- }
- if(childpid>0){//parent code
- fd=fd1;
- }
- else{
- fd=fd2;
- }
- bytesread=copyfile(STDOUT_FILENO,fd);
- return 0;
- }
2.select在单进程中监视两个文件描述符
用独立的进程来监视两个文件描述符可能很有用,但是这两个进程都有独立的地址空间,因此他们的交互变得非常困难。
select函数提供了一种在单个进程中监视多个文件描述符的方法。它可以对三种可能的情况进行监视——可以无阻塞地进行读操作,可以无阻塞地进行写操作,或者有挂起的错误情况的文件描述符。老版本的UNIX在sys/time.h中定义了select方法,但在POSIX是在sys/select.h
- #include <sys/select.h>
- int select(int nfds,
- fd_set *restrict readfds,
- fd_set *restrict writefds,
- fd_set *restrict errorfds,
- struct timeval *restrict timeout);
select参数:
select返回:nfds 给出了要监视文件描述符的范围,nfds的值必须至少要比要监视的文件描述符的最大值大1。
readfds 指定了为读操作监视的文件描述符集。
writefds 指定了为写操作监视的文件描述符集。
errorfds 指定了为错误情况监视的文件描述符集。
readfds,writefds,errorfds中任何一个都可能为NULL,在这种情况下select不为相应的事件监视描述符
timeout是一个超时值,经过一段特定的时间后,即使没有准备就绪的描述符,它也会迫使select返回,当timeout为NULL时,select可能会无限期地阻塞。
成功返回时,除了那些已经准备就绪的描述符之外,select清空readfds,writefds,errorfds中所有描述符,并返回已准备就绪的文件描述符的数目。
如果不成功,返回-1并设置errno。
历史上,系统将描述符集作为整数比特掩码来实现,但在大多数的系统中,当文件描述符多于32个时,这中实现将不能正常工作。现在通常用整数数组中的比特域来表示描述符集,用宏FD_SET,FD_CLR,FD_ISSET,FD_ZERO以一种独立于实现的方式来操作描述符集。
调用select之前,先用这三个宏来设置描述符掩码,在select返回之后用FD_ISSET来测试掩码中对应于文件描述符fd的那个比特位是否被设置
- void FD_CLR(int fd,fd_set *fdset);
- int FD_ISSET(int fd,fd_set *fdset);
- void FD_SET(int fd,fd_set *fdset);
- void FD_ZERO(fd_set *fdset);
FD_SET设置了×fdset中对应于文件描述符fd的那个比特位FD_CLR清除了相应的比特位
FD_ZERO清除了×fdset中的所有比特位
- #include <sys/select.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <string.h>
- #include <stdio.h>
- int main(int argc,char *argv[]){
- int fd,fd1,fd2;
- if(argc!=3){
- fprintf(stderr,"Usage : %s filel file2 .\n",argv[0]);
- return 1;
- }
- if( (fd1=open(argv[1],O_RDONLY)) == -1){
- fprintf(stderr,"Failed open file %s .\n",argv[1]);
- return 1;
- }
- if( (fd2=open(argv[2],O_RDONLY)) == -1){
- fprintf(stderr,"Failed open file %s .\n",argv[2]);
- return 1;
- }
- fd=whichisready(fd1,fd2);
- if(fd==fd1){
- printf("The %s is ready.\n",argv[1]);
- }
- else if(fd==fd2){
- printf("The %s is ready.\n",argv[2]);
- }
- return 0;
- }
- int whichisready(int fd1,int fd2){
- int maxfd;
- int nfds;
- fd_set readset;
- if( (fd1<0)||(fd1>=FD_SETSIZE)||(fd2<0)||(fd2>FD_SETSIZE) ){
- errno=EINVAL;
- return -1;
- }
- maxfd=(fd1>fd2)?fd2:fd2;
- FD_ZERO(&readset);
- FD_SET(fd1,&readset);
- FD_SET(fd2,&readset);
- nfds=select(maxfd+1,&readset,NULL,NULL,NULL);
- if(nfds==-1){
- return -1;
- }
- if( FD_ISSET(fd1,&readset) ){
- return fd1;
- }
- if( FD_ISSET(fd2,&readset) ){
- return fd2;
- }
- errno=EINVAL;
- return -1;
- }
whichisready存在问题:
1.只能返回一个值
2.当两个文件描述符都准备好时优先返回第一个文件描述符