什么是进程间通信
就是两个进程为了完成某个工作而需要互相交流,互相告知双方进度,结果。
原理:先让A,B进程都可以看到一份公共资源,一个往里面写,一个从里面读。
进程是具有独立性的,所以进程和进程之间无法通过交互访问地址来完成,
所以这份资源是由OS提供的内核缓冲区,让两个进程去访问这份公共资源。
通信方式的种类
管道(生命周期随进程)
匿名管道pipe(重点掌握)
命名管道(重点掌握)
System V IPC(生命周期随内核)
System V 消息队列(了解学习)
System V 共享内存(重点掌握)
System V 信号量(了解学习)
匿名管道
名字就告诉我们了,没有名字,那如何让两个进程看到同一份资源呢?
通过父子继承的方式,子进程以父进程的PCB为模板创建自己的PCB,从而也关联了父进程的打开文件(匿名管道)
也就是说:匿名管道仅仅限于由血缘关系的父子进程。而且必须在子进程创建之前去创建匿名管道
写一个简单的匿名管道通信,只供解释原理。
但是呢,还有很多种特殊的情况:
1,读端和写段都在未关闭的情况下,他们之间会相互阻塞。读端不读,读的少,写端会等。写端不写,写的慢,读端会等。
2,如果关闭读端,写端会受到13号信号。因为都不读了,写端没有意义了。
3,如果写端关闭,读端会读完缓冲区的内容,并且read()最后返回0
#include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 int main()
7 {
8 int fd_arr[2] = {0};
9
10 //创建成功后, 0是读端,1是写端
11 if(pipe(fd_arr) < 0)
12 {
13 perror("pipe");
14 exit(-1);
15 }
16
17 if(fork() == 0)
18 {
19 //子进程 去写,关闭相应的读端
20 close(fd_arr[0]);
21 const char* ch = "hello chen";
22 write(fd_arr[1],ch,strlen(ch));
23 }
24 else
25 {
26 //父进程,去读,关闭相应的写端
27 close(fd_arr[1]);
char ch[64] = {0};
29 ssize_t s = read(fd_arr[0],ch,64);
30 ch[s] = 0;
31
32 printf("child to father::%s\n",ch);
33
34 }
35
36
37
38 return 0;
39 }
~
命名管道
由于匿名管道仅仅限于有血缘关系的父子进程,所以引入了命名管道,它可以用于主机上任何进程的交换。
它是一种特殊的文件。匿名管道和命名管道本质原理是相同的:都是OS提供内核缓冲区
1,而且命名管道的文件名仅仅是缓冲区的标识符,如果正在通信的进程,就算删除了也不受影响。
2,大小永远是0(因为不会把数据刷新到管道文件里面),还有管道文件的作用仅仅是缓冲区的标识符。
通信原理:
让两个进程分别包含通一个.h头文件,通过宏定义让其做到看同一份资源的目的。
#include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <unistd.h>
8 #define MYFIFO "./myfifo //各自包含头文件,就做到了拿到管道文件的文件名
一个进程以写的方式打开管道文件
#include "commond.h"
3 int main()
4 {
5 int fd = open(MYFIFO,O_WRONLY);
6 if(fd < 0)
7 {
8 perror("opne fail");
9 exit(-1);
10 }
11
12 while(1)
13 {
14 printf("girlfriend say:#");
15 fflush(stdout);
16
17 char ch[64] = {0};
18 read(1,ch,64);
19 write(fd,ch,64);
20 }
21
22 return 0;
23 }
一个进程以读方式打开管道文件
#include "commond.h"2
3 int main()
4 {
5 umask(0); // 文件权限 = default & (~umask)6
7 if(mkfifo(MYFIFO,0666) < 0) //创建管道文件
8 {
9 perror("mkfifo");
10 exit(-1);
11 }
12
13 //我们只需要读就可以了
int fd = open(MYFIFO,O_RDONLY);
15 if(fd < 0)
16 {
17 perror("errror");
18 exit(-1);
19 }
20
21 while(1)
22 {
23 char ch[64] = {0};
24 ssize_t s = read(fd,ch,sizeof(ch));
25 if(s < 0)
26 {
27 perror("read fail");
exit(-1);
29 }
30 else if(s == 0)
31 {
32 printf("girl go away\n");
33 break;
34 }
35 else
36 {
37 ch[s-1] = 0; //会把\n也读进去38
printf("girlfriend say to boy:%s",ch);
39 }
40 }
41
42
43 close(fd);
44 return 0;
45 }
共享内存
通信速度最快
会存在多个,进程A和B,C和D,,,,都要通信,意味着共享内存会存在多份。
OS如何管理呢? 先描述后组织,对共享内存的描述肯定是在结构体里面。
A和B,C和D如何保证能准确看到对应的共享内存呢? 所以共享内存的结构体肯定封装了一个唯一标识符。
因为共享内存是OS提供的,所以它的生命周期是随内核的。如何删除共享内存呢?函数调用(最终会去调用系统调用),OS重启
如何让进程通过共享内存来交互呢?
1,通过某种系统调用,在内存中创建一份内存空间。
2,通过某种调用,让要通信的进程挂接到这份空间上。
3,通信完成后去掉挂接。
4,释放共享内存。
先看看申请共享内存的系统调用
其他的共享内存操作:
.h头文件
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <sys/ipc.h>
8 #include <sys/shm.h>
9
10
11 #define MYPATH "./"
12 #define PR0_ID 0x11223344
server.c
#include "commond.h"
2
3
4 int main()
5 {
6
7 key_t key = ftok(MYPATH,PR0_ID); //生成OS层面共享内存的标识符
8 if(key < 0)
9 {
10 perror("ftok");
11 exit(-1);
12 }
13
14 //printf("key::%d\n",key);
15
16 //生成共享内存,生成全新的,并且指明权限(类似于文件)
17 int shmid = shmget(key,4096,IPC_CREAT | IPC_EXCL | 0664);
18 if(shmid < 0)
19 {
20 perror("shmget");
21 exit(-1);
22 }
23
24 //挂接
25 char* shmsg = (char*)shmat(shmid,NULL,0);
26 //printf("挂接成功\n");
27 sleep(15);
//读出共享内存的数据
30 while(1)
31 {
32 printf("%s\n",shmsg);
33 sleep(1);
34 }
35
36
37 //去挂接
38 shmdt(shmsg);
39
40 //删除共享内存
41 shmctl(shmid,IPC_RMID,NULL);
42 return 0;
43 }
client.c
#include "commond.h"
2
3
4 int main()
5 {
6 //获得相同的共享内存标识符
7 key_t key = ftok(MYPATH,PR0_ID);
8 if(key < 0)
9 {
10 perror("ftok");
11 exit(-1);
12 }
13
14 int shmid = shmget(key,4096,IPC_CREAT);//如果存在则返回已经存在的
15
16 //挂接
17 char* shmsg = (char*)shmat(shmid,NULL,0);
18 sleep(10);
19
20 //写入共享内存的数据
21 char c = 'A';
22 while(c <= 'Z')
23 {
24 shmsg[c-'A'] = c;
25 c++;
26 shmsg[c-'A'] = 0;
27 sleep(2);
}
29
30 //去挂接
31 shmdt(shmsg);
32
33 //我们不需要去删除
34
35 return 0;
36 }