进程间通信——管道
来源:互联网 发布:bose蓝牙音响 知乎 编辑:程序博客网 时间:2024/04/30 13:22
1 管道
作为进程之间的通信手段之一,本质就是内核中的一块有限的缓冲区(64K字节,不同操作系统不一样),而且独立于文件系统,自己构成一个只存在于内存中的文件系统(可追踪内核的pipe.c存在于文件系统目录下)。其特点如下。
1)半双工通信,同一时间数据只能从一端流向另一端;
2)分为匿名管道和有名管道。而且对于匿名管道,只能用于具有共同祖先的进程之间通信,即父子进程或者父孙进程等有关系的进程之间通信,有名管道则没有此限制;
3)填充的是文件描述符数组,其中fd[0] 表示读端,fd[1]表示写端。其在创建子进程前后的关系如下图所示。
2 通用编程方式
2.1 匿名管道
如下表所示,需要注意的就是,父子进程可以关闭管道无用的端口。
1
//假设用例是子进程通过管道发送给父进程
2
// 子进程需要关闭读端,父进程需要关闭写端
3
intmain(int argc,char *argv[])
4
{
5
int pipefd[2];
6
if (pipe(pipefd) == -1)
7
ERR_EXIT("pipe error");
8
pid_t pid;
9
pid = fork();
10
if (pid == -1)
11
ERR_EXIT("fork error");
12
if (pid ==0)
13
{
14
close(pipefd[0]);
15
write(pipefd[1],"hello", 5);
16
close(pipefd[1]);
17
exit(EXIT_SUCCESS);
18
}
19
close(pipefd[1]);
20
char buf[10] = {0};
21
read(pipefd[0], buf,10);
22
printf("buf=%s\n", buf);
23
return0;
24
}
2.2 有名管道
简单示例,如下表所示。注意打开方式的不同,除了下示的open打开一个已经存在的管道文件外,还可以通过mkfifo函数创建,事实上它也是一个命令。
其用法说明如下。
1)有名管道,是一种特殊的文件类型,即通过ls可以查看到其指定为“p”,除此之外还有六种类型,共计七种,d-目录文件,l-符号链接文件,s-套接字文件,b-块设备文件,c-字符设备文件,“-”表示普通文件。
2) 打开方式有所不同,有可能会阻塞。没错,打开时就阻塞。具体可参见下表,注意区分O_NONBLOCK使能后,对应的错误。
O_NONBLOCK disable
O_NONBLOCK enable
READ
阻塞,直至有程序以写打开该管道
返回成功
WRITE
阻塞,直至有程序以读打开该管道
返回失败,错误码为ENXIO
3)示例程序
1
intmain(int argc,char *argv[])
2
{
3
int fd;
4
/* fd = open("p1", O_RDONLY);*/
5
fd = open("p1", O_RDONLY | O_NONBLOCK);
6
7
if (fd == -1)
8
ERR_EXIT("open error");
9
10
printf("open succ\n");
11
return0;
12
}
2.3 对比
主要体现在两点,一是打开方式,匿名管道不可能会发生阻塞;二是用途,匿名管道不能用于不具备亲缘关系的进程之间通信。
3 读写规则
若管道的写端不存在,继续读该管道时,认为已经读到了末尾,返回0;
若管道的读端不存在,继续写该管道时,返回错误;
[可见系统对写采取0容忍的态度 ]
若管道不存在可读数据,那么在非阻塞模式下返回-1,且errno等于EAGAIN,而在阻塞模式下则会继续阻塞直至有数据;
若管道已经满时,那么在非阻塞模式下同样返回-1,且errno等于EAGAIN,在阻塞模式下则继续阻塞,直至有空间可写;
当写入的数据大于PIPEBUF(在文章一开始处已经介绍了管道本质就是内核的一块缓冲区大小为PIPEBUF 64K),不会保证写入的原子性。如下代码所示。
1
intmain(void)
2
{
3
char a[TEST_SIZE];
4
char b[TEST_SIZE];
5
char c[TEST_SIZE];
6
7
memset(a,'A', sizeof(a));
8
memset(b,'B', sizeof(b));
9
memset(c,'C', sizeof(c));
10
11
int pipefd[2];
12
13
int ret =pipe(pipefd);
14
if (ret == -1)
15
ERR_EXIT("pipe error");
16
17
pid_t pid;
18
pid = fork();
19
if (pid ==0)//第一个子进程
20
{
21
close(pipefd[0]);
22
ret = write(pipefd[1], a, sizeof(a));
23
printf("apid=%d write%d bytes to pipe\n",getpid(), ret);
24
exit(0);
25
}
26
27
pid = fork();
28
29
30
if (pid ==0)//第二个子进程
31
{
32
close(pipefd[0]);
33
ret = write(pipefd[1], b, sizeof(b));
34
printf("bpid=%d write%d bytes to pipe\n",getpid(), ret);
35
exit(0);
36
}
37
38
pid = fork();
39
40
41
if (pid ==0)//第三个子进程
42
{
43
close(pipefd[0]);
44
ret = write(pipefd[1], c, sizeof(c));
45
printf("bpid=%d write%d bytes to pipe\n",getpid(), ret);
46
exit(0);
47
}
48
49
50
close(pipefd[1]);
51
52
sleep(1);
53
int fd =open("test.txt", O_WRONLY | O_CREAT | O_TRUNC,0644);
54
char buf[1024*4] = {0};
55
int n =1;
56
while (1)
57
{
58
ret = read(pipefd[0], buf, sizeof(buf));
59
if (ret ==0)
60
break;
61
printf("n=%02d pid=%d read %d bytes from pipe buf[4095]=%c\n", n++,getpid(), ret, buf[4095]);
62
write(fd, buf, ret);
63
64
}
65
return0;
66
}
其结果,如下图所示。“A”、“B”、“C”完全是乱序输出的,并不是按顺序,写完A再写B和C的。
- 进程间通信—管道
- 进程间通信—管道
- 进程间通信—管道
- 进程间通信——管道通信
- 进程间通信——管道通信
- Windows进程间通信——管道
- 进程间通信——管道(Pipe)
- Linux进程间通信——管道
- 进程间通信——匿名管道
- Linux进程间通信——管道
- Linux进程间通信——管道
- 进程间通信——管道
- 进程间通信——管道
- 进程间通信——管道
- 进程间通信——管道
- 进程间通信——管道
- 进程间通信 ——管道
- 进程间通信——管道
- TTL、RS232、CMOS电平
- 算法 for_each
- HDU 1269 迷宫城堡 强联通分量模板存放处
- 关于System类的使用方法【小程序】
- [Offer收割]编程练习赛1 hihocoder 1270 建造基地 (完全背包)
- 进程间通信——管道
- 新浪微博时间转换工具类
- 会场安排问题(贪心算法)
- Android知识点整理
- 剑指offer-顺时针打印矩阵
- 7种qsort排序方法
- 2016.3.5【初中部 NOIP提高组 】模拟赛A
- sqlite优化
- ubuntu操作系统安装(二) git