管道
管道是一种队列型的数据结构,它的数据从一端输入,另一端输出。管道最常见应用就是连接两个进程的输入和输出,将一个进程的输入作为另一个进程的输出。
shell中有专门的管道运算符|
,常用于检索内容,如:find | grep str
无名管道
无名管道一般直接称为管道,占用两个文件描述符,只能被具有亲缘关系的进程共享,一般是指父子进程
无名管道的建立
UNIX中一切皆是文件,管道也是文件中的一种。当系统创建一个管道时,它返回两个文件描述符,分别用作输出端(读)和输入端(写);
在UNIX中,使用pipe
函数来创建管道:
头文件:#include <unistd.h>
声明:int pipe(int __pipedes[2])
功能:创建单向通信通道(管道)。如果成功,两个文件描述符将存储在__pipedes[2]
中。一般写在__pipedes[1]
上的字节可以从__pipedes[0]
读取。
返回值:成功返回0;否则返回-1;
单向管道
通常管道的两端分别被两个不同的进程控制,这样两个进程就能够进行通信。控制输入端的进程向管道发送信息,控制输出端的进程从管道中读取信息。
在父进程创建管道并产生子进程之后,父子进程就都拥有管道两端的访问权。此时通过控制父子进程中管道两端开闭,就能够实现父子进程之间的单向通信;例:
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2];
pid_t fpid;
char buff[20];
// 创建管道,实现父进程向子进程发送信息
if (pipe(fd) < 0) {
printf("create Pipe error !!\n");
}
// 创建子进程
fpid = fork();
if (fpid < 0) {
printf("fork error !!\n");
} else if (fpid == 0) {
// 子进程关闭管道输入端
close(fd[1]);
// 子进程从管道输出端读取
read(fd[0], buff, 20);
printf("%s\n", buff);
} else {
// 父进程关闭管道输出端
close(fd[0]);
// 父进程从输入端写入
write(fd[1], "hello world\n", 12);
}
return 0;
}
若想要实现子进程向父进程发送信息,将父子进程中代码内容交换即可(即更改输入输出端);
双向管道
管道是进程间的单向交流方式,如果要实现双向通信就需要使用两个管道。
具体使用方式如下:
#include <stdio.h>
#include <unistd.h>
int main() {
int fd1[2], fd2[2];
pid_t fpid;
char buff[20];
// 创建管道1和管道2
if (pipe(fd1) < 0 || pipe(fd2) < 0) {
printf("create Pipe error !!\n");
}
// 创建子进程
fpid = fork();
if (fpid < 0) {
printf("fork error !!\n");
} else if (fpid == 0) {
// 子进程关闭管道1输入端,从管道1输出端读取并打印
close(fd1[1]);
read(fd1[0], buff, 20);
printf("[child] %s\n", buff);
// 子进程关闭管道2输出端,从管道2输入端写入
close(fd2[0]);
write(fd2[1], "child information!\n", 20);
} else {
// 父进程关闭管道1输出端,从管道1输入端写入
close(fd1[0]);
write(fd1[1], "parent information!\n", 20);
// 父进程关闭管道2输入端,从管道2输出端读取并打印
close(fd2[1]);
read(fd2[0], buff, 20);
printf("[parent] %s\n", buff);
}
return 0;
}
运行结果:
[child] parent information!
[parent] child information!
连接I/O管道模型
通过使用dup2
函数可以对标准输入、输出、错误进行重定向,例如:
dup2(fd1,0)//复制文件描述符fd1到文件描述符0即可重定向标准输入
dup2(fd2,1)//复制文件描述符fd2到文件描述符1即可重定向标准输出
dup2(fd3,2)//复制文件描述符fd3到文件描述符2即可重定向标准错误
通过将标准输入,输出,错误重定向到管道两端的文件描述符,就可以实现两个进程间标准输入输出的传递;
以下程序中,父进程向stdout
输出hello
,直接转移到子进程的stdin
,由子进程中的fgets(buff)
接收
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2];
pid_t fpid;
char buff[256];
// 创建管道
if (pipe(fd) < 0) {
printf("create Pipe error!!");
return -1;
}
// 创建子进程
fpid = fork();
if (fpid < 0) {
printf("fork error!!");
return -1;
}
// 子进程关闭管道输入端;将标准输入(stdin,文件描述符为0)重定向为文件描述符fd[0];
else if (fpid == 0) {
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
fgets(buff, 256, stdin);
printf("[child] %s\n", buff);
return 0;
}
// 父进程关闭管道输出端;将标准输出(stdout,文件描述符为1)重定向为文件描述符fd[1];
else {
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
puts("hello");
return 0;
}
}
运行结果
[child]:hello