一.什么是共享内存
共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说就是,进程不再通过执行进入内核的系统调用来传递彼此的数据
它的工作原理可以用下图来解释:
两个不同的进程通过页表映射将其中的内容存储到一块相同的物理内存中,这一块内存就是共享内存。
共享内存的数据结构/usr/include/linux/shm.h
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
二.关于共享内存的函数
为了模拟共享内存,我们设计了一个测试用例来测试。下面先介绍一下几个必要的函数。
1 shmget函数
返回值:成功返回一个非负整数,即该共享内存的标识码;失败返回-1。
2 shmctl函数,控制共享内存
shmid:有shmget返回的一个共享内存标识符。
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构(通常设为NULL)
cmd:有三个选项
- IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值
- IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除消息队列
3.shmat(),shmdt(),共享内存操作函数
其中shmat函数的参数意义如下:shm_id:由shmget函数返回的共享内存标识。
shm_addr:指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
shm_flg:是一组标志位,通常为0。
shmat返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1。
shmdt返回值:成功返回0,失败返回-1。
三.用共享内存实现客户端client和服务端server的通信
common.h
#pragma once
#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <unistd.h>
#define PATHNAME "."
#define PROJ_ID 0x666
int CreateShm(int size);
int GetShm(int size);
int DestroyShm(int shmid);
common.c
#include "common.h"
static int CommonShm(int size,int flags)
{
key_t key=ftok(PATHNAME,PROJ_ID);
if(key<0)
{
perror("ftok");
return -1;
}
int shmid=shmget(key,size,flags);
if(shmid<0)
{
perror("shmget");
return -2;
}
return shmid;
}
int CreateShm(int size)
{
return CommonShm(size,IPC_CREAT|IPC_EXCL|0666);
}
int GetShm(int size)
{
return CommonShm(size,IPC_CREAT);
}
int DestroyShm(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL)<0)
{
perror("shmctl");
return -1;
}
return 0;
}
server.c
#include "common.h"
int main()
{
int shmid=CreateShm(4096);//服务端创建一块共享内存,大小自己定
char *addr=shmat(shmid,NULL,0);//将服务端与共享内存连接起来,addr指向该共享内存
int i=0;
while(i++<26)
{
printf("client# %s\n",addr);//将共享内存中的内容输出来
sleep(1);
}
shmdt(addr);//将共享内存与服务端断开连接
DestroyShm(shmid);
return 0;
}
client.c
#include "common.h"
int main()
{
int shmid = GetShm(0);//客户端获取已存在的共享内存时size传0
char *addr=shmat(shmid,NULL,0);//客户端和共享内存相连接
int i=0;
for(;i<26;++i)
{
addr[i]='A'+i;//客户端给共享内存中写数据
printf("%s\n",addr);//输出共享内存中的内容
sleep(1);
}
shmdt(addr);
return 0;
}
运行的结果如下图
服务端一运行就一直在等待客户端向共享内存里写数据,客户端一旦往共享内存中写入数据,服务端就打出客户端往共享内存中写入的数据。
和消息队列类似的,也有相关命令来查看和删除系统中的共享内存
查找共享内存:ipcs -m
删除共享内存:ipcrm -m + shmid
下面总结一下共享内存的特点:
- 共享内存没有同步与互斥机制
- 共享内存实现的是双向通信
- 共享内存可以用于任何进程间的通信
- 共享内存不再面向字节流或是面向数据块的概念,它只是一块内存,可以随意在上面
- 共享内存的生命周期是随内核