普通管道和命名管道

来源:互联网 发布:avio.pw 新域名 编辑:程序博客网 时间:2024/05/09 06:46


 

管道分为普通管道和命名管道;两者都是半双工的。
普通管道只能用于父子进程或兄弟进程间的通信,因为普通管道通过fork调用来拷贝文件描述符的,在文件系统中,普通管道并不对应物理文件。
命名管道在文件系统中有物理文件存在,因此可以用于非亲属的进程间通信。

1、普通管道

#include<unistd.h>

int pipe(int fd[2])
用于创建管道,创建后fd[0]和fd[1]在同一个进程用,再用fork系统调用产生子进程,父子进程一个关闭读端fd[0],另一个关闭写端fd[1],之后就可以用于通信了,用普通的I/O函数read、write、close即可。
如果管道输入端关闭,从管道读数据会以为读到了数据的末尾,读函数返回读到的字节数为0。
向管道写数据时,如果管道缓冲区有空闲,则会写入,否则一直阻塞。管道的缓冲区在内存,是有限的,一般为缓冲区分配一个页面的大小。如果管道读端关闭,则写数据会出错,产生信号SIFPIPE。

管道传输的是无格式的字节流,用管道通信时双方要约定好数据格式

在shell中使用|就是用了管道通信,如经常使用的grep操作。

[cpp] view plain copy
  1. #include<stdio.h>  
  2. #include<unistd.h>  
  3. #include<sys/types.h>  
  4. #include<errno.h>  
  5. char readbuf[100];  
  6. char writebuf[100];  
  7. int main()  
  8. {  
  9.     int fd[2];  
  10.     pid_t pid;  
  11.       
  12.     if(pipe(fd)<0)  
  13.         printf("create pipe error\n");  
  14.     if((pid=fork())<0){  
  15.         printf("fork error\n");  
  16.         exit(-1);  
  17.     }  
  18.     if(pid==0)//子进程  
  19.     {  
  20.         close(fd[0]);//关闭读端  
  21.           
  22.         strcpy(writebuf,"hello world");  
  23.         int num=write(fd[1],writebuf,strlen(writebuf));  
  24.         printf("child wirite %d bytes\n",num);  
  25.         close(fd[1]);  
  26.         printf("child close fd[1]\n");  
  27.         sleep(10);//等待父进程读  
  28.     }  
  29.     else if(pid>0)//父进程  
  30.     {  
  31.         close(fd[1]);//关闭写端  
  32.         sleep(5);//等待,确保子进程写入数据  
  33.         printf("\n");  
  34.         int num=read(fd[0],readbuf,100);  
  35.         printf("read data is %s\n",readbuf);  
  36.         close(fd[0]);  
  37.         exit(1);  
  38.     }  
  39.     return 1;  
  40.       
  41. }  

2、命名管道

命名管道以文件的形式存在于文件系统中,及时无亲缘关系的进程在系统中打开该文件即可通信。命令管道遵守FIFO先进先出,对命名管道读
总是从开始处返回数据,对它写则是添加到末尾,不支持lseek等操作。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode)
创建命名管道用mkfifo,第一个参数是路径,第二个参数是模式,与open相同。如果第一个参数是已存在的路径名,则会出错返回EEXIST。用I/O操作
函数即可操作命名管道,只是比普通管道多了个open操作。
命名管道不同于普通文件,命名管道打开时,要有两个进程,一个读一个写来打开。
如果当前进程打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。
如果当前进程打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)
在管道使用完后,用unlink删除管道。
fifo1.c建管道,写数据
[cpp] view plain copy
  1. #include<sys/types.h>  
  2. #include<sys/stat.h>  
  3. #include<errno.h>  
  4. #include<fcntl.h>  
  5. #define PATH "/tmp/namedpipe"  
  6. char buf[100];  
  7. int main()  
  8. {  
  9.     int fd;  
  10.     if(mkfifo(PATH,O_CREAT|O_EXCL)<0)  
  11.         printf("can't create namedpipo\n");  
  12.       
  13.     if((fd=open(PATH,O_WRONLY,0))!=-1)  
  14.         printf("open namedpipe success\n");  
  15.     strcpy(buf,"this is a test namedpipe program");  
  16.     int num=write(fd,buf,strlen(buf));  
  17.     if(num>0)  
  18.         printf("write data length is %d\n",num);  
  19.     return 1;  
  20. }  

fifo2.c读管道数据,之后删除管道
[cpp] view plain copy
  1. #include<sys/types.h>  
  2. #include<sys/stat.h>  
  3. #include<errno.h>  
  4. #include<fcntl.h>  
  5. #define PATH "/tmp/namedpipe"  
  6. char buf[100];  
  7. int main()  
  8. {  
  9.     int fd;  
  10.     fd=open(PATH,O_RDONLY,0);  
  11.     if(fd<0)  
  12.         printf("open namedpipe error\n");  
  13.     if(fd>0)  
  14.         printf("open named pipe success\n");  
  15.     int num=read(fd,buf,100);  
  16.     if(num>0){  
  17.         printf("read data length is %d\n",num);  
  18.         printf("read data is %s\n",buf);  
  19.     }  
  20.     unlink(PATH);  
  21.     return 1;  
  22. }  
0 0