管道文件、select

一、管道:

mkfifo 1.pipe        //创建管道文件。通过ll可以看到管道文件的文件类型是"p".ll还可以看到管道文件的文件大小为0.因为管道文件不是用
                              来存储数据的。
管道的特点:
1.管道式半双工的,一端读,另一端只能写。
2.双方使用同一个管道(即一方读,另一方写),一方打开管道之后,需要等待另一方打开,才能继续执行。
3.读端读取管道时,如果管道内没有数据,read就会阻塞,一直等着。
4.如果管道写端关闭,读端read时,会返回0.
   如果读端关闭,写端还继续向管道写数据,则写端程序会直接崩溃。

通过管道实现两个进程的通信:
步骤一:首先建立两个管道文件:
mkfifo 1.pipe;
mkfifo 2.pipe;
步骤二:为了同时编译两个进程,makefile可以这样写:
a:a.c b.c
        gcc a.c -o a
        gcc b.c -o b
.PHONY:clean
clean:
        rm -f a b
步骤三:编写a.c 与b.c,如下:
a.c                       
#include "func.h"
int main(int argc,char* argv[]){
        if(argc !=3){
                printf("error args\n");
                return -1;
        }
        int  fdw=open( argv[1],O_WRONLY);
        if(-1==fdw){
                perror("open");
                return -1;
        }
        int  fdr=open( argv[2],O_RDONLY);
        if(-1==fdr){
                perror("open1");
                return -1; 
        }
   //   printf("fdr=%d,fdw=%d\n",fdr,fdw);
        char buf[128];
        while(1){
                bzero(buf,sizeof(buf));
                 read(0,buf,sizeof(buf));   // 读取标准输入 
                 write(fdw,buf,strlen(buf)-1)//写 入管道,回车不要 
      
                bzero(buf,sizeof(buf));
                 read(fdr,buf,sizeof(buf));   //读取管道
                printf("%s\n",buf);         //把读到的内容写到屏幕
        }
        return 0;
}
b.c                          
#include "func.h"
int main(int argc,char* argv[]){
        if(argc !=3){
                printf("error args\n");
                return -1;
        }
        int  fdr=open( argv[1],O_RDONLY);
        if(-1==fdr){
                perror("open");
                return -1;
        }
        int  fdw=open( argv[2],O_WRONLY);
        if(-1==fdw){
                perror("open1");
                return -1;
        }
  //    printf("fdr=%d,fdw=%d\n",fdr,fdw);
        char buf[128];
        while(1){
                bzero(buf,sizeof(buf));
                 read(fdr,buf,sizeof(buf)); //读取管道
                 printf("%s\n",buf);

                bzero(buf,sizeof(buf));
                 read(0,buf,sizeof(buf)); //读取标准输入
                 write(fdw,buf,strlen(buf)-1); //写入管道      
        }
        return 0;
}

注意1:要防止死锁的情况。比如1号先打开管道1,再打开管道2,那么2号也要先打开管道1,再打开管道2。如果反了,就会死锁。
注意2:因为从标准输入缓冲区输入的时候会按回车,这个回车键不需要传送。所以write的长度是strlen(buf)-1
 
          以上的代码只能够实现,自己说一句话之后等待对方再说一句话,然后才能自己再说一句。为了实现自己可以一次说好几句话,可以使用下面的select函数进行改进。因为现在程序卡在两个地方(标准输入和管道),可以使用select来监控,帮我们判断哪一个描述符可用。

二、select函数(后面学习了epoll之后就不再使用select):  
select多路复用。虽然现在不用select了,因为又出现了升级版。但是还是要学,为了理解升级版的时候更好理解。
用select是为了监控多个读描述符,然后告诉我们。

#include <sys/select.h> 
#include <sys/time.h> 
int select(int maxfd, fd_set *readset,fd_set *writeset, fd_set *exceptionset, const struct timeval * timeout); 
参数:
maxfd:最大的描述符+1.
timeout:NULL表示永远等下去。
fd_set readset;
FD_ZERO(&readset);      //将集合清零。
FD_SET(0,&readset);   //增加一个fd
FD_SET(fdr,&readset);
FD_CLR(fdr,&readset);  //删除一个id
int ret=select(fdr+1,&readset,NULL,NULL,NULL);   //执行时select会将readset中的数据读走,清空readset,等到fdr或者0可读了,
                       会把fdr或0填入readset。
对步骤三的改进(通过select函数对以上进程通信的改进):
a.c                      
#include "func.h"
int main(int argc,char* argv[]){
        if(argc !=3){
                printf("error args\n");
                return -1;
        }
        int  fdw=open( argv[1],O_WRONLY);
        if(-1==fdw){
                perror("open");
                return -1;
        }
        int  fdr=open (argv[2],O_RDONLY);
        if(-1==fdr){
                perror("open1");
                return -1;
        }
        printf("fdr=%d,fdw=%d\n",fdr,fdw);
        char buf[128];
        fd_set rdset;
        int ret;
        int ret1;s
        while(1){
                 FD_ZERO(&rdset);    //将集合中rsf所有fd清零 
                 FD_SET(0,&rdset);    //增加一个fd 
                 FD_SET(fdr,&rdset);
                ret= select(fdr+1,&rdset,NULL,NULL,NULL);
                if(ret >0){                   / /说明有可读的 
                        if( FD_ISSET(0,&rdset)){ //判断该fd是否有设置
                                bzero(buf,sizeof(buf));
                                read(0,buf,sizeof(buf));//读取标准输入
                                write(fdw,buf,strlen(buf)-1);//写入管道      
                        }
                        if( FD_ISSET(fdr,&rdset)){      
                                bzero(buf,sizeof(buf));
                                ret1=read(fdr,buf,sizeof(buf));//读取管道
                                if(ret1 == 0){     //防止写端终止,一直刷0
                                        printf("b is over\n");
                                        break;
                                }
                                printf("%s\n",buf);
                        }
                }
        }
        return 0;
}
b.c               
#include "func.h"
int main(int argc,char* argv[]){
        if(argc !=3){
                printf("error args\n");
                return -1;
        }
        int  fdr=open( argv[1],O_RDONLY);
        if(-1==fdr){
                perror("open");
                return -1;
        }
        int  fdw=open( argv[2],O_WRONLY);
        if(-1==fdw){
                perror("open1");
                return -1;
        }
        printf("fdr=%d,fdw=%d\n",fdr,fdw);
        char buf[128];
        fd_set rdset;
        int ret;
        int ret1;
        while(1){
                FD_ZERO(&rdset);
                FD_SET(0,&rdset);
                FD_SET(fdr,&rdset);
                ret= select(fdr+1,&rdset,NULL,NULL,NULL);
                if(ret >0){
                        if( FD_ISSET(0,&rdset)){
                                bzero(buf,sizeof(buf));
                                read(0,buf,sizeof(buf));//读取标准输入
                                write(fdw,buf,strlen(buf)-1);//写入管道      
                        }
                        if( FD_ISSET(fdr,&rdset)){      
                                bzero(buf,sizeof(buf));
                                ret1=read(fdr,buf,sizeof(buf));//读取管道
                                if(0==ret1){
                                        printf("a is over\n");
                                        break;
                                }
                                printf("%s\n",buf);
                        }
                }
        }
        return 0;
}
注:如果写端终止了,读端会一直返回0.为了防止一直在刷0,需要有个判断。

while(1){
  FD_ZERO(&readset);
  FD_SET(0,&readset);
  FD_SET(fdr,&readset);                 //每次循环的时候都要重新设置,因为上一次返回的不知道是什么了。
  bzero(&t,sizeof(t));
  t.tv_sec=3;                    //监控三秒钟,会打印出"--------".一定要在这里重新定义3秒钟。因为执行完了之后,时间会清零
  ret=select(fdr+1,&readset,NULL,NULL,&t);       //最后的&t一般写NULL,很少会用到写时间的情况。
  if(ret>0){  
    if(FD_ISSET(0,&readset)){
    }
    if(FD_ISSET(fdr,&readset)){
      bzero(buf,sizeof(buf));
      ret1=read(fdr,buf,sizeof(buf));
      if(0==ret1){                    
        printf("b is over");
        break;
      }
      printf("%s\n",buf);
    }
  }
  printf("-----------------------------");
}



猜你喜欢

转载自blog.csdn.net/pengchengliu/article/details/80501557