以下用到的消息队列中的相关内容,均可在这里找到:https://blog.youkuaiyun.com/sandmm112/article/details/79936107
共享内存是进程间通信的一种方式。系统中物理内存结构及进程与共享区的映射关系如下:
创建一个共享内存是指在共享取开辟一片区域,将该区域分别映射到两个进程的虚拟地址空间中,那这两个进程便可以通过这片共享区域进行通信了。因为共享内存位于用户空间中,所以每次进行通信时,不会像消息队列一样先将一个进程中用户空间中的数据拷贝到内核,再将内核中的数据拷贝到另一进程的用户空间中。这样便可不在涉及到内核,所以说共享内存是最快的进程间通信方式。
但是共享内存并没有提供同步与互斥机制,所以当多个进程同时访问共享内存时可能会数据的二义性。
注意:共享内存的的生命周期也是随内核的。
以下来介绍一些有关共享内存的函数,来进一步理解共享内存:
1. 共享内存与消息队列一样,首先需要一个key值来创建共享内存。key值的获取方法与消息的队列中的方法相同
2. 创建共享内存
int shmget(key_t key,size_t size,int shflg);//头文件:<sys/ipc.h>,<sys/shm.h>
参数:
key:ftok函数返回的key值
size:创建的共享内存的大小
shmflg:用法与消息队列中相同
返回值:成功返回一个非负整数即共享内存的标识符,失败返回-1。
共享内存创建成功后,操作系统会给该共享内存维护一个结构体struct shmid_ds,保存它的基本信息,结构体中的内容类似消息队列的结构体struct msqid_ds
3. 将共享内存映射到进程的地址空间
void* shmat(int shmid,const char* shmaddr,int shmflg);
参数:
shmid:共享内存标识符
shmaddr:指定要连接的地址,为NULL时,自动选择一个地址
shmflg:设置共享内存特性,一般为0
返回值:成功返回一个指向共享内存的指针,失败返回-1
4. 断开共享内存与进程的映射关系
int shmdt(const char* shmaddr);
参数:由shmat返回的指针
返回值:成功返回0,失败返回-1
注意:将共享内存与进程脱离并不是删除共享内存
5. 控制共享内存
int shmctl(int shmid,int cmd,struct shmid_da* buf);
参数:
shmid:共享内存的标识符
cmd:对共享内存所要采取的操作,取值与消息队列msgctl函数中的参数cmd相同。
buf:指向共享内存结构体的指针
返回值:成功返回0,失败返回-1
以下通过代码来演示如何利用共享内存来进行进程间通信:
先封装有关共享内存操作的方法:
头文件comm.h:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
//异常退出宏函数
#define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
//创建共享内存
int CreateShm(size_t size);
//获取共享内存
int GetShm(size_t size);
//挂接共享内存
int CatShm(int shmid,void* addr);
//断开共享内存
int DtShm(const void* addr);
//销毁共享内存
int DestoryShm(int shmid);
封装方法comm.c
#include "comm.h"
//创建/获取共享内存
static int CommShm(int size,int flags)
{
//创建进程间通信标识符
key_t key = ftok(PATHNAME,PROJ_ID);
if(key < 0)//创建失败
{
ERR_EXIT("ftok error");
}
int shmid = shmget(key,size,flags);//创建或获取共享内存
if(shmid < 0)//共享内存创建失败
{
ERR_EXIT("shmid error");
}
return shmid;//返回共享内存标识符
}
//创建共享内存
int CreateShm(size_t size)
{
return CommShm(size,IPC_CREAT|IPC_EXCL|0666);
}
//获取共享内存
int GetShm(size_t size)
{
return CommShm(size,IPC_CREAT);
}
//断开共享内存
int DtShm(const void* addr)
{
int ret = shmdt(addr);
if(ret < 0)//断开失败
{
printf("shmid error\n");
return -1;
}
return 0;
}
//销毁共享内存
int DestoryShm(int shmid)
{
int ret = shmctl(shmid,IPC_RMID,NULL);
if(ret < 0)
{
printf("shmctl error\n");
return -1;
}
return 0;
}
在一个进程中创建共享内存并将其映射到该进程中接收消息:server.c
#include "comm.h"
int main()
{
//创建共享内存
int shmid = CreateShm(4097);
//挂解共享内存
char* addr = shmat(shmid,NULL,0);
sleep(2);
int i = 0;
printf("client say#\n ");
sleep(2);
while(i < 26)
{
printf("%s\r",addr);
i++;
fflush(stdout);
sleep(1);
}
printf("\n");
//断开共享内存
shmdt(addr);
sleep(2);
//删除共享内存
DestoryShm(shmid);
return 0;
}
在另一进程中获取共享内存并映射到该进程用于发送信息:client.c
#include "comm.h"
int main()
{
int shmid = GetShm(4096);//获取共享内存
//挂接共享内存
char* addr = shmat(shmid,NULL,0);
int i = 0;
while(i < 26)
{
addr[i] = 'A'+ i;
i++;
addr[i] = 0;
sleep(1);
}
shmdt(addr);
sleep(1);
return 0;
}
分别在两个终端下运行两个进程,得到如下结果:[admin@localhost ShareMemory]$ ./server
client say#
ABCDEFGHIJKLMNOPQRSTUVWXYZ //各个字符会一次输出一个
[admin@localhost ShareMemory]$
[admin@localhost ShareMemory]$ ./client
[admin@localhost ShareMemory]$
同样的,可以通过如下命令来查看和删除共享内存:
ipcs -m //查看共享内存
ipcrm -m 共享内存标识符 //删除指定标识符的共享内存