目录
1、概述
2、管道的创建
3、管道读写行为
3.1、管道读
在linux中管道有两种,一是无名管道(匿名管道),第二种是有名管道;无名管道主要用于有血缘关系的父子进程间通信,有名管道则不受该限制,可用于任意进程之间的通信;这里我们主要学习无名管道。
1、概述
创建无名管道的函数如下:
#include <unistd.h>
int pipe(int pipefd[2]);
调用失败返回-1,成功返回0;调用成功时该函数会创建一个单向的管道用于进程之间的通讯,返回的管道包含读端和写端,其中fd[0]用于读,fd[1]用于写,写到fd[1]的数据会被内核保存到缓冲区,直到fd[0]读走数据。
2、管道的创建
代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
int main(int argc, char *argv[]) {
pid_t fd[2];
int ret;
ret = pipe(fd);
if (-1 == ret) {
perror("pipe error!\n");
exit(1);
}
printf("Hello pipe, fd[0] = %d, fd[1] = %d\n", fd[0], fd[1]);
return 0;
}
很简单,直接调用pipe并传入一个文件描述符数组,如果出错则退出,调用成功则返回两个可用的文件描述符,其中fd[0]用于读,fd[1]用于写。
gcc编译:gcc pipe.c -o pipe,运行./pipe得出如下运行结果:
3、管道读写行为
由于管道是单向通讯的,所以在使用的时候会有一些限制。
- 管道读
如果管道中有数据,则读取实际能读到的数据;
如果管道中无数据,此时有两种情况:一是有管道写端,此时会阻塞直到写端写入数据;二是没有管道写端(写端被关闭),因为没有写端,永远不会有数据写入,此时返回0。
- 管道写
如果管道满了,则写阻塞,直到能写入。
如果管道未满,此时有两种情况:一是没有管道读端,此时异常终止(SIGPIPE导致);二是有读端,则返回实际写入的字节数。
下面简单列举几个例子,主要是父子进程之间通讯。
3.1、管道读
代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
int main(int argc, char *argv[]) {
pid_t fd[2];
int ret;
char buf[1024];
char *info = "Hello Pipe(from read)\n";
ret = pipe(fd);
if (-1 == ret) {
perror("pipe error!\n");
exit(1);
}
ret = fork();
if (0 == ret) {
printf("I'm child\n");
close(fd[1]);
while ((ret = read(fd[0], &buf, 1024)) > 0) {
write(STDOUT_FILENO, &buf, ret);
}
close(fd[0]);
} else if (ret > 0) {
printf("I'm parent\n");
close(fd[0]);
write(fd[1], info, strlen(info));
close(fd[1]);
} else {
perror("fork error!\n");
exit(1);
}
return 0;
}
首先调用pipe创建了读写管道的读端和写端,然后调用fork创建了一个子进程,如果ret 为0,说明是在子进程,如果ret大于0,说明是在父进程,如果ret小于0则说明fork出错,退出程序。
代码中申明了一个字符串"Hello Pipe(from read)",由于父进程用于写,子进程用于读,所以父进程首先关闭了fd[0],然后用fd[1]往管道中写入"Hello Pipe(from read)",之后关闭fd[1]。
由于子进程用于读,所以一开始关闭了管道的写端fd[1],然后循环从fd[0]中读入数据存储到buf中并打印到屏幕,直到读完管道中的数据,最后关闭管道读端fd[0]。
运行结果如下:
上面演示的是管道有数据的情况,立刻就读出了管道中的数据;下面考虑以下两种情况
- 管道无数据 有写端
- 管道无数据 有读端
第一种情况,管道将会读阻塞,可以运行如下实例看看:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
int main(int argc, char *argv[]) {
pid_t fd[2];
int ret;
char buf[1024];
char *info = "Hello Pipe(from read)\n";
ret = pipe(fd);
if (-1 == ret) {
perror("pipe error!\n");
exit(1);
}
ret = fork();
if (0 == ret) {
printf("I'm child\n");
close(fd[1]);
while ((ret = read(fd[0], &buf, 1024)) > 0) {
write(STDOUT_FILENO, &buf, ret);
}
close(fd[0]);
printf("After read\n");
} else if (ret > 0) {
printf("I'm parent\n");
close(fd[0]);
//write(fd[1], info, strlen(info));
sleep(10);
printf("After sleep\n");
close(fd[1]);
} else {
perror("fork error!\n");
exit(1);
}
return 0;
}
代码结构和之前一样,为了构造管道无数据的情况,父进程中没有写入数据,而是sleep(10),然后10s后输出"After sleep"且关闭管道写端fd[1];子进程保持不变,由于管道中一直没有数据,会导致子进程管道的读端一直阻塞直到父进程关闭管道写端fd[1]。
程序输出如下: