概述
进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址。共享内存并未提供同步机制,使用信号量进行同步。
共享内存的特点:
1)共享内存是进程间共享数据的一种最快的方法。
一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
2)使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。
若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。
申请共享内存步骤
- 创建或者打开,shmget
- 映射,shmat
- 解除映射,shmdt
- 释放,shmctl
相关shell命令
Ipcs ipcmk ipcrm
查看共享内存
ipcs -m
key shmid owner perms bytes nattch status
0x00000000 3309582 fengqian 600 524288 2 dest
0x00000000 18022415 fengqian 600 524288 2 dest
0x0119079d 36044816 fengqian 600 1024 1
nattch:当前有多少个进程映射到这块个共享内存,即调用shmat后+1,shmdt后-1。
status:状态,空或者dest
注意:
- 调用shmctl销毁内存,状态改为dest,不会立即销毁。 当nattch = 0时,销毁共享内存块。
- 若没调用shmctl销毁内存,即使nattch=0,共享内存块仍然还在。
- 1个进程多同1个内存多次shmat,nattch累加多次。
- 没有shmdt的,程序退出时会解除映射。
相关函数
Ftok函数
key_t ftok(const char *pathname, int proj_id);
pathname: 指定的文件名,该文件必须是存在而且可以访问
proj_id:子序号,只有8个比特被使用(0-255)
当成功执行时,返回一个key_t值,失败返回-1
实际应 用中,很容易产生的一个理解是,在proj_id相同的情况下,只要文件(或目录)名称不变,就可以确保ftok返回始终一致的键值。然而,这个理解并非 完全正确,有可能给应用开发埋下很隐晦的陷阱。因为ftok的实现存在这样的风险,即在访问同一共享内存的多个进程先后调用ftok函数的时间段中,如果 pathname指定的文件(或目录)被删除且重新创建,则文件系统会赋予这个同名文件(或目录)新的i节点信息,于是这些进程所调用的ftok虽然都能 正常返回,但得到的键值却并不能保证相同。由此可能造成的后果是,原本这些进程意图访问一个相同的共享内存对象,然而由于它们各自得到的键值不同,实际上 进程指向的共享内存不再一致;如果这些共享内存都得到创建,则在整个应用运行的过程中表面上不会报出任何错误,然而通过一个共享内存对象进行数据传输的目 的将无法实现。
文件删除创建后inode不一样,参考inode原理。
1.创建共享内存
int shmget(key_t key, size_t size,int shmflg);
key:进程间通信键值,ftok() 的返回值。
size:该共享存储段的长度(字节)。
shmflg:标识函数的行为及共享内存的权限,其取值如下:
IPC_CREAT:如果不存在就创建
IPC_EXCL: 如果已经存在则返回失败
位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,格式和 open() 函数的 mode_t 一样,但可执行权限未使用。
2.共享内存映射
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:
将一个共享内存段映射到调用进程的数据段中。简单来理解,让进程和共享内存建立一种联系,让进程某个指针指向此共享内存。
参数:
shmid:共享内存标识符,shmget() 的返回值。
shmaddr:共享内存映射地址(若为 NULL 则由系统自动指定),推荐使用 NULL。
shmflg:共享内存段的访问权限和映射条件( 通常为 0 ),具体取值如下:
0:共享内存具有可读可写权限。
SHM_RDONLY:只读。
SHM_RND:(shmaddr 非空时才有效)
返回值:
成功:共享内存段映射地址( 相当于这个指针就指向此共享内存 )
失败:-1
3.解除映射
int shmdt(const void *shmaddr);
功能:
将共享内存和当前进程分离( 仅仅是断开联系并不删除共享内存,相当于让之前的指向此共享内存的指针,不再指向)。
参数:
shmaddr:共享内存映射地址。
返回值:
成功:0
失败:-1
4.共享内存控制
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:
共享内存属性的控制。
参数:
shmid:共享内存标识符。
cmd:函数功能的控制,其取值如下:
IPC_RMID:删除。(常用 )
IPC_SET:设置 shmid_ds 参数,相当于把共享内存原来的属性值替换为 buf 里的属性值。
IPC_STAT:保存 shmid_ds 参数,把共享内存原来的属性值备份到 buf 里。
SHM_LOCK:锁定共享内存段( 超级用户 )。
SHM_UNLOCK:解锁共享内存段。
SHM_LOCK 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。
buf:shmid_ds 数据类型的地址,用来存放或修改共享内存的属性。
返回值:
成功:0
失败:-1
示例代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
//IPC_CREAT 如果不存在就创建
//IPC_EXCL 如果已经存在则返回失败
#define SHM_MODE (SHM_R | SHM_W | IPC_CREAT)
#define PROJ_ID 0x01
#define TAG 0X55000055
struct ShareMemory{
uint32_t tag;
char data[SHM_SIZE - sizeof(uint32_t)];
};
int main(int argc, char *argv[])
{
key_t key;
int shmid;
void* shmaddr = NULL;
int ret = 0;
//获得 key
key = ftok("./", PROJ_ID);
if (key == -1)
perror("ftok");
printf("key[0x%x]\n", key);
//创建共享内存
shmid = shmget(key, SHM_SIZE, SHM_MODE);
if (shmid < 0){
perror("shmget");
exit(-1);
}
printf("shmid[%d]\n", shmid);
//映射
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void*)-1){
perror("shmat");
exit(-1);
}
ShareMemory *stShm = (ShareMemory*)shmaddr;
if (stShm->tag == TAG){
printf("%s", (char*)stShm->data);
return 0;
}
printf("write data in share memory\n");
bzero(shmaddr, SHM_SIZE);
stShm->tag = TAG;
strcpy((char*)stShm->data, "hello\n");
printf("enter to destory share memory...\n");
getchar();
//删除映射
ret = shmdt(shmaddr);
if (ret < 0){
perror("shmdt");
exit(1);
}
//删除共享内存
ret = shmctl(shmid, IPC_RMID, NULL);
if (ret < 0){
perror("shmctl");
exit(1);
}
return 0;
}
参考http://blog.youkuaiyun.com/tennysonsky/article/details/46425485