1.概念
共享内存区,按标准可分为Posix共享内存区和System V共享内存区,两者在概念上类似。
Posix 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称,其正式称呼为IEEE 1003,而国际标准名称为ISO/IEC 9945。
System V,曾经也被称为 AT&T System V,是Unix操作系统众多版本中的一支。它最初由 AT&T 开发,在1983年第一次发布。一共发行了4个 System V 的主要版本:版本1、2、3 和 4。
2.Posix.1提供了两种在无亲缘关系进程间共享内存区的方法
a. 内存映射文件(memory-mapped file),由open函数打开,由mmap函数把所得到的描述符映射到当前进程空间地址中的一个文件。
b.共享内存区对象(shared-memory object),由shm_open函数打开一个Posix.1 IPC名字,所返回的描述符由mmap函数映射到当前进程的地址空间。
这两种共享内存区的区别在于共享的数据的载体(底层支撑对象)不一样:
内存映射文件的数据载体是物理文件。
共享内存区对象,也就是共享的数据载体是物理内存。
3.我们经常说的共享内存,一般是指共享内存区对象,也就是共享物理内存。
4.关键函数说明
/*指定一个名字参数,以创建一个新的共享内存区对象或者打开一个已存在的共享内存区对象*/
int shm_open(const char *name, int oflag, mode_t mode);
oflag参数与open函数的flags一样,可参考man手册
mode参数与open函数的mode一样,是指定权限位,可参考man手册
/*删除一个共享内存区对象的名字,删除一个名字不会影响对于底层支撑对象的现有引用,直到对于该对象的引用全部关闭*/
int shm_unlink(const char *name);
/*将共享内存区对象的大小设置成length字节*/
int ftruncate(int fd, off_t length);
/*获取共享内存区对象的信息,比如大小等*/
int fstat(int fd, struct stat *buf);
/*mmap函数把一个Posix共享内存区对象映射至调用进程的地址空间*/
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr可以指定描述符fd应被映射到进程内空间的起始地址,设为NULL表示让内核自己去选择,
length是指定映射区大小,通常设为共享内存大小
offset也就是共享内存的offset,也就是映射从共享内存哪个位置开始,通常设置为0,
port用于设置内存映射区的保护:
PORT_READ -> 可读
PORT_WRITE -> 可写
PORT_EXEC -> 可执行
PORT_NONE -> 数据不可访问
flags用于设置内存映射区的数据被修改时,是否改变其底层支撑对象(这里的对象是内存),MAP_SHARED和MAP_PRIVATE必须指定一个
MAP_SHARED -> 变动是共享的
MAP_PRIVATE -> 变动是私自的
MAP_FIXED -> 准确的解析addr参数
当flags设定为MAP_SHARED时,修改被映射的数据会改变其底层支撑对象
当flags设定为MAP_PRIVATE时,修改被映射的数据不会改变其底层支撑对象
5.特别提醒
共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,比如信号量
6.简单的测试代码
----- gcc shm_open.c -lrt -----
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#define MAXSIZE 1024*1024*16 /*文件大小,建议设置成内存页的整数倍*/
#define NAME "shmtest" /*文件名,创建成功会出现在/dev/shm目录中*/
int main(int argc, char const *argv[])
{
printf("start shm_open\n");
/*打开或者创建一个共享内存区对象,O_CREAT会在/dev/shm下生成一个文件*/
int fd = shm_open(NAME, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
if (fd == -1) {
perror("shm_open fail:");
return -1;
}
ftruncate(fd, MAXSIZE); /*设置共享内存对象大小,此刻通过free -m命令查看内存并未被占用*/
printf("start mmap\n");
char* ptr = (char*)mmap(NULL, MAXSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
/*内存的加载是按照页大小来加载*/
*ptr = 'A'; /*只加载一个内存页*/
memset(ptr, 'A', MAXSIZE); /*通过free -m查看,内存被加载至cached,且/dev/shm下的文件会被赋值(前提是MAP_SHARED)*/
printf("start shm_unlink\n");
/*如果不执行shm_unlink,进程退出时,/dev/shm下的文件依然存在,通过free -m看到内存依然占用在系统级cached*/
/*执行shm_unlink且没有进程引用这个共享内存后,/dev/shm下的文件会被删除,通过free -m看到会释放cached*/
shm_unlink(NAME);
return 0;
}
参考:《unix网络编程》卷2 13章
原文出自:http://blog.csdn.net/daiyudong2020/article/details/50500651