共享存储允许两个或多个进程共享一个给定的存储区,是进程间通信最快的一种方式。
不要同时对共享存储空间进行写操作,通常,信号量用于同步共享存储访问。
最简单的共享内存的使用流程
①ftok函数生成键值
②shmget函数创建共享内存空间
③shmat函数获取第一个可用共享内存空间的地址
④shmdt函数进行分离(对共享存储段操作结束时的步骤,并不是从系统中删除共享内存和结构)
⑤shmctl函数进行删除共享存储空间
1.ftok函数生成键值
每一个共享存储段都有一个对应的键值(key)相关联(消息队列、信号量也同样需要)。
所需头文件:#include<sys/ipc.h>
函数原型 :key_t ftok(const char *path ,int id);
path为一个已存在的路径名
id为0~255之间的一个数值,代表项目ID,自己取
返回值:成功返回键值(相当于32位的int)。出错返回-1
例如:key_t key = ftok( “/tmp”, 66);
2.shmget函数创建共享存储空间并返回一个共享存储标识符
所需头文件:#include<sys/shm.h>
函数原型: int shmget(key_t key, size_t size,int flag);
key为ftok生成的键值
size为共享内存的长度,以字节为单位
flag为所需要的操作和权限,可以用来创建一个共享存储空间并返回一个标识符或者获得一个共享标识符。
flag的值为IPC_CREAT:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则直接返回共享存储标识符。
flag的值为 IPC_CREAT | IPC_EXCL:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则产生错误。
返回值:成功返回共享存储ID;出错返回-1
例如:int id = shmget(key,4096,IPC_CREAT|IPC_EXCL|0666);创建一个大小为4096个字节的权限为0666(所有用户可读可写,具体查询linux权限相关内容)的共享存储空间,并返回一个整形共享存储标识符,如果key值已经存在有共享存储空间了,则出错返回-1。
int id = shmget(key,4096,IPC_CREAT|0666);创建一个大小为4096个字节的权限为0666(所有用户可读可写,具体查询linux权限相关内容)的共享存储空间,并返回一个共享存储标识符,如果key值已经存在有共享存储空间了,则直接返回一个共享存储标识符。
3.shmat函数获取第一个可用共享内存空间的地址
所需头文件:#include<sys/shm.h>
函数原型: void *shmat(int shmid, const void *addr, int flag);
shmid为shmget生成的共享存储标识符
addr指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
flag为对数据的操作,如果指定为SHM_RDONLY则以只读方式连接此段,其他值为读写方式连接此段。
翻阅linux下shm.c文件得到#define SHM_RDONLY 010000 /* read-only access */
返回值:成功返回指向共享存储段的指针;错误返回-1(打印出指针的值为全F)
例如:char *addr = shmat(id, NULL, 0);就会返回第一个可用的共享内存地址的指针的值给addr
4.shmdt函数进行分离
当不需要对此共享内存进行操作时候,调用shmdt函数进行分离,不是删除此共享存储空间哟。
所需头文件:#include<sys/shm.h>
函数原型: int shmdt(const void *addr);
addr为shmat函数返回的地址指针
返回值:成功返回0;错误返回-1
例如:int ret = shmdt(addr);
5.shmctl函数对共享内存进行控制
简单的操作就是删除共享存储空间了,也可以获取和改变共享内存的状态
所需头文件:#include<sys/shm.h>
函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid就是shmget函数返回的共享存储标识符
cmd有三个,常用删除共享内存的为IPC_RMID;IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中;IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内。(内核为每个共享存储段维护着一个结构,结构名为shmid_ds,这里就不讲啦,里面存放着共享内存的大小,pid,存放时间等一些参数)
buf就是结构体shmid_ds
返回值:成功返回0;错误返回-1
例如:int ret = shmctl(id, IPC_RMID,NULL);删除id号的共享存储空间
ps:在Linux下,比如你申请24字节大小的共享存储空间,系统还是会默认给你分配一页的大小,但你还是只能使用这一页上24字节的空间。使用getconf PAGE_SIZE 命令就能显示出一页的大小
使用ipcs -m可以查看当前系统所有的共享内存空间信息
如果你的程序创建了一个共享内存段,但没有销毁,可以使用命令ipcrm -m shmid命令删除共享内存段,不然程序再运行有可能出错。
下面用一个代码例子来使用共享内存
我创建了一个结构体,想让结构体存入共享内存。写了两个程序,service和client,代码基本相同,不同就是service程序的开始创建共享内存。这两个程序是一个死循环,让你选择是存数据还是读数据还是销毁共享内存。代码写的不是很精致,主要是为了练共享内存,见谅哈。
command.c文件,构造想存入的结构体,和共享内存的操作函数
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/shm.h>
#include "Command.h"
int sharememory(int ipc_size,int flag)
{
int id;
key_t key=ftok("/tmp",66);
if(key < 0)
{
printf("get key error\n");
return -1;
}
id = shmget(key,ipc_size,flag);
if(id < 0)
{
printf("get id error\n");
return -1;
}
return id;
}
int create_ipc(int ipc_size)
{
return sharememory(ipc_size,IPC_CREAT|IPC_EXCL|0666);
}
int get_ipc(int ipc_size)
{
return sharememory(ipc_size,IPC_CREAT|0666);
}
int destroy_sharememory(int id)
{
return shmctl(id,IPC_RMID,NULL);
}
command.h文件。好让service和client调用嘛,方便。
#define NAME_LEN 20
typedef struct {
char name[NAME_LEN];
int age;
}ckx;
int sharememory(int ipc_size,int flag);
int create_ipc(int ipc_size);
int get_ipc(int ipc_size);
service.c文件。创建共享内存空间啦,读写等
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdlib.h>
#include "Command.h"
int main()
{
int id=create_ipc(sizeof(ckx));
int i=0;
ckx *p;
if(id < 0)
{
printf("create sharememory error\n");
return 0;
}
id = 0;
while(1)
{
printf("\n\n1.input data to sharememory\n2.get sharememory data\n\
3.destroy sharememory\ninput select:");
scanf("%d",&i);
if(i > 3 |i< 1)
{
printf("input error\n");
continue;
}
id = get_ipc(sizeof(ckx));
if(id < 0)
{
printf("get sharememory error\n");
break;
}
p = (ckx *)shmat(id,NULL,0);
if(p < 0)
{
printf("get sharememory addr error\n");
p = NULL;
break;
}
if(i == 1)
{
char name[NAME_LEN];
int age=0;
printf("input name:");
fflush(stdin);
getchar();
gets(name);
printf("input age:");
scanf("%d",&age);
strcpy(p->name,name);
p->age = age;
printf("write success\n");
if(shmdt(p) == -1)
{
printf("shmdt error\n");
}
id = 0;
}
if(i == 2)
{
printf("name:%s \t age:%d\n",p->name,p->age);
if(shmdt(p) == -1)
{
printf("shmdt error\n");
break;
}
id = 0;
}
if(i == 3)
{
if(shmdt(p) == -1)
{
printf("shmdt error\n");
break;
}
break;
}
}
if(id !=0)
{
if(destroy_sharememory(id)<0)
{
printf("destroy error\n");
}
}
}
client.c基本上就和service.c代码差不多啦,只是想体现共享内存嘛,service读写和client读写,观察现象,体现共享内存
#include<stdio.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<stdlib.h>
#include "Command.h"
int main()
{
int i=0;
ckx *p;
int id = 0;
while(1)
{
printf("\n\n1.input data to sharememory\n2.get sharememory data\n\
3.destroy sharememory\ninput select:");
scanf("%d",&i);
if(i > 3 |i< 1)
{
printf("input error\n");
continue;
}
id = get_ipc(sizeof(ckx));
if(id < 0)
{
printf("get sharememory error\n");
break;
}
p = (ckx *)shmat(id,NULL,0);
if(p < 0)
{
printf("get sharememory addr error\n");
p = NULL;
break;
}
if(i == 1)
{
char name[NAME_LEN];
int age=0;
fflush(stdin);
getchar();
printf("input name:");
gets(name);
printf("input age:");
scanf("%d",&age);
strcpy(p->name,name);
p->age = age;
printf("write success\n");
if(shmdt(p) == -1)
{
printf("shmdt error\n");
}
id = 0;
}
if(i == 2)
{
printf("name:%s \t age:%d\n",p->name,p->age);
if(shmdt(p) == -1)
{
printf("shmdt error\n");
break;
}
id = 0;
}
if(i == 3)
{
if(shmdt(p) == -1)
{
printf("shmdt error\n");
break;
}
break;
}
}
if(id !=0)
{
if(destroy_sharememory(id)<0)
{
printf("destroy error\n");
}
}
}