文章目录
- 管道
- 特点
- 匿名管道
- 匿名管道的创建
- 实现步骤
- 有名管道
- Linux系统命令实现
- C语言实现
- 写入
- 读取
管道
管道中的数据只能单向流动,也就是半双工通信,如果想实现相互通信(全双工通信),我们需要创建两个管道。
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试 图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到 内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。
特点
管道分为有名管道fifo和无名管道pipe
-
管道创建后是单向的,一端是写,一端是读
-
管道有大小,linux内核对每个管道的大小限制在4096字节
-
无名管道一般用在父子进程间通讯
-
有名管道一般用在进程与进程之间通讯
-
管道是阻塞的,读取管道过程中一直处于阻塞的状态
-
管道在被读取前,只能写入一次,多次写入是无效的
匿名管道
特点:
1、创建之后在文件系统不可见
2、半双工通信
3、固定的读端(0),写端(1)
4、只能用于具有亲缘关系的进程间的通信
匿名管道用完了就会被自动销毁。
例如linux系统的命令:
command1 | command2
以上这行代码就组成了一个管道,它的功能是将前一个命令(command1)的输出,作为后一个命令(command2)的输入。
匿名管道的创建
通过pipe 函数来创建匿名管道。
创建成功则返回 0,创建失败就返回 -1。
int pipe (int fd[2]);
该函数拥有一个存储空间为 2 的文件描述符数组:
-
fd[0] 指向管道的读端,fd[1] 指向管道的写端
-
fd[0] 的输出数据,是 fd[1] 的输入数据。
实现步骤
-
父进程创建两个匿名管道,管道 1(fd1[0]和 fd1[1])和管道 2(fd2[0] 和 fd2[1];因为管道的数据是单向流动的,所以要想实现数据双向通信,就需要两个管道,每个方向一个。
-
父进程 fork 出子进程,于是对于这两个匿名管道,子进程也分别有两个文件描述符指向匿名管道的读写两端;
-
父进程关闭管道 1 的读端 fd1[0] 和 管道 2 的写端 fd2[1],子进程关闭管道 1 的写端 fd1[1] 和 管道 2 的读端 fd2[0],这样,管道 1 只能用于父进程写、子进程读;管道 2 只能用于父进程读、子进程写。
管道是用环形队列实现的,数据从写端流入从读端流出,这就实现了父子进程之间的双向通信。
有名管道
匿名管道由于没有名字,只能用于父子进程间的通信。为了克服这个缺点,提出了有名管道,也称做 FIFO,因为数据是先进先出的传输方式。
所谓有名管道也就是提供一个路径名与之关联,这样,即使与创建有名管道的进程不存在亲缘关系的进程,只要可以访问该路径,就能够通过这个有名管道进行相互通信。
Linux系统命令实现
使用 Linux 命令 mkfifo 来创建有名管道
mkfifo myPipe
往 myPipe 这个有名管道中写入数据:(数据未被读走时将阻塞)
echo "hello" > myPipe
执行另外一个命令来读取这个有名管道里的数据:
cat myPipe
C语言实现
步骤 | 进程A | 进程B | 步骤 |
---|---|---|---|
1.创建管道 | mkfifo | ||
2.打开管道 | open | open | 1.打开管道 |
3.读写管道 | write/read | write/read | 2.读写管道 |
4.关闭管道 | close | close | 3.关闭管道 |
5.删除管道 | unlink |
写入
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>#define FIFO_FILE "/tmp/fifo"int main(void){printf("创建管道...\n");if(mkfifo(FIFO_FILE,0666) == -1){perror("mkfifo");return -1; }printf("打开管道...\n");int fd = open(FIFO_FILE,O_WRONLY);if(fd == -1){perror("open");return -1;}printf("发送数据...\n");for(;;){printf(">");char buf[1024];gets(buf);if(!strcmp(buf,"!"))break;if(write(fd,buf,(strlen(buf)+1))) * sizeof(buf[0]) == -1){perror("write");return -1; } }printf("关闭管道...\n");if(close(fd) == -1){perror("close");return -1; }printf("删除管道...\n");if(unlink(FIFO_FILE) == -1){perror("unlink");return -1; }printf("大功告成!\n");return 0;
}
读取
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>#define FIFO_FILE "/tmp/fifo"int main(void){printf("打开管道...\n");int fd = open (FIFO_FILE,O_RDONLY);if(fd == -1){perror("open");return -1;}printf("接收数据...\n");for(;;){char buf[1024];size_t rb = read(fd,buf,sizeof(buf));if(rb == -1){perror("read");return -1;} if(!rb)break;printf("< %s\n",buf);}printf("关闭管道...\n");if(close(fd) == -1){perror("close");return -1;}printf("大功告成!\n");return 0;