管道
(一).通信方式:
进程间通信又称IPC(Inter Process Communication),它可以通过文件 管道 有名管道 共享内存 消息队列 信号量 套接字这几个方式进行通信,但是文件这种以及消息队列基本已被淘汰。
所以常用的通信方式有:
1.管道
2.信号
3.共享映射区
4.本地套接字
(二).管道基本概念:
而其中的管道机制,是属于最简单的一种通信方式。它适用于有血缘关系的进程之间的单向通信。它的本质是伪文件,是一段内核缓冲区。实现原理是内核使用环形队列,借助内核缓冲区实现。
当我们调用pipe()
函数创建管道时,需要传入一个文件描述符数组,一个表示读端,一个表示写端。就好像一个管道一样,写方进程将数据写入,读方再通过管道将数据读出。既然规定了读和写的两端,以及是环形队列以及缓冲区实现,就证明了有一定的局限性。
1.数据不可反复读取,即读了之后在缓冲区中就没有了
2.半双工通信,只能单方向的传输数据
3.有血缘关系的进程之间通信
(三).使用管道进行通信:
创建管道的函数原型:int pipe(pipefd[2])
所需头文件:#include <unistd.h>
参数pipefd[2]
会返回两个文件描述符,一个用于读,另一个用于写。默认是fd[0]读,fd[1]写。如果创建管道成功,返回0,否则返回-1。
例子:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd[2];
pid_t pid;
if(pipe(fd) == -1)
{
perror("pipe error:");
exit(1);
}
pid = fork();
if(pid == -1)
{
perror("fork error:");
exit(2);
}
else if(pid == 0) //本例子中子进程写数据
{
close(fd[0]); //关闭读数据的文件描述符
write(fd[1], "hello pipe!\n", strlen("hello pipe!\n")); //写入数据
}
else
{
close(fd[1]);
char buf[102400];
int ret = read(fd[0], buf, sizeof(buf)); //通过管道读出数据
if(ret == 0)
{
printf("读到了文件末尾\n");
}
write(STDOUT_FILENO, buf, ret); //输出到终端
}
return 0;
}
输出结果:
hello pipe!
(五).注意事项:
在使用管道时,有几种特殊情况需要注意:
1.管道中没有数据时,如果管道写端全部关闭,那么此时read函数会返回0,如果管道写端没有全部关闭,那么此时read会阻塞等待数据。也就是说,在上面的例子中,如果子进程通过sleep函数睡眠10秒,父进程也会阻塞等待子进程写数据,因为此时子进程的写端仍处于开启状态。
2.管道读端全部关闭,进程会异常终止(可以理解为读数据的都没有了,那写数据也没有意义了)
3.管道已满时,写入会阻塞。
要知道管道只能在有血缘关系的进程之间通信,而有名管道恰可以用于无血缘关系的进程间通信。