一、概念
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”,其本质时内核中的一块缓冲区。
二、特性
1.半双工通信。管道是双向选择,单向通信。
2.读写特性。
如果管道中没有数据,则read会阻塞;如果管道中数据满了,则write会阻塞;
如果管道所有写端关闭,则read读完之返回0而不是阻塞;如果所有读端关闭,则write写完后触发异常退出而不是阻塞。
3.管道自带同步与互斥。
同步:保证数据操作的合理性,时序可控,即我操作完了别人才能操作;
互斥:保证数据操作的时间唯一访问性,即我操作的时候别人不能操作。
(注:管道的操作大小小于PIPE_BUF时,可以保证操作原子性)
4.管道的生命周期随进程,进程生命周期结束则管道生命周期也结束
5.管道提供字节流服务:传输灵活,但是会造成数据粘连没有边界
6.管道仅能用于具有亲缘关系的进程间通信,因为子进程需要通过复制父进程获取管道操作句柄。
三、匿名管道
1,实现函数:int pipe(int pipefd[2])
pipedfd[0]:管道的读取端; pipefd[1]:管道的写入端
返回值:成功返回0,失败返回-1
2.示例
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4 #include<stdlib.h>
5 #include<errno.h>
6
7 int main()
8 {
9 int pipefd[2] = {0};
10 int ret = pipe(pipefd);
11 if(ret < 0)
12 {
13 perror("pipe error");
14 exit(-1);
15 }
16 int pid = fork();
17 if(pid < 0)
18 {
19 perror("fork error");
20 return -1;
21 }
22 else if(pid == 0)
23 {
24 char buf[1024] = {0};
25 close(pipefd[1]);
26 read(pipefd[0], buf, 1023);
27 printf("buf = %s\n",buf);
28 }
29 else
30 {
31 char* ptr = "hello";
32 close(pipefd[0]);
33 write(pipefd[1], ptr, strlen(ptr));
34
35 }
36 return 0;
37
38 }
这样就简单的实现了父子进程之间通过管道实现数据传输。值得注意的是:管道的创建要在fork子进程之前,并且由于管道的读写特性,用户在操作管带的时候最好是没有用到哪一端就关闭掉那一端。
四、命名管道
1.特点
(1)有名字,管道文件是开头为p的文件,而管道文件就是命名管道的名字
(2)在磁盘上存在这样的文件
(3)伪文件,该文件在磁盘大小永远为0
(4)在内核中有一个对应的缓冲区
(5)半双工的通信方式
(6)命名管道可以实现非亲缘关系的进程间通信
2.实现函数:int mkfifo(const char *pathname, mode_t mode)
参数:pathname:管道文件路径及名称
mode:管道文件操作权限
返回值:成功返回0,失败返回-1
3.示例:利用命名管道实现两个进程间通信
首先写一个读取数据的程序
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
char *fifo = "./test.fifo";
umask(0);
int ret = mkfifo(fifo, 0664);
if (ret < 0 && errno != EEXIST) {
perror("mkfifo error");
return -1;
}
int fd = open(fifo, O_RDONLY);
if (fd < 0) {
perror("open error");
return -1;
}
while(1) {
sleep(5);
char buf[1024] = {0};
read(fd, buf, 1023);
printf("buf:[%s]\n", buf);
}
close(fd);
return 0;
}
接下来是写入数据的程序
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *fifo = "./test.fifo";
umask(0);
int ret = mkfifo(fifo, 0664);
if (ret < 0 && errno != EEXIST) {
perror("mkfifo error");
return -1;
}
int fd = open(fifo, O_WRONLY);
if (fd < 0) {
perror("open error");
return -1;
}
while(1) {
char buf[1024] = {0};
scanf("%s", buf);
write(fd, buf, strlen(buf));
}
close(fd);
return 0;
}
这样两个进程同时运行,在写端输入数据,就可以在读端接收到数据,从而实现非亲缘关系的进程间通信。