管道分为普通管道和命名管道;两者都是半双工的。
普通管道只能用于父子进程或兄弟进程间的通信,因为普通管道通过fork调用来拷贝文件描述符的,在文件系统中,普通管道并不对应物理文件。
命名管道在文件系统中有物理文件存在,因此可以用于非亲属的进程间通信。
用于创建管道,创建后fd[0]和fd[1]在同一个进程用,再用fork系统调用产生子进程,父子进程一个关闭读端fd[0],另一个关闭写端fd[1],之后就可以用于通信了,用普通的I/O函数read、write、close即可。
如果管道输入端关闭,从管道读数据会以为读到了数据的末尾,读函数返回读到的字节数为0。
向管道写数据时,如果管道缓冲区有空闲,则会写入,否则一直阻塞。管道的缓冲区在内存,是有限的,一般为缓冲区分配一个页面的大小。如果管道读端关闭,则写数据会出错,产生信号SIFPIPE。
fifo2.c读管道数据,之后删除管道
普通管道只能用于父子进程或兄弟进程间的通信,因为普通管道通过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操作。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<errno.h>
char readbuf[100];
char writebuf[100];
int main()
{
int fd[2];
pid_t pid;
if(pipe(fd)<0)
printf("create pipe error\n");
if((pid=fork())<0){
printf("fork error\n");
exit(-1);
}
if(pid==0)//子进程
{
close(fd[0]);//关闭读端
strcpy(writebuf,"hello world");
int num=write(fd[1],writebuf,strlen(writebuf));
printf("child wirite %d bytes\n",num);
close(fd[1]);
printf("child close fd[1]\n");
sleep(10);//等待父进程读
}
else if(pid>0)//父进程
{
close(fd[1]);//关闭写端
sleep(5);//等待,确保子进程写入数据
printf("\n");
int num=read(fd[0],readbuf,100);
printf("read data is %s\n",readbuf);
close(fd[0]);
exit(1);
}
return 1;
}
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错误(当前打开操作没有设置阻塞标志)
总是从开始处返回数据,对它写则是添加到末尾,不支持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建管道,写数据
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#define PATH "/tmp/namedpipe"
char buf[100];
int main()
{
int fd;
if(mkfifo(PATH,O_CREAT|O_EXCL)<0)
printf("can't create namedpipo\n");
if((fd=open(PATH,O_WRONLY,0))!=-1)
printf("open namedpipe success\n");
strcpy(buf,"this is a test namedpipe program");
int num=write(fd,buf,strlen(buf));
if(num>0)
printf("write data length is %d\n",num);
return 1;
}
fifo2.c读管道数据,之后删除管道
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#define PATH "/tmp/namedpipe"
char buf[100];
int main()
{
int fd;
fd=open(PATH,O_RDONLY,0);
if(fd<0)
printf("open namedpipe error\n");
if(fd>0)
printf("open named pipe success\n");
int num=read(fd,buf,100);
if(num>0){
printf("read data length is %d\n",num);
printf("read data is %s\n",buf);
}
unlink(PATH);
return 1;
}