共享内存
-
前言
- 主要是翻译链接地址.
- 加上之前看的一些书籍的总结。
-
包含头文件
#include <sys/shm.h>
-
int shmget(key_t key, size_t size, int shmflg);
-
描述
- 返回的是系统虚拟共享内存片段的
shmid
和传入key
值存在映射。可以通过指令查看ipcs -m
- 返回值还和一个数据结构存在映射关系,还和一篇共享内存存在映射关系。
- 如果
key
值等于IPC_PRIVATE
则会创建一个结构体,shmid
,以及对应大小的内存。 - 如果
key
不存在任何映射,但是声明了shmflag&IPC_CREAT
,也会创建。
- 返回的是系统虚拟共享内存片段的
-
创建的内容
- 创建会创建一个结构体。
shm_perm.cuid,shm_perm.uid
被设置为调用进程的用户ID
.shm_perm.cgid|shm_perm.gid
被设置为调用进程的组ID
.shmflg
由shm_perm.mode
保存,对低九位做与运算。size
由shm_perm.segsz
保存。shm_lpid,shm_naatch,shm_atime,shm_dtime
都被设为0.shm_ctime
被设置位当前时间。-
创建后可以通过函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
获取对应的信息。 -
共享内存创建之后被初始化为0.
-
key
的值key
表示获取已经创建的,但是shmflg
必须为0
而且key & IPC_PRIVATE
必须为0.或者就是创建新的。IPC_PRIVATE
只能是其子进程可以。通常和shmflg=0,key
会创建一个私有的,但是不能已经存在。
-
shmflg
的值IPC_CREAT
表示创建一个新的。如果没有则会找已经创建的与key
相对应的内存,同时还会检查当前的调用者是否由权限使用这一块内存。IPC_EXCL
和IPC_CREAT
一起使用表示一定要新创建。如果已经存在则通过errnor
返回错误。SHM_HUGETLB
使用大的页分区来分配共享内存。2.6
以后。SHM_HUGE_2MB|SHM_HUGE_1GB
表示大表。自3.8
以后。SHM_NORESERVE
和mmap
函数的MAP_NORESERVE
同样的目的。表示这块不保留交互区。没有交换区就可能造成SIGSEGV
的错误。
-
错误码代表的含义,在
errno.h
中定义EACCES
没有权限。EEXIST
通过IPC_CREAT|IPC_EXECL
的方式创建,同时声明了这两个flag
,而且与key
相关的共享内存已经存在。EINVAL
设置的size
超过上限值SHMMAX|/proc/sys/kernel/shmmax
,可以查看这两个值,修改这两个值。修改通过指令echo value > /proc/sys/kernel/shmmax
的方式修改。也可能是小于最小值SHMMIN
。或者是这个key
对应的内存已经存在,但是size
大于key
的size
.可以结合指令ipcs -m
查看。ENFILE
系统设置的总的共享内存被用完。上面是单个的超过了限制,这个是总数上超过了限制。ENOENT
没有与key
对应的内存存在。肯定没有传入IPC_CREAT
ENOMEM
系统无法分配内存,这里是系统资源耗尽。
-
资源限制
-
SHMALL
限制了共享内存的总数,在/proc/sys/kernel/shmall
由定义。 -
SHMMAX
限制了共享内存的单次创建最大。在/proc/sys/kernel/shmmax
-
SHMMIN
最小目前是1byte
-
SHMMNI
共享内存可以有几个,在/proc/sys/kernle/shmmni
中定义。
-
-
-
void *shmat(int shmid, const void *shmaddr, int shmflg);
-
功能
- 将与
shmid
绑定的共享内存映射到调用者的shmaddr
处。
- 将与
-
shmaddr
- 如果为
NULL
,系统自动选择一个合适的地方进行映射。 - 如果不为空且
shmflg&SHM_RND
不为0.那么就按照页面对其的方式绑定到shmaddr
,size=(size+SHMLBA-1)/SHMLBA*SHMLBA
. - 其他的则是,
shmaddr
这个地址必须是按照页对齐的地方。
- 如果为
-
shmflg
SHM_EXEC
表示这块内存可以用于执行,但是调用者需要由执行权限。SHM_RDONLY
绑定的这块内存只用于读取,否则就是读写。SHM_REMAP
占用进程一段地址。如果这段地址中有已经被占用的,则返回EINVAL
,这种情况一般是使用shmaddr=NULL
.
-
返回值
- 成功绑定后的地址起始位置。
- 失败返回-1,同时哈辉设置
errno
为对应的错误代码。
-
-
int shmdt(const void *shmaddr);
-
功能
- 解除调用者与
shmaddr
共享内存的映射关系。
- 解除调用者与
-
shmaddr
- 这个只应该是已经和调用者存在映射关系,而且这个值必须是由
shmat
调用的返回值。
- 这个只应该是已经和调用者存在映射关系,而且这个值必须是由
-
成功后
- 修改对应的共享结构体的值,比如
shm_dtime -> detach time
,shm_lpid->last process id
,shm_nattch->count of attach
。
- 修改对应的共享结构体的值,比如
-
返回值
- 成功返回0,失败返回-1,同时设置
errno
表示对应的错误。
- 成功返回0,失败返回-1,同时设置
-
-
错误值
-
shmat
EACCES
表示没有权限。EIDRM
已经被删除,ID,REMOVED
EINVAL
表示地址没对齐的方式映射,给的地址无效,不能绑定,或者是给了SHM_REMAP
但是shmaddr
为空。ENOMEM
系统没有内存为其分配描述符或者页表。
-
shmdt
EINVAL
表示没有和shmaddr
绑定的。或者地址不是起始地址,而是中间地址。
-
-
子进程
-
fork
- 子进程会继承父进程的共享继承,都会绑定,任何权限的都会共享。
-
execve
- 执行新的程序的时候就会取消绑定。
-
_exit
- 退出的时候会取消绑定。
-
-
建议
-
shamt
- 建议
shmaddr
为NULL
,因为不同的进程挂载的地方可能不同,而且有可能还不合法。随机的更加合理一些。 - 甚至可以绑定一个被标记为即将删除的共享内存。
SHMLBA|segment low boundary address
.这个表示调用者需要保证这个地址是这个的倍数。可以看成对齐。这个保证处理器缓存的执行其他的绑定。
- 建议
-
-
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
-
功能
- 完成
cmd
参数声明的指令。对应的虚拟内存由shmid
表示。 buf
则是一个结构体指针。这个结构体在sys/shm.h
中定义。
- 完成
-
shmid_ds
成员shm_perm:permission
指向一个ipc_perm
结构体,记录有共享内存的操作权限。shm_segsz:segment size
表示当前这个共享内存的大小。shm_cpid:create process" id
表示创建这个共享内存的进程号。shm_lpid:last process id
表示最后一个进程调用shmat,shmdt
的进程。shm_nattch:number of attch
现在有多少的进程在使用这块内存。shm_atime:attach time
最后一个调用shmat
函数的时间。shm_dtime:detach time
最后一个调用shmdt
函数的时间。shm_ctime:control time
最后一个调用shmctl IPC_SET
的时间。
-
cmd
操作类型IPC_STAT
从内核中拷贝数据与shmid
对应的共享内存的数据到buf
中。但是调用者必须要有可读权限。IPC_SET
将buf
的一些数据写入到内核的数据结构中。当然还有变量shm_ctime:control time
.可以修改值shm_perm.uid,shm_perm,gid,shm_perm.mode
等。IPC_RMID
标记删除,实际不会删除,会等待没有绑定为止,而且检查权限。IPC_INFO
返回共享内存的限制信息,通过buf
返回,而且类型也会变成shminfo
.所有解析的时候需要强转一下。如果值太大可能会不同,但是输出16
进制可以查看。不同的Linux
其中结构体shminfo
也可能不同。SHM_STAT
,和IPC_STAT
一样,返回结构体shmid_ds
.然而shmid
不再是一个绑定的key
,而是内核中数组的的下标。
-
返回值
IPC_INFO|SHM_INFO
返回当前内核共享内存最大下标。这个参数可以用于SHM_STAT|SHM_STAT_ANY
来获取共享内存的所有的信息,即shmid_ds
。- 如果
SHM_STAT
则成功返回对应的shmid
。其他的成功返回0.失败则返回-1.并设置对应的errno
.
-