int shmget(key_t key, size_t size, int shmflg);
通过参数key返回一个System V共享内存段关联标记。当key设置为IPC_PRIVATE或者不存在重复key shmflg 设置成IPC_CREATE的情况下,一个新的共享内存段被创建,分配的长度等于参数size并且和PAGE_SIZE对齐。
如果shmflg指定IPC_CREATE and IPC_EXCL而且一个共享内存段已经存在在key名下,函数将会调用失败,errno变量也会被设置成EEXIST错误。(这个效果类似open函数使用O_CREATE|O_EXCL)
shmflg有以下的组合方式
去创建一个新段。如果这个flag没有用,shmget函数key名下将会找到段关联标记,并且检查调用函数的用户是否有权限去访问这个共享段。
伴随着IPC_CREAT使用,用于确认失败情况下是否为段已经存在。
指定权限授权给所有者、组和领域。这些bits和open函数参数中拥有相同的格式,相同的意义。目前,可执行权限没有在这系统中启用。
(自从Linux 2.6)
允许段使用”巨型页”。阅读Linux内核文件HugeTLB获取更多信息。
(自从Linux 2.6.15)
这个标记用于类似于mmap(2) MAP_NORESERVE 的意图。不要为这个段预存交换空间。当预存了交换空间,修改段将会获得一个保障。当没有预留交换空间,当没有足够的物理内存的时候去写的时候可能会出现SIGSEGV。
当一个新的共享内存段被创建,它内存将会清理成0,而且他的关联数据结构 shmid_ds将会初始化成下面的样子:
shmid_ds定义:
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
struct ipc_perm {
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
变量 | 设置 |
---|---|
shm_perm.cuid and shm_perm.uid | 设置成调用者用户的ID |
shm_perm.cgid and shm_perm.gid | 设置成调用者的groupID |
shm_perm.mode | 低9位将会被设置成shmflg参数值 |
shm_segsz | 设置成struct的size |
shm_lpid, shm_nattch, shm_atime and shm_dtime | 设置成0 |
shm_ctime | 设置成时间戳 |
如果共享内存段已经存在,权限被验证,在删除的时候需要做检查。
如果成功将会返货共享内存的标示。发生错误的时候将会返回-1。
void *shmat(int shmid, const void *shmaddr, int shmflg);
通过标记符挂载上一块共享内存到一块process的内存地址中。这个挂载地址是需要遵循下面的规则:
如果shmaddr为空,系统将会寻找一块合适的(没有使用)地址在被挂载段中。
如果shmaddr补位空而且SHM_RND被指定在shmfg,挂载发生的地址等于shmaddr地址按照SHMLBA对齐的位置。否则shmaddr必须是在一个页对齐的位置。
如果在shmfg中指定SHM_RDONLY,段在进程中只能读。否则这段是可以读写。没有只写不读的概念。
SHM_REMAP标记可能指定在shmflg中,去表示—映射的段可能替换任何存在的映射在shmaddr开始size大小的内存区域。在这个情况下shmaddr必须有值。
当挂载成功之后对于shmid_ds数据结构的修改如下:
变量 | 设置 |
---|---|
shm_atime | 设置成当前时间 |
shm_lpid | 设置成本进程的编号 |
shm_nattch | 将会+1 |
如果返回值为被释放掉的内存段地址;当错误发生的时候将会返回(void*)-1。
int shmdt(const void *shmaddr);
释放掉一块共享内存的挂载;与shmat操作相反;
当调用成功之后将会触对shmid_ds的修改
变量 | 设置 |
---|---|
shm_dtime | 设置成当前时间 |
shm_lpid | 设置成本进程的编号 |
shm_nattch | 将会-1 |
如果返回值为0;当错误发生的时候将会返回(void*)-1。
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
在shmid中执行一次cmd控制操作,返回值将会写入buf中,下面是命令描述:
变量 | 设置 |
---|---|
IPC_STAT | 复制当前共享内存的shmid_ds信息 |
IPC_RMID | 标记共享内存需要被删除,当shm_nattch计数为0时将会回收,调用者必须是创建者,或者是privileged |
IPC_INFO | 返回系统级关于共享内存相关的参数配置,需要强转换类型为shminfo |
SHM_INFO | 返回shm_info信息 |
SHM_STAT | 返回一个shmid_ds。shmid不是标识符,而是内核内部数组的下边 |
SHM_LOCK | 防止共享内存段交换。锁定之后的共享内存块如果有调用者去操作页的时候将会触发失败 |
SHM_UNLOCK | 解锁内存段,允许他被交换出去。 |
struct shminfo {
unsigned long shmmax; /* Maximum segment size */
unsigned long shmmin; /* Minimum segment size; always 1 */
unsigned long shmmni; /* Maximum number of segments */
unsigned long shmseg; /* Maximum number of segments that a process can attach; unused within kernel */
unsigned long shmall; /* Maximum number of pages of shared memory, system-wide */
};
IPC_INFO or SHM_INFO操作返回在内核记录全部共享内存队列记录信息中最高使用条目的下标(这个下标能用在SHM_STAT flag下获得系统中全部共享内存段);
SHM_STAT 将返回共享内存的标识符
其他操作将返回 0 作为成功;