进程间通信1.docx
- 文档编号:26231854
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:66
- 大小:184.08KB
进程间通信1.docx
《进程间通信1.docx》由会员分享,可在线阅读,更多相关《进程间通信1.docx(66页珍藏版)》请在冰豆网上搜索。
进程间通信1
无名管道的用法
无名管道又被称为pipe,是进程间通信的一种方式。
pipe具有以下特点:
●只能用于具有血缘关系的进程之间
●半双工的通信模式,具有固定的读端和写端
●一种特殊的文件,存在于内存中。
可通过read、write对其操作
因为pipe存在于内存中,所以无法像操作普通文件那样通过指定路径来打开文件。
通常的做法是在父进程中创建管道,再创建子进程。
由于子进程继承了父进程打开的文件描述符,所以父子进程就可以通过创建的管道进行通信。
为了在父进程中创建管道,需要先定义一个包含两个元素的整型数组,用来存放管道读端和写端对应的文件描述符。
该数组在创建管道时作为参数传递。
要注意的是,管道是一种半双工方式,即对于进程来说,要么只能读管道,要么只能写管道。
不允许对管道又读又写。
其中数组的第一个元素固定代表管道的读端,第二个元素代表管道的写端。
对于一个进程来说,只会用到其中一个。
若读取管道时没有数据,则进程会被阻塞,直到有数据可读。
写管道时除非管道已满,一般情况下写入操作很快会返回。
这里假设父进程读管道,子进程写管道。
参考代码如下所示:
int pfd[2];
pid_t pid;
if(pipe(pfd)<0) // 创建管道失败
{
perror(“failtocreatepipe:
”);
exit(-1);
}
if((pid=fork())==-1) // 创建子进程失败
{
perror(“failtocreatechildprocess:
”);
exit(-1);
}
elseif(pid==0) // 子进程
{
close(pfd[0]); // 子进程关闭读端
……
写管道
……
}
else // 父进程
{
close(pfd[1]); // 父进程关闭写端
……
读管道
……
}
4.进程间通信请点评
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcessCommunication)。
如下图所示。
图30.6.进程间通信
4.1.管道请点评
管道是一种最基本的IPC机制,由pipe函数创建:
#include
intpipe(intfiledes[2]);
调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。
所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。
pipe函数调用成功返回0,调用失败返回-1。
开辟了管道之后如何实现两个进程间的通信呢?
比如可以按下面的步骤通信。
图30.7.管道
1.父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。
2.父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
3.父进程关闭管道读端,子进程关闭管道写端。
父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
例30.7.管道
#include
#include
#defineMAXLINE80
intmain(void)
{
intn;
intfd[2];
pid_tpid;
charline[MAXLINE];
if(pipe(fd)<0){
perror("pipe");
exit
(1);
}
if((pid=fork())<0){
perror("fork");
exit
(1);
}
if(pid>0){/*parent*/
close(fd[0]);
write(fd[1],"helloworld\n",12);
wait(NULL);
}else{/*child*/
close(fd[1]);
n=read(fd[0],line,MAXLINE);
write(STDOUT_FILENO,line,n);
}
return0;
}
使用管道有一些限制:
∙两个进程通过一个管道只能实现单向通信,比如上面的例子,父进程写子进程读,如果有时候也需要子进程写父进程读,就必须另开一个管道。
请读者思考,如果只开一个管道,但是父进程不关闭读端,子进程也不关闭写端,双方都有读端和写端,为什么不能实现双向通信?
∙管道的读写端通过打开的文件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那里继承管道文件描述符。
上面的例子是父进程把文件描述符传给子进程之后父子进程之间通信,也可以父进程fork两次,把文件描述符传给两个子进程,然后两个子进程之间通信,总之需要通过fork传递文件描述符使两个进程都能访问同一管道,它们才能通信。
使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):
1.如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
2.如果有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
3.如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。
在第33章信号会讲到怎样使SIGPIPE信号不终止进程。
4.如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。
管道的这四种特殊情况具有普遍意义。
在第37章socket编程要讲的TCPsocket也具有管道的这些特性。
Linux下进程间通信机制
(二)Pipe和Fifo
分类:
LinuxC2011-08-2317:
2740人阅读评论(0)收藏举报
无名管道pipe:
viewplaincopytoclipboardprint?
1.#include
2.#include
3.#include
4.#include
5.#include
6.#define N 128
7.
8.int main()
9.{
10. char buf_w[N];
11. char buf_r[N];
12. int fd[2];
13. int pid;
14. pipe(fd); //管道基于文件描述符
15. if((pid=fork()) == -1)
16. {
17. perror("fork");
18. exit(-1);
19. }
20.
21. if(pid == 0)
22. {
23. close(fd[0]);
24. while
(1)
25. {
26. while(fgets(buf_w, N, stdin) !
= NULL)
27. //注意fgets函数的用法,谨慎处理字符串中的'\0'和'\n'
28. {
29. printf("read from child\n");
30. buf_w[strlen(buf_w)-1] = 0;//去掉字符串中的'\n'
31. write(fd[1], buf_w ,strlen(buf_w));
32. }
33. }
34. }
35. else
36. {
37. close(fd[1]);
38. int n;
39. while
(1)
40. {
41. if((n = read(fd[0], buf_r, N)) >0)
42. {
43. buf_r[n] ='\n';
44. write(1, buf_r, n+1);
45. printf("write form parent%d\n",n);
46. }
47. sleep(3);
48. }
49. }
50. exit(0);
51.}
有名管道fifo:
viewplaincopytoclipboardprint?
1.//createfifo.c
2.
3.#include
4.#include
5.#include
6.#include
7.#include
8.#include
9.#include
10.
11.int main()
12.{
13. if(mkfifo("fifo.demo", 0666) == -1)
14. {
15. perror("mkfifo");
16. exit(-1);
17. }
18. return 0;
19.}
20.
21.
22.//writefifo.c
23.//先执行writefifo,不执行readfifo,则阻塞在open函数(可以增加参数,实现非阻塞)
viewplaincopytoclipboardprint?
1.#include
2.#include
3.#include
4.#include
5.#include
6.#include
7.#define N 64
8.
9.int main()
10.{
11. int fd;
12. char buf[N];
13. if((fd=open("fifo.demo", O_WRONLY)) == -1)
14. {
15. perror("open fifo.demo");
16. exit(-1);
17. }
18.
19. while
(1)
20. {
21. if(fgets(buf, N, stdin) !
= NULL)
22. {
23. write(fd, buf, strlen(buf));
24. if(strncmp(buf,"exit", 4) == 0)//比较前四个字符,buf第五个字符是'\n'
25. {
26. close(fd);
27. exit
(1);
28. }
29. }
30. }
31.}
32.
33.
34.
35.//readfifo.c
36.//先执行readfifo,不执行writefifo,则阻塞在open函数(可以增加参数,实现非阻塞)
37.
38.
39.#include
40.#include
41.#include
42.#include
43.
44.
45.#define N 64
46.
47.int main()
48.{
49. int fd;
50. char buf[N];
51. int n;
52.
53. if((fd=open("fifo.demo", O_RDONLY))== -1)
54. {
55. perror("open fifo.demo");
56. exit(-1);
57. }
58.
59. while
(1)
60. {
61. if((n = read(fd, buf, N)) >= 0)
62. {
63. if(n == 0)
64. {
65. exit
(1);
66. }
67. write(1, buf, n);//读多少,写多少
68. }
69. }
70.}
两个进程间通过管道拷贝数据:
viewplaincopytoclipboardprint?
1.//readfile.c
2./*读文件,写管道*/
3.//先调用mkfifo命令创建一个fifo文件
4.
5.
6.#include
7.#include
8.#include
9.#include
10.#include
11.#include
12.#include
13.
14.
15.#define N 128
16.int main(int argc, char *argv[])
17.{
18. int n;
19. int fd;
20. int filefd;
21. char buf[N];
22.
23. //
24. if(argc !
= 2)
25. {
26. fprintf(stderr,"usage:
%s
27. perror("");
28. exit(-1);
29. }
30.
31.
32. //open source file
33. if((filefd=open(argv[1],O_RDONLY)) ==-1)
34. {
35. perror("open filefd");
36. exit(-1);
37. }
38.
39. /*create fifo
40. if(mkfifo("copyfifo",0666) ==-1)
41. {
42. perror("mkfifo");
43. exit(-1);
44. }*/
45.
46. //open fifo
47. if((fd = open("copyfifo",O_WRONLY))==-1)
48. {
49. perror("open copyfifo");
50. exit(-1);
51. }
52.
53.
54. while((n=read(filefd, buf, N)) >=0)
55. {
56. if(n == 0)
57. {
58. close(fd);
59. close(filefd);
60. }
61. else
62. {
63. write(fd, buf, n);//注意读多少,写多少
64. }
65.
66. }
67.
68.}
69.
70.//writefile.c
71./*读管道,写文件*/
72.#include
73.#include
74.#include
75.#include
76.#include
77.#include
78.#include
79.
80.
81.#define N 128
82.int main(int argc, char *argv[])
83.{
84. int n;
85. int fd;
86. int filefd;
87. char buf[N];
88.
89. //
90. if(argc !
= 2)
91. {
92. fprintf(stderr,"usage:
%s
93. perror("");
94. exit(-1);
95. }
96.
97.
98. //open source file
99. if((filefd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0666)) ==-1)
100. {
101. perror("open filefd");
102. exit(-1);
103. }
104.
105. //open fifo
106. if((fd = open("copyfifo",O_RDONLY))==-1)
107. {
108. perror("open copyfifo");
109. exit(-1);
110. }
111.
112.
113. while((n=read(fd, buf, N)) >=0)
114. //写端关闭,管道中没有文件时返回0,利用返回值0判断写端是否存在,不返回零没有数据则阻塞
115. {
116. if(n == 0)
117. {
118. close(fd);
119. close(filefd);
120. exit
(1);
121. }
122. else
123. {
124. write(filefd, buf, n);
125. }
126.
127. }
128.}
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 进程 通信