前言
简介
共享内存是可被多个进程读取的内存。用特殊的系统调用分配和释放内存并设置权限;通过一般的读写操作读写内存段中的数据。使用共享内存是进程间进行本地通信的方法之一。最重要的是,它能够较少由于I/O操作带来的开销。
适用范围
它适用于任何类型的协作,尤其适合需要安全性的情况。
来源
共享内存并非从某一个进程中划分出来的,进程的内存总是私有的,而共享内存是从系统的空闲内存池中分配的,希望访问它的每个进程连接它。这个连接过程称为映射,它给共享内存段分配每个进程的地址空间中的本地地址。
API
有两套共享内存 API:POSIX API 和比较老(但是仍然有效)的 System V API。下面做了一个简单的对比。
POSIX API | system V API | |
---|---|---|
获取共享内存ID | int shm_open(const char *name, int oflag, mode_t mode); | int shmget(key_t key, size_t size, int shmflg); |
映射内存 | void mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset); | void shmat(int shmid, const void shmaddr, int shmflg); |
解除映射 | int munmap(void *addr, size_t length); | void shmat(int shmid, const void shmaddr, int shmflg); |
因为 POSIX 是 UNIX 和 Linux® 及其衍生系统上的公认标准,所以下面介绍POSIX API。
POSIX 为创建、映射、同步和取消共享内存段提供五个入口点:
- int shm_open(const char *name, int oflag, mode_t mode);
功能描述:创建或者打开已经存在的共享内存块
返回值:成功返回文件描述符,失败返回-1
name:共享内存区的名称
oflag:标志位,可选择读,写等
mode:权限位 int shm_unlink(const char *name);
功能描述:
根据(shm_open() 返回的)文件描述符,删除共享内存段。实际上,这个内存段直到访问它的所有进程都退出时才会删除,这与在 UNIX 中删除文件很相似。但是,调用 shm_unlink() (通常由原来创建共享内存段的进程调用)之后,其他进程就无法访问这个内存段了。
返回值:失败返回-1
name:共享内存区名称void mmap(void addr, size_t length, int prot, int flags,int fd, off_t offset);
功能描述:把共享内存段映射到进程的内存。这个系统调用需要 shm_open() 返回的文件描述符,它返回指向内存的指针。在某些情况下,还可以把一般文件或另一个设备的文件描述符映射到内存。
返回值:成功返回映射区地址,失败返回-1
addr:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址
length:映射区的长度,以字节为单位。
prot:内存保护标志
flags:标志位
fd:文件描述符
offset:被映射对象内容的起点。void mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset);
功能描述:作用与 mmap() 相反。
返回值:成功返回0,失败返回-1- int msync(void *addr, size_t length, int flags);
功能描述:用来让共享内存段与文件系统同步 — 当把文件映射到内存时,这种技术有用。
返回值:成功返回0,失败返回-1
addr:文件映射到进程空间的地址;
length:映射空间的大小;
flags:刷新的参数设置,可以取值MS_ASYNC/ MS_SYNC/ MS_INVALIDATE
fd:文件描述符
offset:被映射对象内容的起点使用过程
1.创建内存段(shm_open)
2.映射内存段(mmap)
3.使用
4.删除(munmap(),shm_unlink)
程序示例
下面的程序简单说明了共享内存的使用方法。在开辟一片共享内存区域后,子进程写入数据,而父进程在子进程写入数据后,将数据读出,并打印。具体代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78/*================================================================
* Copyright (C) 2018 Ltd. All rights reserved.
*
* 文件名称:mmap.c
* 创 建 者:hyb
* 创建日期:2018年01月07日
* 描 述:
*
================================================================*/
void error_and_die(const char *msg)
{
perror(msg);
exit(-1);
}
int main(int argc,char *argv[])
{
int r=0;
const char *memname = "sample";
const size_t region_size=sysconf(_SC_PAGE_SIZE);
int fd=shm_open(memname,O_CREAT|O_TRUNC|O_RDWR,0666);
if(-1 == fd)
{
error_and_die("shm_open");
}
//修改fd指针指向文件或者内存区的大小
r=ftruncate(fd,region_size);
if(r!=0)
{
error_and_die("ftruncate");
}
//将ptr指针指向共享内存区
void *ptr = mmap(0,region_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(NULL == ptr)
{
error_and_die("mmap");
}
close(fd);
pid_t pid=fork();
if(pid==0)
{
//子进程写入数据
char *d=(char *)ptr;
snprintf(d,region_size,"hello world");
exit(0);
}
else
{
//等待子进程退出后,读取共享内存数据
int status;
waitpid(pid,&status,0);
printf("child wrote %s\n",(char *)ptr);
}
//解除内存映射
r=munmap(ptr,region_size);
if(r!=0)
{
error_and_die("munmap");
}
//删除共享内存
r=shm_unlink(memname);
if(0!=r)
{
error_and_die("shm_unlink");
}
return 0;
}
编译:1
gcc -g mmap.c -lrt -o mmap
运行:1
child wrote hello world
总结
本文简单介绍了共享内存以及简单使用。后面将会有更多的介绍。