- 专栏内容:linux下并发编程
- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
目录
前言
概述
原理机制
系统命令
接口说明
代码演示
结尾
前言
本专栏主要分享linux下并发编程相关知识,包括多进程,多线程,进程/线程间通信,并发同步控制,以及高并发下性能提升,请大家多多留言。
概述
共享内存是进程间通信的一种常用手段,相较于其它通信方法,它可以在进程间传递大量的不同格式的数据,同时这些数据不需要持久保存在磁盘上。
原理机制
共享内存相当于操作系统在进程的地址空间外,再开辟了一段物理内存,再把这段物理内存映射到进程的虚拟地址空间内,这样进程就可以访问了。
那么不同进程如何知道这段物理内存呢?
所以,第一步是确定一个唯一的key值,把这个key在不同进程间传递,不同进程拿到后就可以用这个key值向操作系统查到共享内存空间。
第二步,就是映射到进程的内存空间。我们都知道,进程的内存空间是一个虚拟地址空间,通过段页机制将物理内存地址转成虚拟内存地址,所以此处的共享内存,也需要把它挂到进程的虚拟地址空间中,进程才可以访问,不然还是找不到。
第三步,此时就可以直接读写了。
第四步,使用完成后,资源需要释放,否则会造成内存泄漏,这可是编程的大忌。因为涉及到多个进程共同使用,当然我们释放有两种形式,一是当前进程不再引用使用,此时共享内存还存在;一种是其它进程不再使用时,删除共享内存,此时共享内存就彻底不存在了。
系统命令
查询共享内存系统资源,使用系统命令
ipcs -m
查询结果如下:
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x0101de21 229412 test 600 8192 1
手动使用系统命令删除共享内存,使用共享内存的id
ipcrm -m shmid
接口说明
接口对应的头文件为
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
(1)创建和获取已有的共享内存,每个共享内存都有唯一的key来标识,key值可以用IPC_PRIVATE,也可以用ftok函数生成。
int shmget(key_t key, size_t size,int shmflg);
size ,共享内存的大小,分配时会与PAGE_SIZE向上取整;
shmflg,可以取值
IPC_CREAT | 创建共享内存,如果不带时,只是查找key值标识的共享内存 |
IPC_EXCL | 与IPC_CREATE一起使用,如果已经有key标识的共享内存时,就会失败 |
SHM_HUGETLB | 创建大页内存,一般页面大小为2KB/4KB,这里可以使更大的页面,在密集型读时可以得到性能提升; |
SHM_HUGE_2MB | 同上,创建2MB的大页内存 |
SHM_NORESERVE | 不为此共享内存使用swap空间 |
(2)映射到进程私有虚拟地址空间
void * shmat(int shmid, char __user * shmaddr, int shmflg);
将shmid标识的共享内存,映射到shmaddr对应的进程内存地址上,映射后的地址是与SHMLBA向下对齐的地址。如果地址为空,则由系统选择一块进程空闲空间进行映射。
关于SHMLBA,这是关于CPU cache调度相关,有些话长,在另外一篇博文中专门解释。
shmflg,标识共享内存的使用权限,可以取值
SHM_EXEC | 共享内存可以有执行权限 |
SHM_RDONLY | 以只读权限使用共享内存 |
SHM_REMAP | 如果当前映射地址已经有映射有其它共享内存,可以进行替换 |
(3)解除与当前进程的映射
int shmdt(const void *shmaddr);
当然这里只是与当前进程解除了映射,内核资源也仅仅对引用计数减一,并没有删除;
(4)删除共享内存资源
int shmctl(int shmid, int cmd, struct shmid_ds * buf);
此系统函数,可以对共享内存进行一些控制操作,其中包括删除共享资源,由cmd来指定对应的操作;
cmd,可以取下面的值
IPC_STAT | 调用者需要有读权限,会将kernal中共享内存信息拷到buf中返回 |
IPC_SET | 可以设置shmid_ds结构成员的信息 |
IPC_RMID | 销毁共享内存,当然必须是引用计数为0,调用者一定是创建者或最后一个使用进程 |
IPC_INFO | 获取共享内存的系统限制相关参数值 |
SHM_INFO | 获取共享内存资源使用情况 |
SHM_STAT | 同IPC_STAT |
SHM_LOCK | 锁定共享内存,此时内存页面不会被替换 |
SHM_UNLOCK | 解除锁定 |
(5)ftok系统函数对应的头文件与声明如下:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
代码演示
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define SHM_SIZE 8192
int main(int argc ,char *argv[])
{
key_t shm_key = ftok(".", 1);
int shm_id = -1;
char *pshm = NULL;
char str[] = "Hello, World!";
int pid = 0;
int underSubprocess = 0;
shm_id = shmget(shm_key, SHM_SIZE, 0600|IPC_CREAT);
if(shm_id < 0)
{
printf("get shm error [%s]\n",strerror(errno));
return -1;
}
pshm = (char *)shmat(shm_id, NULL, 0);
if(pshm == (char *)-1)
{
printf("attach sharememory error [%s]\n", strerror(errno));
return -1;
}
pid = fork();
if(pid == 0)
{
// under subprocess
printf("I'm in the subprocess\n");
sleep(2);
printf("recv: %s\n",pshm);
underSubprocess = 1;
}
else if(pid > 0)
{
strncpy(pshm, str, sizeof(str));
sleep(5);
printf("I'm father ,will exit.\n");
}
else
{
printf("fork error\n");
}
if(-1 == shmdt(pshm))
{
printf("detach sharememory error [%s]\n",strerror(errno));
return -1;
}
if(underSubprocess == 0)
shmctl(shm_id, IPC_RMID, NULL);
return 0;
}
在运行过程中查看系统共享内存
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x0101de21 229412 test 600 8192 1
确实已经创建了。
结尾
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。另外有什么想要了解的内容,也可以给我发邮件,互相谈讨,定知无不言。
注:未经同意,不得转载!