1.匿名管道pipe
2.命名管道mkfifo
1.匿名管道pipe
- 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
- 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
- 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中;
- 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
(1) 测试一下管道的大小:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int fd[2];
int count = 0;
if(pipe(fd)<0)
{
perror("Fail to create pipe");
exit(EXIT_FAILURE);
}
while(1)
{
write(fd[1],"a",sizeof(char));
printf("count = %d.\n",++count);
}
return 0;
}
结果如下:
单独创建一个无名管道,并没有实际的意义。我们一般是在一个进程在由pipe()创建管道后,一般再由fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)
(2)从管道中读写数据
A.通过标准输入stdin给管道里输入数据并且通过标准输出读取管道中的数据
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#define N 10
#define MAX 100
int child_read_pipe(int fd)
{
char buf[N];
int n = 0;
while(1)
{
n = read(fd,buf,sizeof(buf));//从fd文件中读取(整个buf的size:sizeof(buf))数据到buf
buf[n] = '\0'; //sizeof(数组名)计算的是整个数组的大小
printf("read %d bytes:%s.\n",n,buf);
if(strncmp(buf,"quit",4)==0)//如果读到的是quit 则结束循环
break;
}
return 0;
}
int parent_write_pipe(int fd)
{
char buf[MAX] = {0};
while(1)
{
printf(">");
fgets(buf,sizeof(buf),stdin);//从stdin(标准输入文件(键盘输入))读取数据到buf
buf[strlen(buf)-1] = '\0'; 给读取完的buf最后的字符加上\0,作为字符串的结束标志,用于strlen(buf)
write(fd,buf,strlen(buf));//将buf(实际大小strlen(buf))的数据写入到fd文件中去
usleep(500);
if(strncmp(buf,"quit",4)==0)//如果写入的是quit 则结束循环
break;
}
return 0;
}
int main()
{
int pid;
int fd[2];
if(pipe(fd)<0)
{
perror("Fail to pipe");
exit(EXIT_FAILURE);
}
if((pid = fork())<0)
{
perror("Fail to fork");
exit(EXIT_FAILURE);
}
else if(pid == 0)
{
close(fd[1]);//子进程用于读,则关闭写段
child_read_pipe(fd[0]);
}
else
{
close(fd[0]);//父进程用于写,则关闭读端
parent_write_pipe(fd[1]);
}
exit(EXIT_SUCCESS);
}
输出结果:
从以上验证我们可以看到:
<1>当写端存在时,管道中没有数据时,读取管道时将阻塞
<2>当读端请求读取的数据大于管道中的数据时,此时读取管道中实际大小的数据
<3>当读端请求读取的数据小于管道中的数据时,此时放回请求读取的大小数据
B.通过读取文件中的数据到管道,并且通过管道输出到另一个新文件中去
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX 100
int child_work(int pfd,char *fname)//创建文件fname,并且从管道pfd读数据到该文件中去
{
int n,fd;
char buf[MAX];
if((fd = open(fname,O_WRONLY | O_CREAT | O_TRUNC,0666)) < 0)//创建新文件并打开
{
fprintf(stderr,"Fail to open %s : %s.\n",fname,strerror(errno));
return -1;
}
while( n = read(pfd,buf,sizeof(buf)) )//将数据从管道读到buf中
{
write(fd,buf,n);//将buf中的数据写入到fd文件中去
}
close(pfd);
close(fd);
return 0;
}
int father_work(int pfd,char *fname)//从fname文件中读取数据到管道pfd
{
int fd,n;
char buf[MAX];
if((fd = open(fname,O_RDONLY)) < 0)//打开已经存在的fname文件
{
fprintf(stderr,"Fail to open %s : %s.\n",fname,strerror(errno));
return -1;
}
while(n = read(fd,buf,sizeof(buf)))//从fname文件中读取数据到buf中
{
write(pfd,buf,n);//将buf中的数据写入到pfd管道中
}
close(pfd);
close(fd);
return 0;
}
int main()
{
int pid;
int fd[2];
if(pipe(fd) < 0)
{
perror("Fail to pipe");
exit(EXIT_FAILURE);
}
if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(EXIT_FAILURE);
}else if(pid == 0){
close(fd[1]);
child_work(fd[0],"pipe_read");
}else{
close(fd[0]);
father_work(fd[1],"pipe_write");
wait(NULL);
}
exit(EXIT_SUCCESS);
}
//注意:文件中使用了两个buf:是管道和文件链接的媒介
输出结果:
2.命名管道mkfifo
-
匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
-
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
-
命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
$ mkfifo filename
- 命名管道也可以从程序里创建,相关函数有:
*int mkfifo(const char filename,mode_t mode);
A.程序使用mkfifo函数创建一个命名管道文件test.fifo,将Makefile 的文件都读取到test.fifo文件中
mkfifo_write.c
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<sys/stat.h>
int main()
{
//管道操作
umask(0);
int ret;
ret = mkfifo("./test.fifo",0664);
if(ret < 0)
{
perror("mkfifo error");
exit(EXIT_FAILURE);
}
//管道文件操作
int fd;
printf("start open ----------\n");
fd = open("./test.fifo",O_WRONLY,0664);
printf("end open ----------\n");
if(fd < 0)
{
perror("open error");
exit(EXIT_FAILURE);
}
printf("./test.fifo open success\n");
//管道写数据
while(1)
{
char buf[10] = {0};
printf("I say:");
fflush(stdout);
scanf("%s",buf);
//write("./test.fifo",buf,strlen(buf));
write(fd,buf,strlen(buf));
}
close(fd);
return 0;
}
mkfifo_read.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
int main()
{
umask(0);
int ret = mkfifo("./test.fifo", 0664);
if (ret < 0) {
if (errno != EEXIST) {
perror("mkfifo error");
return -1;
}
}
printf("start open -------\n");
int fd = open("./test.fifo", O_RDONLY);
printf("end open -------\n");
if (fd < 0) {
perror("open error");
return -1;
}
printf("fifo:%s open success!!\n", "./test.fifo");
while(1) {
sleep(5);
char buf[10] = {0};
read(fd, buf, 9;
printf("peer say: %s\n", buf);
}
close(fd);
return 0;
}
写入端输出结果:
输出端输出结果:
B.创建管道将一个文件中的数据拷贝到另一个文件中:
(1) 首先通过命令创建管道(也可通过函数mkfifo)
(2) 写两个文件:mkfifo_read.c 和 mkfifo_write.c
mkfifo_read.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
//这个代码是 将makefile文件里的数据写入到管道文件(test.fifo)中去
int main()
{
//以只读的方式打开makefile文件
int infd;
infd = open("makefile",O_RDONLY);
if(infd == -1)
{
perror("open error");
exit(EXIT_FAILURE);
}
printf("open makefile success\n");
//以只写的方式打开管道test.fifo
int outfd;
outfd = open("test.fifo",O_WRONLY);
if(outfd == -1)
{
perror("open error");
exit(EXIT_FAILURE);
}
printf("open test.fifo success\n");
//通过字符数组buf,将文件makefile里的数据写入管道
char buf[1024];
int n;
while((n = read(infd,buf,1023))>0)
{
write(outfd,buf,strlen(buf));
}
//最后别忘记关闭两个文件:makefile文件 和 管道文件
close(infd);
close(outfd);
return 0;
}
mkfifo_write.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/stat.h>
#include<signal.h>
#include<errno.h>
//这个代码是 将管道(test.fifo)中的文件写入到新文件makefile2中去
int main()
{
umask(0);
//首先以可写方式创建并打开新文件makefile2
int outfd;
outfd = open("makefile2",O_WRONLY|O_CREAT|O_TRUNC,0664);
if(outfd == -1)
{
perror("open error");
exit(EXIT_FAILURE);
}
printf("create makefile2 success\n");
//然后以可读方式打开管道
int infd;
infd = open("test.fifo",O_RDONLY);
if(infd == -1)
{
perror("open error");
exit(EXIT_FAILURE);
}
printf("open test.fifo success\n");
//最后借助字符数组buf,将管道中的数据写入到makefile2文件中
char buf[1024];
int n;
while((n = read(infd,buf,1023))>0)
{
write(outfd,buf,strlen(buf));
}
//关闭两个文件:makefile2文件 和 管道文件
close(infd);
close(outfd);
unlink("test.fifo");
return 0;
}
(3) 再写一个makefile文件
all: mkfifo_write mkfifo_read
mkfifo_write:mkfifo_write.c
gcc $^ -o $@
mkfifo_read:mkfifo_read.c
gcc $^ -o $@
(4) 然后运行读取程序 会阻塞在这一步
(5) 阻塞时在另一个终端运行写入文件程序
原终端读取程序不再阻塞,运行成功
文件通过管道拷贝成功