管道
管道分为两种类型:全双工管道与半双工管道。半双工管道即意味着数据只能在一个方向上流动。全双工管道上的数据可以两个方向流动。老旧系统上大多不提供全双工管道,所以为了移植性考虑,应主要使用半双工管道。通常管道只能在具有公共祖先的两个进程间使用。
尽管有局限性,但是半双工管道仍旧是最常用的进程间通信方式。管道通过pipe()函数创建。
int pipe(int fd[2]);
fd返回两个文件描述符,fd[0]为读而打开,fd[1]为写而打开。我们通过一个例子来说明。
int fd[2];
char line[length];
pipe(fd);
if(pid = fork()>0) {
close(fd[0]);
write(fd[1], "hello world!\n", 12);
}//parent
else {
close(fd[1]);
n = read(fd[0], line, length);
printf("%s", line);
}//child
首先我们创建一个半双工管道,然后通过fork()函数生成子进程。子进程会继承父进程的所有文件描述符,显然fd[0]、fd[1]都继续保存着。然后父进程关闭读通道,子进程关闭写通道,父进程向半双工管道写入一串字符串,然后子进程读入后写入标准输出。
其实实现全双工管道也不是一件复杂的事。我们只需要建立两个半双工管道即可。
int fd1[2],fd2[2];
char line1[length],line2[length];
pipe(fd1);
pipe(fd2);
if(pid = fork()>0) {
close(fd1[0]);
close(fd2[1]);
write(fd1[1], "hello world!\n", 12);
read(fd2[0],line1,length);
printf("%s", line1);
}//parent
else {
close(fd1[1]);
close(fd2[0]);
n = read(fd1[0], line, length);
write(fd2[1], "hello world!\n", 12);
printf("%s", line2);
}//child
FIFO
FIFO有时被称为命名管道。未命名的管道只能在两个有共同祖先的进程之间使用。FIFO是一个半双工管道。
FIFO由mkfifo()函数创建。
int mkfifo(const char *pathname, mode_t mode);
mode参数指定文件权限位,类似于open函数的第二个参数。而pathname是一个普通的Unix路径名,代表FIFO的位置。
需要注意的是mkfifo()函数只能创建一个新的管道,而打开一个新的管道只能用open()函数。
#define FIFO1 "tmp/fifo.1"
#define FIFO2 "tmp/fifo.2"
char line1[length],line2[length];
mkfifo(FIFO1, FILE_MODE);
mkfifo(FIFO2, FILE_MODE);
if(pid = fork()>0) {
readfd = open(FIFO1, O_RDONLY, 0);
writefd = open(FIFO2, O_wDONLY, 0);
write(writefd, "hello world!\n", 12);
read(readfd, line1, length);
printf("%s", line1);
}//parent
else {
readfd = open(FIFO2, O_RDONLY, 0);
writefd = open(FIFO1, O_wDONLY, 0);
write(writefd, "hello world!\n", 12);
read(readfd, line2, length);
printf("%s", line2);
}//child
可以看到这和半双工管道很像,可以把理解成存储在Unix系统中的管道。