管道和FIFO
一概述
管道是一种把两个进程之间的标准输入和标准输出连接起来的机制。管道是一种历史悠久的进程间通信的方法。当进程创建管道时,每次都需要提供两个文件描述符来操作管道。其中一个对管道进行写操作,另一个对管道进行读操作。对管道的读写与一般的IO系统函数一致,使用write()函数写入数据,使用read()读出数据。
二、管道
1.函数pipe
//创建管道
#include<unistd.h>
int pipe(int filedes[2]);
//filedes是一个文件描述符的数组,用于保存管道返回的两个文件描述符。
//f[0]:读操作而创建和打开的。
//f[1]:写操作而创建和打开的。
//fd[1]的输出是fd[0]的输入。
//当函数执行成功时,返回0,失败时返回值为-1。
pipe()函数的例子:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
int main(void)
{
int result = -1;
int fd[2],nbytes;
pid_t pid;
char string[] = "你好,管道";
char readbuffer[80];
int *write_fd = &fd[1];/*写文件描述符*/
int *read_fd = &fd[0];/*读文件描述符*/
result = pipe(fd);
if(-1 == result)
{
printf("建立管道失败\n");
return -1;
}
pid=fork();/*分叉出现*/
if(-1 == pid)
{
printf("fork进程失败\n");
return -1;
}
if(0 == pid)
{
close(*read_fd);
result = write(*write_fd,string,strlen(string));
return 0;
}
else
{
close(*write_fd);
nbytes = read(*read_fd,readbuffer,sizeof(readbuffer));
printf("接受到%d个数据,内容为:%s\n",nbytes,readbuffer);
return 0;
}
}
2.半双工管道
由于管道仅仅是将某个进程的输出和另一个进程的输入相邻的单向通信的方法,因此称其为"半双工"。
在shell中管道用"|"
表示,如下所示,管道的一种使用方式。
ls -l | grep *.c
-
把ls -l的输出当做"
grep * .c
"的输入,管道在前 一 个进程 中建立输入通道 ,在后一 个进程建立输出通道,将数据从管道的左边传输到管道的右边,将Is -I
的输出通过管道传给 “grep *.c
”。 -
进程创建管道,每次创建两个文件描述符来操作管道。其中一个对管道进行写操作。另一 个描述符对管道进行读操作。
-
如下图所示,显示了管道如何将两个进程通过内核连接起来,从图中可以看出这两个文件描述符是如何连接在一 起的。如果进程通过管道
fda[0]
发送数据,它可以从fdb[0]
获得信息。
-
由于进程A和进程B都能够访问管道的两个描述符,因此管道创建完毕后要设置在各个进程中的方向,希望数据向那个方向传输。
-
这需要做好规划,两个进程都要做统一 的设置,在进程A中设置为读的管道描述符,在进程B中要设置为写;
-
反之亦然,并且要把不关心的管道端关掉。对管道的读写与一 般的IO 系统函数一 致,使用write()函数写入数据, read() 函数读出数据,某些特定的IO 操作管道是不支持的,例如偏移函数lseek()。
3.全双工管道
全双工管道是由两个半双工管道构成的,写入fd[1]的数据只能从fd[0]读出,写入fd[0]的数据只能从fd[1]读出。
int main(int argc, char **argv)
{
int fd[2], n;
char c;
pid_t childpid;
pipe(fd); /* 假设为全双工管道*/
if ( (childpid = fork()) == 0) {
/* child */
sleep(3);
if ( (n = read(fd[0], &c, 1)) != 1)
err_quit("child: read returned %d", n);
printf("child read %c\n", c);
write(fd[0], "c", 1);
exit(0);
}
/* parent */
write(fd[1], "p", 1);
if ( (n = read(fd[1], &c, 1)) != 1)
err_quit("parent: read returned %d", n);
printf("parent read %c\n", c);
exit(0);
}
三、popen和pclose函数
常见的操作是创建一个连接到另一个进程的管道,然后读其输出或向其输 入端发送数据,为此,标准I/O库提供了两个函数popen和pclose。这两个函数实 现的操作是:创建一个管道, fork一个子进程,关闭未使用的管道端,执行一 个shell运行命令,然后等待命令终止。
- 函数popen先执行fork,然后调用exec执行cmdstring,并且返回一个标准I/O 文件指针。
- 若type是"r",则文件指针连接到cmdstring的标准输出;若type是"w",则文件指针连接到cmdstring的标准输入,如下图所示。
#include <stdio.h>
FILE *popen