linux shared,从 0 开始学习 Linux 系列之「22.共享内存 Shared Memory」
共享内存
版权声明:本文为 cdeveloper 原创文章,可以随意转载,但必须在明确位置注明出处!
共享内存 Shared Memory
这次我们来学习在 Linux 中最快的一种 IPC 方式:共享内存 Shared Memory,它的基本原理是:内核开辟一片内存区域,然后多个用户进程可以将这片区域映射到它们自己的地址空间中进行读写。为什么这种方式最快?因为数据不需要在进程之间复制,只要一个进程写入数据,另一个进程就能马上读取数据了,但是读取和写入必须同步。
共享内存属性
我们可以在终端中使用 ipcs -m 查看系统当前开辟的共享内存:
ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 104529920 orange 600 2097152 2 dest
0x00000000 688129 orange 600 524288 2 dest
0x00000000 113672194 orange 600 524288 2 dest
...
可以看到列出了共享内存的一些属性:地址,id,拥有者等等。
共享内存在哪里?
进程可以将共享内存映射到自己的地址空间中,如下:
进程地址空间
可以看到中间有一个内存映射区域,进程把内核的共享内存映射到这个地方,模型如下:
共享内存
进程可以通过共享内存的 API 获得内核所管理的共享内存的一份映射,例如直接同步读写自己的映射文件就可以实现进程 A 和 B 之间的通信了,非常简单。
共享内存的操作
共享内存使用起来比较简单,但是需要注意必须保持不同进程的读写同步,可以使用信号量或者互斥锁等,但是本篇文章主要介绍共享内存,不会涉及同步相关的操作,下面的例子主要介绍共享内存相关的 API 操作,当你熟悉了基本的用法,在以后学习同步时可以加上同步机制来保证读写正确。
共享内存(SHM)的操作主要分为下面 4 个步骤:
创建或获取 SHM
映射 SHM 到进程地址空间
操作映射后的区域,即读写
关闭进程地址空间的映射区域
分别来了解这些操作的 API。
1. 创建:shmget
创建 SHM 调用 shmget:
#include
#include
/*
* key:SHM 标识
* size:SHM 大小
* shmflg:创建或得到的属性,例如 IPC_CREAT
* return:成功返回 shmid,失败返回 -1,并设置 erron
*/
int shmget(key_t key, size_t size, int shmflg);
2. 映射:shmat
shmat 将由 shmget 返回的 shmid 标识的 SHM 映射到进程的地址空间:
#include
#include
/*
* shmid:SHM ID
* shmaddr:SHM 内存地址
* shmflg:SHM 权限
* return:成功返回 SHM 的地址,失败返回 (void *) -1,并设置 erron
*/
void *shmat(int shmid, const void *shmaddr, int shmflg);
其中 shmaddr 参数主要有 2 种情况:
shmaddr = NULL:系统选择一块合适的内存地址作为映射的起始地址
shmaddr != NULL:用户自己地址,但是该地址需要符合一定的条件,详情参考 man shmat
3. 关闭映射:shmdt
shmdt 解除当前进程映射的 SHM:
#include
#include
/*
* shmaddr:已经映射的 SHM 地址
* return:成功返回 0,失败返回 -1,并设置 erron
*/
int shmdt(const void *shmaddr);
例子:write_shm.c,read_shm.c
我们先创建一个片 SHM,然后写入内容:
#include
#include
#include
#include
#include
#include
int main() {
// 1. 创建 SHM
int shm_id = shmget(13, 2048, IPC_CREAT | 0666);
if (shm_id != -1) {
// 2. 映射 SHM
void* shm = shmat(shm_id, NULL, 0);
if (shm != (void*)-1) {
// 3. 写 SHM
char str[] = "I'm share memory";
memcpy(shm, str, strlen(str) + 1);
// 4. 关闭 SHM
shmdt(shm);
} else {
perror("shmat:");
}
} else {
perror("shmget:");
}
return 0;
}
我们再写一个程序来映射 SHM 并读取其中的内容:
#include
#include
#include
#include
#include
#include
int main() {
// 1. 获取 SHM
int shm_id = shmget(13, 2048, IPC_CREAT | 0666);
if (shm_id != -1) {
// 2. 映射 SHM
void* shm = shmat(shm_id, NULL, 0);
if (shm != (void*)-1) {
// 3. 读取 SHM
char str[50] = { 0 };
memcpy(str, shm, strlen("I'm share memory"));
printf("shm = %s\n", (char *)shm);
// 4. 关闭 SHM
shmdt(shm);
} else {
perror("shmat:");
}
} else {
perror("shmget:");
}
if (0 == shmctl(shm_id, IPC_RMID))
printf("delete shm success.\n");
return 0;
}
编译:
gcc write_shm.c -o write_shm
gcc read_shm.c -o read_shm
先运行写入 SHM:
./write_shm
再运行读取 SHM:
./read_shm
I'm share memory
成功读取了写进程的写入的数据,虽然不是同步的,但是至少能够获取数据。最后再来分析分析内核中的 SHM 调用过程吧。
共享内存的内核机制
由于 SHM 的操作函数比较多,我这里只分析 shmget 函数,其他的函数都是类似的,先从 glibc 的库函数开始:linux/shmget.c
int shmget (key, size, shmflg)
key_t key;
size_t size;
int shmflg;
{
return INLINE_SYSCALL (ipc, 5, IPCOP_shmget, key, size, shmflg, NULL);
}
这个函数直接进行了系统调用,来继续分析 Linux 3.4 内核大体的执行过程:陷入内核后调用由 SYSCALL_DEFINE3 定义的系统调用 shmget,之后对 shm_ops 中的回调函数赋值,然后调用 ipcget,在这个函数中先进行预处理,然后调用 shm_ops 中设置的 newseg 回调函数,这个回调函数在内核中开辟一段 SHM 内存空间,至此就完成共享内存的创建。
其中 shm_ops 结构非常重要:
// ipc/shm.c
/*
* Structure that holds some ipc operations. This structure is used to unify
* the calls to sys_msgget(), sys_semget(), sys_shmget()
* . routine to call to create a new ipc object. Can be one of newque,
* newary, newseg
* . routine to call to check permissions for a new ipc object.
* Can be one of security_msg_associate, security_sem_associate,
* security_shm_associate
* . routine to call for an extra check if needed
*/
struct ipc_ops {
int (*getnew) (struct ipc_namespace *, struct ipc_params *);
int (*associate) (struct kern_ipc_perm *, int);
int (*more_checks) (struct kern_ipc_perm *, struct ipc_params *);
};
从这个结构的注释可以看到这个结构被「消息队列」,「信号量」,「共享内存」3 种 IPC 所使用,所以这个结构非常的关键,其中的函数指针基本都指向某一个 IPC 的实现函数,之后被底层回调使用。
整个过程如下:
shmget
因为 shmget 会创建或者获取一个 SHM,所以最后会涉及内核内存的分配 vmalloc 和 kmalloc,详细的过程可以继续跟踪 ipc/shm.c
结语
这次我们学习了速度最快的 IPC 机制:共享内存(Shared Memory),IPC 的 API 操作比较多这里不可能全部介绍,但是一定要善于使用 man 手册。我们也在内核中了解了 shmget 的基本调用过程,其他的函数也是类似的,都可以通过 SYSCALL_DEFINE 这个宏开始跟踪,这个内核系统调用的关键地方。希望你能多多实践,也欢迎交流。
感谢你的阅读,我们下次再见 :)
linux shared,从 0 开始学习 Linux 系列之「22.共享内存 Shared Memory」相关推荐
- 从0开始学习 GitHub 系列之「04.向GitHub 提交代码」----转载自stormzhang 原创文章
之前的这篇文章「从0开始学习 GitHub 系列之「Git速成」」相信大家都已经对 Git 的基本操作熟悉了,但是这篇文章只介绍了对本地 Git 仓库的基本操作,今天我就来介绍下如何跟远程仓库一起协作 ...
- 从0开始学习 GitHub 系列之「06.团队合作利器 Branch」----转载自stormzhang 原创文章
Git 相比于 SVN 最强大的一个地方就在于「分支」,Git 的分支操作简直不要太方便,而实际项目开发中团队合作最依赖的莫过于分支了,关于分支前面的系列也提到过,但是本篇会详细讲述什么是分支.分支的 ...
- 从0开始学习GitHub系列之「Git 速成」
从0开始学习GitHub系列之「Git 速成」 糖果果| 2016-06-24 10:55 浏览量(32) 评论(0) 推荐(0) 数据 小编注:[从0开始学习 GitHub]是一个系 ...
- 从0开始学习GitHub系列之「认识并加入GitHub」
从0开始学习GitHub系列之「认识并加入GitHub」 糖果果| 2016-06-16 16:01 浏览量(245) 评论(0) 推荐(0) 数据 小编注:[从0开始学习 GitHu ...
- 从0开始学习GitHub系列之「向GitHub 提交代码」
DevStore首页 >文章 >文章详情 从0开始学习GitHub系列之「向GitHub 提交代码」 糖果果| 2016-06-15 10:57 浏览量(500) 评论(1) ...
- 从0开始学习 GitHub 系列之「03.Git 速成」
前面的 GitHub 系列文章介绍过,GitHub 是基于 Git 的,所以也就意味着 Git 是基础,如果你不会 Git ,那么接下来你完全继续不下去,所以今天的教程就来说说 Git ,当然关于 G ...
- 从0开始学习 GitHub 系列之「03.Git 速成」----转载自stormzhang 原创文章
前面的 GitHub 系列文章介绍过,GitHub 是基于 Git 的,所以也就意味着 Git 是基础,如果你不会 Git ,那么接下来你完全继续不下去,所以今天的教程就来说说 Git ,当然关于 G ...
- 从0开始学习 GITHUB 系列之「加入 GITHUB」【转】
本文转载自:http://stormzhang.com/github/2016/05/26/learn-github-from-zero2/ 版权声明:本文为 stormzhang 原创文章,可以随意 ...
- 从0开始学习 GitHub 系列之「初识 GitHub」
## 1. 写在前面 我一直认为 GitHub 是程序员必备技能,程序员应该没有不知道 GitHub 的才对,没想到这两天留言里给我留言最多的就是想让我写关于 GitHub 的教程,说看了不少资料还是 ...
最新文章
- 2022-2028年中国防臭袜行业投资分析及前景预测报告
- android siri 源码,Android的SIRI 。
- Hibernate Tools 学习总结
- openwrt多wan限上下行速脚本,基于qosv4,imq模块替换成ifb模块[ZT]
- 【转】细说.NET中的多线程 (三 使用Task)
- PHP设计模式之工厂模式
- 系统学习机器学习之特征工程(四)--分箱总结
- 牢记将iPhone特色硬件优势发挥到极致
- 《数字图像处理》实验二
- 20155305《网络对抗》信息搜集与漏洞扫描
- 03.NopCommerce功能与特点介绍
- 书单 | 振聋发聩,撼世经典!总有那么一些书经得住时间的考验
- uos系统桌面怎么没有计算机图标,UOS系统体验:启动、桌面和开始菜单
- 清除maven仓库lastUpdated文件
- APISpace 星座查询API
- word中公式编辑器的快捷键
- 《阴阳师》手游分析报告
- wordpress博客构建
- 我用Python抓取了自如上所有的租房信息,随心所欲的选房
- 电脑调整分区后分区不见恢复数据的方法
热门文章
- 文巾解题 1190. 反转每对括号间的子串
- linux桌面文件夹改图标,Linux 给桌面程序设置个性化图标
- python if main_python中if __name__ == '__main__' :main(()
- 【Linux】24_网络管理数据链路层详解
- 互联网性能与容量评估的方法论和典型案例
- Jackson学习二之集合类对象与JSON互相转化--转载
- windows端口查看及进程查找
- linux shell 流程控制(条件if,循环【for,while】,选择【case】语句实例 --转载
- apache2 默认端口修改
- Ubuntu 18.04上进行HyperLedger Fabric 1.2.0环境及链码安装、部署和测试