目录
一、管道
二、利用管道将写进程和都进程连接起来
三、命名管道
在Unix/Linux 中 命令行
cmd1 | cmd2 #例如 history | grep sqlplus
其中 cmd1 是 cmd2 是Linux中独立的程序 , 而 “ | ” 符号在Linux 称之为管道符。管道一端用来读另一端用来写。 把前一个进程的输出结果作为后一个进程的输入参数。例如
history | grep sqlplus 命令,前面一个是展示所有的历史命令 ,而后面一个是对输入中过滤出待关键字的sqlplus 的字符串。 两个合起来就是 展示所有的带”sqlplus“的历史命令。
在C程序中,方法 int flag = pipe(int pd[2]); 方法,输入参数是一个至少2个元素的int数组。用来创建两个文件描述符,这两个文件描述符链接同一个临时文件, pd[0]用于从管道中读数据, pd [1]用于从管道中写数据。 如果创建成功flag 值为0 否则为-1。.
注意:在创建pipe 后,其中的一个文件描述符 pd[0] 没有'w'权限,只有’r‘权限,而 pd[1] 没有'r'权限,只有’w‘权限。
对文件描述符和IO重定向不了解的可以看看:
https://blog.csdn.net/superSmart_Dong/article/details/118531682
在下例代码展示了用子进程和父进程利用管道读写数据的过程。由于子进程创建时会完全继承父进程的文件描述符。意味着父进程中存在pd[2],那么子进程也有一样的pd[2]。所以一个进程如果当作管道的写进程,那么它的读文件描述符就要先关闭,如果一个进程如果当作管道的读进程,那么它的写文件描述符就要关闭。
#include
#include
#include
#include
int pd[2] ,n,i;
char line[256];
int main(){
pipe(pd); //pd[1]用于写 , pd[0]用于读
printf("pd[ %d , %d ]",pd[0],pd[1]);
if(fork()){
//parent分支
printf("parent %d close pd[0] \n",getpid());
close(pd[0]);
i =0;
while( i++ <10){
//假设父进程向管道写入10次;
n = write(pd[1],"I AM YOUR PAPA",16);
printf("parent %d write %d byte to pipe\n",getpid(),n);
}
printf("parent %d exit\n",getpid());
}else{
//child 分支
printf("child %d close pd[1] \n",getpid());
close(pd[1]);
while( 1){
//管道读入诺干;
n = read(pd[0],line,128);
if(n){
printf(" child %d read %d byte from pipe\n :%s \n",getpid(),n,line);
}else{
exit(0);
}
}
}
}
输出结果可能不为一:其中一种输出结果如图。
在Linux 中当 sh 获取命令行 cmd1 | cmd2 时,会复刻出一个子进程sh。子进程sh会观察命令行中是否有 ”|“ 管道符号,把命令行划分为头部和尾部
然后子进程执行一下代码段:从而实现两个进程通过管道和重定向IO实现对子进程的标准输入的内容时父进程的标准输出的内容。
int pd[2];
pipe(pd);
int pid =fork();
if(pid){
close(pd[0]); //父进程用于从管道中写入数据,所以关闭读fd(pd[0])
close(1);
dup(pd[1]); //拿pd[1]复制出一个最小未使用的fd = 1,让标准输出指向管道pd[1]所指的物理文件
close(pd[1]); //关闭原来的pd[1]
exec(cmd1);
}else{
close(pd[1]); //子进程用于从管道中读入数据,所以关闭写fd(pd[1])
close(0);
dup(pd[0]); //拿pd[0]复制出一个最小未使用的fd =0,让标准输入指向管道pd[0]所指的物理文件
close(pd[0]); //关闭原来的pd[0]
exec(cmd2);
}
命名管道又叫做FIFO,在C语言中可以为管道命名。一个被赋予名字的管道。可以实现在两个独立进程或者两个项目工程中进行数据交互。命名管道在文件系统中以特殊文件的形式存在,不是临时文件,意味着,如果没有把命名管道文件进行 rm 或者 unlink 删除,则该管道会一致存在下去,除非储存介质损坏。
在 sh 中:创建命名管道的命令为:
mknod pipename p
在C程序中,创建命名管道的系统调用为
int r =mknod("pipename",S_IFIFO,0)
可以利用在命名管道实现两个独立进程的数据交互
/*********写管道进程*****************/
#include
#include
#include
#include
char *line = "name pipe message";
int main(){
int fd ;
mknod("mypipe",I_SFIFO,0);
fd = open("mypipe",O_WRONLY);
write(fd,line,strlen(line));
close(fd);
}
在写另外一个用于读管道的C程序
/*********读管道进程*****************/
#include
#include
#include
int main(){
char buf[128];
int fd = open("mypipe",O_RDONLY);
read(fd,buf,128);
printf("%s\n",buf);
close(fd);
}
对这两个C程序分别进行编译。如果管道没数据就读内容的话,得到的输入也是空的。就算进程生命结束,创建出来的命名管道依旧是存在的。