概念
管道是UNIX系统IPC的最古老形式,并且所有的UNIX系统都提供此种通信机制。管道有下列两种局限性:
-
历史上,它们是半双工的,现在某些系统提供全双工管道,但是为了最佳的可移植性,我们决不应预先假定系统使用此特性。
-
它们只能在具有公共祖先的进程之间使用。通常一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间可以使用该管道。
注:FIFO没有第二种局限性,UNIX域套接字没有这两种局限性
pipe函数
管道是由调用pipe函数而创建的:
#include <unistd.h>
int pipe(int fd[2])
//返回值:若成功,返回0;若出错,返回-1
经由参数,fd返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。fd[1]的输出是fd[0]的输入。
创建管道
单个进程创建pipe
fork之后的半双工管道
单个进程中的管道几乎没有任何用处。通常会先调用pipe,接着调用fork,从而创建从父进程到子进程的IPC通道,反之亦然。
fork之后取决于我们想要的数据流方向。对于父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程关闭写端(fd[1]),即可实现父进程对子进程的单向通信。
当管道的一端被关闭之后,下列两条规则起作用:
- 当读(read)一个写端已经被关闭的管道时,在所有数据都被读取之后,read返回0,表示文件结束。
- 如果写(write)一个读端已经被关闭的管道,则产生SIGPIPE信号。
实例
//示例一
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
int main()
{
int pipefd[2];
if (pipe (pipefd) == -1)
{
perror ("pipe");
exit (EXIT_FAILURE);
}
pid_t pid;
if((pid=fork())<0)
{
perror("fork");
}
else if(pid==0)
{
printf("这是子进程,pid=%d,",getpid());
printf("父进程的pid=%d\n",getppid());
if (close (pipefd[1]) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
char text[20];
ssize_t readed = read (pipefd[0], text, 20);
if (readed == -1)
{
perror ("read");
exit (EXIT_FAILURE);
}
printf("%s\n", text);
if (close (pipefd[0]) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
}
else
{
sleep (1);
printf("这是父进程,pid=%d\n",getpid());
if (close (pipefd[0]) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
ssize_t written = write (pipefd[1], "hello world", 12);
if (written == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
if (close (pipefd[1]) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
}
return 0;
}
输出结果:
这是子进程,pid=2799,父进程的pid=2798
这是父进程,pid=2798
hello world