概念
我们知道进程间的通信有管道的方式进程通信管道制作_云的小站的博客-CSDN博客
但是我们的管道通信其实属于一种取巧的方式,利用了打开的文件可读写的特性上,两个进程对此分别进行读写操作就会产生所谓的通信现象,但是外面的管道依旧得再磁盘上创建一个文件,然后文件打开加载在内存上才可以使用,那么有什么方法可以直接加载在内存,而无需对磁盘操作呢?
---------利用systemV共享内存通信
利用系统函数shmget在内存中开辟一块空间用于进程通信的交互点。因为两个进程之间不可以直接通信,所以需要加一个中间层。
学计算机,当两个东西不通的时候,我们最经常干的事情就是加一个中间层,既保护了独立性,又处理了我们需要完成的事情
工具
shmget(创建共享内存)
shmid_t shmget(key_t key, size_t size, int shmflg);
返回值:
失败返回-1,成功返回一个shmid唯一的共享内存标识符。
key:
唯一值,两个进程需要同一个key值才可以访问到同一共享内存空间。
size:
设置共享内存大小
shmflg
共享内存创建选项,IPC_CREAT | IPC_EXCL|0666
IPC_CREAT:如果内存存在key标识符的共享内存直接使用,如果不存在就创建
IPC_EXCL:如果内存存在key标识符的共享内存直接失败退出。
0666:共享内存的使用权限。
shmctl(操作共享内存)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
返回值:
如果操作成功返回0,操作失败返回-1;
shmid:
对该shmid的共享内存进行操作;
cmd:
对共享内存的操作选项,一般我们删除共享内存用的使用:IPC_RMID
buf:
获取该共享内存的数据结构地址。(共享内存也需要被描述组织)。
shmat(进程的挂接操作)
void *shmat(int shmid, const void *shmaddr, int shmflg);
返回值:
成功返回共享内存的地址,失败返回-1,并设置errno错误原因。
shmid:
挂接在shmid标识符的共享内存上。
shmaddr:
理论上可以自己设置将共享内存挂接在自己指定的位置,但是不建议。所以一般设置为nullptr,
shmflg
设置访问方式只读、只写等等。设置为0,读写操作。
shmdt(进程的拆除操作)
int shmdt(const void *shmaddr);
返回值:
成功拆除返回0,失败返回-1;
shmaddr:
将进程地址空间shmaddr的共享内存,进行拆除。
ftok(生成一个key值为shmget服务)
key_t ftok(const char *pathname, int proj_id);
返回值:
返回有个随机数字。
pathname:
传一个文件路径,使用文件的inode值,对了文件必须要有访问权限。
proj_id:
项目id,0~255,其实可以随便写,会截断。
ps:ftok不进行系统调用,他只是一个算法
代码
来来来,工具说完了直接上代码:
头文件
日志+需要的接口
日志
1 #ifndef _LOG_H_
2 #define _LOG_H_
3 #include <ctime>
4 #include <iostream>
5
6 #define Debug 0
7 #define Notice 1
8 #define Warning 2
9 #define Error 3
10
11 std::string mgs[] = {
12 "Debug",
13 "Notice",
14 "Warning",
15 "Error"};
16
17 std::ostream &Log(std::string message, int level)
18 {
19 std::cout << " | " << (unsigned)time(nullptr)
20 << " | " << mgs[level] << " | "<<message;
21 return std::cout;
22 }
23
24 #endif
~
所需头文件
1 #pragma once
2 #include<iostream>
3 #include<cstdio>
4 #include<sys/shm.h>
5 #include<sys/ipc.h>
6 #include<sys/types.h>
7 #include"Log.hpp"
8 #include<assert.h>
9 #include<unistd.h>
10 using namespace std;
11 #define SIZE 4096
12 #define PATH_NAME "."
13 #define PROJ_ID 0x66
14
15
16 string TransToHex(key_t k)
17 {
18 char buffer[32];
19 snprintf(buffer,sizeof(buffer),"0x%x",k);
20 return buffer;
21 }
接收方代码
#include"comm.hpp"
2
3 int main()
4 {
5 // 1.创建公共key
6 key_t k=ftok(PATH_NAME,PROJ_ID);
7 assert(k!=-1);
8 Log("create key done",Debug)<<" sgmrevice key: "<< TransToHex(k)<<endl;
9 sleep(10);
10 //2.创建共享内存
11
12 int shmid = shmget(k,SIZE,IPC_CREAT | IPC_EXCL|0666 );
13 if(shmid==-1)
14 {
15 perror("shmger fail!!\n");
16 exit(-1);
17 }
18
19 Log("create shm done",Debug)<<" sgmrevice shmid: "<<shmid<<endl;
20
21
22 sleep(10);
23 //挂接内存
24 char*shmaddr=(char*)shmat(shmid,nullptr,0);
25 if(*shmaddr==-1)
26 {
27 perror("shmat fail!\n");
28 }
29 Log("shmat shm seccese",Debug)<<" sgmrevice shmid: "<<shmid<<endl;
30 sleep(10);
31
32 //工作
33
34 //拆除链接
35 int n=shmdt(shmaddr);
36 assert(n!=-1);
37 (void)n;
38 Log("shmdt shm done",Debug)<<" sgmrevice shmid: "<<shmid<<endl;
39
40 sleep(10);
41 // n.删除共享内存
42 n=shmctl(shmid,IPC_RMID,nullptr);
43 assert(n!=-1);
44 (void)n;
45 Log("delete shm done",Debug)<<" sgmrevice shmid: "<<shmid<<endl;
46
47 return 0;
48 }
~
发送方代码
1 #include"comm.hpp"
2 int main()
3 {
4 //获取k值
5 key_t k=ftok(PATH_NAME,PROJ_ID);
6 assert(k!=-1);
7 (void)k;
8 Log("create key done 郑建云",Debug)<<"key:"<<TransToHex(k)<<endl;
9 sleep(10);
10 //创建共享内存
11 //由于在receive已经创建共享内存,所以我们主要是找到内存挂接
12 //IPC_CREAT==0
13 int shmid = shmget(k,SIZE,IPC_CREAT);
14 if(!shmid)
15 {
16 perror("shmget fail!!\n");
17 exit(-1);
18 }
19
20 Log("create shm domw",Debug)<<" shmid: "<<shmid<<endl;
21 int cnt=10;
22 while(cnt)
23 {
24 printf("time:%d\r",cnt--);
25 sleep(1);
26 fflush(stdout);
27 }
28 printf("\n");
29 //挂接,shmaddr,不要自己写,避免把其他数据覆盖
30 char*shmaddr=(char*)shmat(shmid,nullptr,0);
31 if(shmaddr==nullptr)
32 {
33 perror("shmat fail\n");
34 exit(-1);
35 }
36 Log("shmat sueecss done",Debug)<<"shmid: "<<shmid<<endl;
37 sleep(10);
38
39 //工作
40
41 //拆除共享内存
42 int n=shmdt(shmaddr);
43 printf("n=%d\n",n);
44 assert(n==0);
45 (void)n;
46 Log("shmdt success done",Debug)<<" shmid "<<shmid<<endl;
47 printf("%p\n",shmaddr);
48 return 0;
49 }
执行顺序必须先执行接收代码,因为接收代码必须先创建好共享内存,然后发送方才可以链接。
否者,发送方先建立了,在执行中接收方将检查出内存中已经存在该key的共享内存,直接报错,返回一个-1到shmid。