共享内存
-
前言
- 主要是翻译链接地址.
- 加上之前看的一些书籍的总结。
-
包含头文件
#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_CREATENOMEM系统无法分配内存,这里是系统资源耗尽。
-
资源限制
-
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,同时设置
-
-
错误值
-
shmatEACCES表示没有权限。EIDRM已经被删除,ID,REMOVEDEINVAL表示地址没对齐的方式映射,给的地址无效,不能绑定,或者是给了SHM_REMAP但是shmaddr为空。ENOMEM系统没有内存为其分配描述符或者页表。
-
shmdtEINVAL表示没有和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.
-


