进程是一个独立的资源分配单位,每个进程的资源都是独立的,有独立的用户地址空间,当有多个进程同时存在时,当有一个进程突然出现异常时,其它的进程是不受影响的.那么,有时候就需要不同进程的信息进行交换或传递才可以工作.所以就出现了进程间通信.
进程间通信的目的:
- 数据传输:一个进程需要将它的数据发送给另一个进程.
- 资源共享:多个进程之间共享同样的资源(比如:共享同一段内存).
- 通知事件:一个进程需要向其它进程发送消息,通知它们发生的事件.(比如:子进程在退出时需要向父进程发送自己的退出信息)
- 进程控制:有些进程需要完全控制另一个进程的执行.
匿名管道
实质:就是操作系统向内核申请的一段内存,这段内存不同的进程都可以访问.
1.匿名管道(pipe)
函数:
#include<unistd.h>
功能:创建一个无名管道.但是进程间必须有亲缘关系.
int pipe(int fd[2]);
fd:文件描述符数组.其中fd[0]表示读端,fd[1]表示写端(可将1看做一支笔,便于记忆~~~)
返回值:成功返回0,失败返回错误码.
创建管道失败的原因: - 内存不够 - 文件描述符已达到上限,无法再打开文件描述符 |
简单的画一下管道的通信:
管道的特点: - 管道是单向通信的,只能一端进行读操作,另一端进行写操作.如果想要双向通信,那么就要创建两个管道. - 使用匿名管道的进程间具有亲缘关系. - 管道自带同步与互斥事件. - 进程退出,管道退出,管道的生命周期随进程. - 管道是提供面向字节流的通信. |
下面是代码实现:
1.父进程关闭读端,子进程关闭了写端.
父进程一直写,子进程一直读.
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/wait.h>
5
6 int main()
7 {
8 int fd[2];
9 int ret = pipe(fd);
10 if(ret == -1)
11 {
12 perror("pipe");
13 return 1;
14 }
15 ret = fork();
16 if(ret == 0)
17 {
18 //子进程关闭了写端
19 close(fd[1]);
20 char buf[100];
21 while(1)
22 {
23 ssize_t size = read(fd[0],buf,sizeof(buf) - 1);
24 if(size > 0)
25 {
26 buf[size] = '\0';
27 printf("child sys:%s\n",buf);
28 }
29 }
30 }
31 else if(ret > 0)
32 {
33 //父进程关闭了读端
34 close(fd[0]);
35 const char* msg = "I am child....\n";
36 while(1)
37 {
38 write(fd[1],msg,strlen(msg));
39 sleep(1);
40 }
41 }
42 int status = 0;
43 wait(&status);
44 printf("sig:%d\n",status&0x7F);
45 return 0;
46 }
2.父进程关闭了写端,子进程关闭了读端.过来一会儿后,父进程也关闭了读端.而子进程却一直在写.那么此时子进程就会收到一个SIGPIPE信号,导致进程终止.
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/wait.h>
5
6 int main()
7 {
8 int fd[2];
9 int ret = pipe(fd);
10 if(ret == -1)
11 {
12 perror("pipe");
13 return 1;
14 }
15 ret = fork();
16 if(ret > 0)
17 {
18 //父进程关闭了写端
19 close(fd[1]);
20 char buf[100];
21 int i = 0;
22 while(i < 5)
23 {
24 ++i;
25 ssize_t size = read(fd[0],buf,sizeof(buf) - 1);
26 if(size > 0)
27 {
28 buf[size] = '\0';
29 printf("child sys:%s\n",buf);
30 }
31 }
32 close(fd[0]);
33 }
34 else if(ret == 0)
35 {
36 //子进程关闭了读端
37 close(fd[0]);
38 const char* msg = "I am child....\n";
39 while(1)
40 {
41 write(fd[1],msg,strlen(msg));
42 sleep(1);
43 }
44 }
45 int status = 0;
46 wait(&status);
47 printf("sig:%d\n",status&0x7F);
48 return 0;
49 }
如果此时,父进程没有关闭读端,而是子进程在写了一会儿以后关闭了自己的写端,那么父进程在读取时就像读到了文件的结尾一样.读端就会阻塞着去读取.
下面来讲述一下管道的自带同步互斥机制是什么意思?
在上述两个例子的基础上,再进行拓展:
情况1:读端不读,写端一直写
如果父进程fork()一个子进程后,父进程的读端打开却一直不读,而子进程的写端却一直在写.那么此时当管道被写满时,再进行写操作时就无法进行,就要一直阻塞的等待读取后空出位置才能写.
情况二:读端一直读,写端不写.
与情况一相似,那么此时读端就会一直阻塞的等待有东西写进管道.
命名管道
命名管道和匿名管道的区别: 1.命名管道:是一种特殊的文件存在于磁盘上;而匿名管道是内核中的一段内存. 2.命名管道可以用于任意进程间的通信;而匿名管道只能用于具有亲缘关系的进程间通信. 3.命名管道和匿名管道的创建与打开方式不同.命名管道当读写端都打开时,才会在内存中资源. |
其它特性都与匿名管道相同.
创建命名管道的函数:
int mkfifo(const char* filename,mode_t mode);
注意:filename相同,文件相同.
简单的代码实例,两个进程间的通信,若需要请戳
该实例在两个文件同时执行时,在两个不同的会话中执行.就可以实现两个进程的通信.