引言
pipe是Linux系统跨进程通信的一种方式。管道分为匿名管道和有名管道。这里我们说的是匿名管道。作用于有血缘关系的进程之间,即必须有相同的祖先,这里使用父进程和子进程来举例说明。
匿名管道是单向的,分为读端和写端,通过调用pipe系统函数实现。
数据方面一个进程只能读或者写,不能自己写自己读。
向管道文件读写数据其实是在读写内核缓冲区。
pipe函数
#include <unistd.h>
int pipe(int pipefd[2]);
pipe() 创建一个管道,一个可用于进程间通信的单向数据通道。 数组 pipefd 用于返回两个指向管道末端的文件描述符。 pipefd[0] 指的是管道的读端。 pipefd[1] 指的是管道的写端。 写端把数据写入管道,直到读端读取数据。
成功时,返回零。 出错时,返回 -1,并适当设置 errno。
函数执行完成,会生成r/w两个文件描述符。无需open,但需手动close。pipefd[0]即读端文件描述符,pipefd[1]即写端文件描述符。
父子进程通信
- 管道创建成功以后,创建该管道的进程(父进程)和子进程同时掌握着管道的读端和写端。
- 父进程关闭读端,子进程关闭写端。父进程写入数据,子进程读取数据。由于管道是单向的,数据从写端输入,从读端输出,从而实现进程间通信。
示例
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(void)
{
pid_t pid;
char buf[15];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0) {
sys_err("fork err");
} else if (pid == 0) {
printf("i am child begin\n");
printf("i am child.my pid = %d\n",getpid());
close(fd[1]);
int len = read(fd[0], buf, sizeof(buf));
write(STDOUT_FILENO, buf, len);
close(fd[0]);
printf("i am child end\n");
} else {
printf("i am parent begin\n");
printf("i am parent.my pid = %d\n",getpid());
close(fd[0]);
write(fd[1], p, strlen(p));
wait(NULL);
close(fd[1]);
printf("i am parent end\n");
}
return 0;
}
- 利用write(STDOUT_FILENO, buf, len);输出打印子进程读取到的内容。
- 父进程利用wait(NULL);等待子进程执行结束再执行逻辑。
管道的读写
读管道
- 管道中有数据,read返回实际读取到的数据字节数。
- 管道中无数据
2.1 管道写端关闭,read返回0(好像读到文件结尾)
2.2 管道写端未全部关闭,但未写入数据,read阻塞等待,直到读取到数据,此时让出cpu
写管道
- 管道读端全部关闭,write会导致进程异常终止,除非捕获SIGPIPE信号。
- 管道读端没有全部关闭
2.1 管道已满,write阻塞
2.2 管道未满,write数据写入后返回写入的字节数
读管道示例
- 2.1的示例:
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(void)
{
pid_t pid;
char buf[15];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0) {
sys_err("fork err");
} else if (pid == 0) {
printf("i am child begin\n");
printf("i am child.my pid = %d\n",getpid());
close(fd[1]);
int len = read(fd[0], buf, sizeof(buf));
printf("len = %d\n",len);
write(STDOUT_FILENO, buf, len);
close(fd[0]);
printf("i am child end\n");
} else {
printf("i am parent begin\n");
printf("i am parent.my pid = %d\n",getpid());
close(fd[0]);
close(fd[1]);
// write(fd[1], p, strlen(p));
wait(NULL);
// close(fd[1]);
printf("i am parent end\n");
}
return 0;
}
i am parent begin
i am parent.my pid = 8790
i am child begin
i am child.my pid = 8793
len = 0
i am child end
i am parent end
- 2.2 示例
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(void)
{
pid_t pid;
char buf[15];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0) {
sys_err("fork err");
} else if (pid == 0) {
printf("i am child begin\n");
printf("i am child.my pid = %d\n",getpid());
close(fd[1]);
int len = read(fd[0], buf, sizeof(buf));
printf("len = %d\n",len);
write(STDOUT_FILENO, buf, len);
close(fd[0]);
printf("i am child end\n");
} else {
printf("i am parent begin\n");
printf("i am parent.my pid = %d\n",getpid());
close(fd[0]);
// close(fd[1]);
// write(fd[1], p, strlen(p));
wait(NULL);
close(fd[1]);
printf("i am parent end\n");
}
return 0;
}
i am parent begin
i am parent.my pid = 8899
i am child begin
i am child.my pid = 8902
写管道示例
- 1 示例
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(void)
{
pid_t pid;
char buf[15];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0) {
sys_err("fork err");
} else if (pid == 0) {
printf("i am child begin\n");
printf("i am child.my pid = %d\n",getpid());
close(fd[0]);
close(fd[1]);
int len = read(fd[0], buf, sizeof(buf));
printf("len = %d\n",len);
write(STDOUT_FILENO, buf, len);
// close(fd[0]);
printf("i am child end\n");
} else {
printf("i am parent begin\n");
printf("i am parent.my pid = %d\n",getpid());
close(fd[0]);
write(fd[1], p, strlen(p));
wait(NULL);
close(fd[1]);
printf("i am parent end\n");
}
return 0;
}
i am parent begin
i am parent.my pid = 10161
i am child begin
i am child.my pid = 10163
len = -1
i am child end
i am parent end
这里很奇怪,关闭读端后,写入管道数据没有进程崩溃。在管道读端关闭的情况下,写入数据,read函数返回的是-1。作为一个TODO吧。
- 2.1 示例
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(void)
{
pid_t pid;
char buf[15];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0) {
sys_err("fork err");
} else if (pid == 0) {
printf("i am child begin\n");
printf("i am child.my pid = %d\n",getpid());
close(fd[1]);
int len = read(fd[0], buf, sizeof(buf));
printf("len = %d\n",len);
write(STDOUT_FILENO, buf, len);
close(fd[0]);
printf("i am child end\n");
} else {
printf("i am parent begin\n");
printf("i am parent.my pid = %d\n",getpid());
close(fd[0]);
for (int i = 0; i < 10000; ++i) {
write(fd[1], p, strlen(p));
}
wait(NULL);
close(fd[1]);
printf("i am parent end\n");
}
return 0;
}
i am parent begin
i am parent.my pid = 10546
i am child begin
i am child.my pid = 10548
len = 15
test for pipe
ti am child end
网友评论