目录
0.引用
1.共享内存的实质
共享内存的实质就是将内核的一块内存映射到进程中的内存,操作本地内存就相当于操作共享内存。
2.使用共享内存的步骤
1.创建共享内存;
2.关联共享内存;
3.使用共享内存;
4.断开与共享内存的关联;
5.删除共享内存.
3.创建共享内存
SYNOPSIS(概要)
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
参数解释:
(1)key 申请的共享内存的key值-可以理解成每个人都有身份证号一样,每一个共享内存都有一个唯一的key值.
(2)size 申请的内存的大小,这里实际上分配内存的时候也是按页来分配的
(3)shmflg 共享内存的属性,与创建文件相同.
The value shmflg is composed of:
IPC_CREAT:创建共享内存.创建文件并制定权限IPC_CREAT|0664
IPC_EXCL:只能和IPC_CREAT一起使用,检测共享内存是否存在,若共享内存存在,则报错,errno=EEXIST
RETURN VALUE(返回值)
On success, a valid shared memory identifier is returned. On errir, -1 is returned, and errno is set to indicate the error.
成功则返回创建的共享内存的描述符,理解为共享内存的ID,这个ID是唯一的.
使用举例:
// 1.创建一块不存在的共享内存
// 如果检测到key值为0x12的共享内存已经存在,该函数调用失败
shmget(0x12,4096,IPC_CREAT|IPC_EXCL|0664);
// 2.打开一块已经存在的共享内存,共享内存的key为0x12
shmget(0x12,0,0);
// 3.操作一块内存,存在打开,不存在则创建
shmget(0x12,4096,IPC_CREAT|0664);
4.连接共享内存
SYNOPSIS
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数解释:
shmid:shmget函数返回的共享内存的ID值;
shmaddr:一般传NULL,表示让内核自动分配地址,也可以自己指定地址;
shmflg:
SHM_RDONLY:只能对共享内存进行读操作;
0:对共享内存可读可写.
返回值:成功则返回与共享内存关联上的在用户空间的那个内存地址.
失败返回 (void*)-1.
注意:
A successful shmat() call updates the members of the shmid_ds structure (see shmctl(2)) associated with the shared memory segment as follows:
shm_atime is set to the current time.
shm_lpid is set to the process-ID of the calling process.
shm_nattch is incremented by one.
5.断开与共享内存的关联
SYNOPSIS
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:断开与共享内存的关联.
参数:shmaddr:shmat返回的内存地址.
返回值:
成功:返回0;
失败:返回-1,并设置errno.
注意:这个函数如果执行成功只是对内核中这块内存的引用计数减一.
6.删除共享内存
SYNOPSIS
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
删除共享内存用的函数是shmctl,这个函数功能很多,在删除时,我们设置好相应的参数即可标记我们想要删除共享内存.(其实这里我们只是
告诉系统我们想要删除了,能不能成功删除还是要取决于共享内存的关联计数是否为0了,等到共享内存的关联技术变为0且我们也告诉过系统
我们想删除了,那么这个共享内存才会真正被删除)
首先对这个函数做一些说明:
函数描述:对共享内存进行一些操作.
函数参数:
shmid:shmget函数返回的共享内存的ID值;
cmd:
IPC_STAT:获得贡献内存的状态信息(此时第三个参数buf的值不能为空)
IPC_SET:设置共享内存信息.
IPC_RMID:删除共享内存(此时第三个参数buf的值可以为空)
再对这个函数用作删除共享内存的功能做出说明:
当cmd参数给出的是IPC_RMID的时候,函数实现的功能就是标记要删除共享内存这个动作.
给出一些注意事项说明,
mannul中在使用IPC_RMID的时候指出:
Mark the segment to be destroyed. The segment will only actually be destroyed after the last process detaches it (i.e., when the shm_nattch member of the associated structure shmid_ds is zero). The caller must be the owner or creator, or be privileged. If a segment has been marked for destruction, then the (nonstandard) SHM_DEST flag of the shm_perm.mode field in the associated data structure retrieved by IPC_STAT will be set.
The caller must ensure that a segment is eventually destroyed; otherwise its pages that were faulted in will remain in memory or swap.
See also the description of /proc/sys/kernel/shm_rmid_forced in proc(5).
翻译:
当使用IPC_RMID的时候,标记出将要删除共享内存.需要注意的是这块共享内存只有在最后一个与之关联的进程与之解绑之后才能真正的实现删除
(举个例子:也就是说结构体shmid_ds结构体中的shm_nattach的数字变成0).调用者必须是所有者护着拥有更高的权限.如果一个一块共享
内存被标记着要被删除,那么这个IPC_STAT相关的数据结构中的shm_perm.mode这个域的标志SHM_DEST将被标记.调用者必须要确保这块内存
真正的被释放了,否则这些申请的出错的页将会留在内存或者交换区中.(这个将非常浪费资源,而且是内核中的宝贵资源,将其删除只能通过命令或者重启系统才能删除了,否则就一直浪费着资源).
7.ipcs/ipcrm/ipcmk
ipcs命令用于报告Linux中进程间通信设施的状态,显示的信息包括消息列表、共享内存和信号量的信息。
其中ipc当然指的是进程间通讯的意思,s指的是show的意思.
用法:
ipcs [resource ...] [output-format]
ipcs [resource] -i <id>
选项:
-i, --id <id> 打印由 id 标识的资源的详细信息(用这个选项必须要先指定资源选项中的m,q,s)
-h, --help 显示此帮助并退出
-V, --version 输出版本信息并退出
资源选项:
-m, --shmems 共享内存段
-q, --queues 消息队列
-s, --semaphores 信号量
-a, --all 全部(默认)
输出格式:
-t, --time 显示附加、脱离和更改时间
-p, --pid 显示 PID 的创建者和最后操作
-c, --creator 显示创建者和拥有者
-l, --limits 显示资源限制
-u, --summary 显示状态摘要
--human 以易读格式显示大小
-b, --bytes 以字节数显示大小
使用示例:
ipcs -l 查看消息限制,同享内存限制,信号量限制(等价于ipcs -l -a)
ipcs -l -q 查看消息限制
ipcs -l -m 查看同享内存限制
ipcs -l -s 查看信号量限制
ipcs -q 1 查看id标识为1的消息队列的情况
ipcrm命令删除一个或更多的消息队列、信号量集或者共享内存标识。(只有当引用计数为0的时候才能真正将其删除)
用途
删除消息队列、信号集、或者共享内存标识。
语法
ipcrm [ -m SharedMemoryID ] [ -M SharedMemoryKey ] [ -q MessageID ] [ -Q MessageKey ] [ -s SemaphoreID ] [ -S SemaphoreKey ]
描述
ipcrm 命令删除一个或更多的消息队列、信号量集或者共享内存标识。
标志
-m SharedMemory ID 删除共享内存标识 SharedMemoryID。与 SharedMemoryID 有关联的共享内存段以及数据结构都会在最后一次拆离操作后删除。
-M SharedMemoryKey 删除用关键字 SharedMemoryKey 创建的共享内存标识。与其相关的共享内存段和数据结构段都将在最后一次拆离操作后删除。
-q MessageID 删除消息队列标识 MessageID 和与其相关的消息队列和数据结构。
-Q MessageKey 删除由关键字 MessageKey 创建的消息队列标识和与其相关的消息队列和数据结构。
-s SemaphoreID 删除信号量标识 SemaphoreID 和与其相关的信号量集及数据结构。
-S SemaphoreKey 删除由关键字 SemaphoreKey 创建的信号标识和与其相关的信号量集和数据结构。
msgctl、shmctl 和 semctl 子例程提供了删除操作的细节。标识和关键字可以用 ipcs 命令找到。
示例
如果要删除和 SharedMemoryID 18602 相关的共享内存段,请输入:
ipcrm -m 18602
ipcmk
命令:
ipcmk - create various ipc resources(创建不同的ipc资源)
语法:
ipcmk <resource options> [additional options]
描述:
ipcmk allows you to create shared memory segments, message queues or semaphore arrays.
资源选项:
Resources may be specified as follows:
-M, --shmem [size] Shared memory segment of size bytes.
-S, --semaphore [number] Semaphore array with number of elements.
-Q, --queue Message queue.
附件选项:
-p, --mode [mode] Permission for the resource. Default is 0644.
-h, --help Display a short help message and exit.
-V, --version Output version information and exit.
8.查看内核中的共享内存的最大值
在配置文件中/proc/sys/kernel/shmmax给出,命令ipcs -m -l也可以查出.
我的Linux localhost.localdomain 3.10.0-957.el7.x86_64 #1 SMP Thu Nov 8 23:39:32 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux机器中的是:18446744073692774399KB
我的Linux localhost.localdomain 2.6.32-754.el6.i686 #1 SMP Tue Jun 19 21:51:20 UTC 2018 i686 i686 i386 GNU/Linux机器中的是1073741824KB
每台机器大概都不一样吧,也许也可以设置成一样,但要看具体的机器配置了.
9.其他的一些问题
问:是不是可以对共享内存进行多次删除?
答:可以,共享内存被删除一次后,如果还有进程和共享内存关联着,共享内存的key值会变成0;
如果共享内存中的key值是大于0的数,共享内存状态正常,任何进程都可以和当前共享内存进行关联;
如果共享内存中的key值等于0,共享内存标记为被删除但是没有被马上删除的原因是还有进程没有和它解除关联,不相干的进程是没有权利和key为0的共享内存进行关联的.
10.shm与mmap之间的相同点与不同点
相同点:
1.mmp和shm使用的都是内存地址;
2.mmp和shm都可以用于有无血缘的进程间通信;
3.mmap和shm读取数据之后并不会消失.
不同点:
1.map如果用于没有血缘关系的进程间通信必须使用文件,shm不需要;
2.shm的速度快于mmap(实际上shm是所有进程间通信方式中最快的一种);
3.mmap比shm安全(一断电shm就GG了,mmap还可能有磁盘文件).
总结:
1.shm不需要磁盘文件,mmap需要磁盘文件;
2.shm效率高;
3.mmap可操作的数据量比shm要大;
4.shm内存位置在内核中只有一块,mmap内存在用户去,每个进程都有各自的内存映射区;
5.mmap的数据更安全,mmap会通过映射的文件做备份;
6.进程退出,共享内存依然存在(大多数情况下,这需要一定的条件);进程退出,映射区就不存在了.