linux 共享内存实现
说起共享内存,一般来说会让人想起下面一些方法:
1、多线程。线程之间的内存都是共享的。更确切的说,属于同一进程的线程使用的是同一个地址空间,而不是在不同地址空间之间进行内存共享;
2、父子进程间的内存共享。父进程以MAP_SHARED|MAP_ANONYMOUS选项mmap一块匿名内存,fork之后,其子孙进程之间就能共享这块内存。这种共享内存由于受到进程父子关系的限制,一般较少使用;
3、mmap文件。多个进程mmap到同一个文件,实际上就是大家在共享文件page cache中的内存。不过文件牵涉到磁盘的读写,用来做共享内存显然十分笨重,所以就有了不跟磁盘扯上关系的内存文件,也就是我们这里要讨论的tmpfs和shmem;
tmpfs是一套虚拟的文件系统,在其中创建的文件都是基于内存的,机器重启即消失。
shmem是一套ipc,通过相应的ipc系统调用shmget能够以指定key创建一块的共享内存。需要使用这块内存的进程可以通过shmat系统调用来获得它。
虽然是两套不同的接口,但是在内核里面的实现却是同一套。shmem内部挂载了一个tmpfs分区(用户不可见),shmget就是在该分区下获取名为"SYSV${key}"的文件。然后shmat就相当于mmap这个文件。
所以我们接下来就把tmpfs和shmem当作同一个东西来讨论了。
tmpfs/shmem是一个介于文件和匿名内存之间的东西。
一方面,它具有文件的属性,能够像操作文件一样去操作它。它有自己inode、有自己的page cache;
另一方面,它也有匿名内存的属性。由于没有像磁盘这样的外部存储介质,内核在内存紧缺时不能简单的将page从它们的page cache中丢弃,而需要swap-out;(参阅《linux页面回收浅析》)
对tmpfs/shmem内存的读写,就是对page cache中相应位置的page所代表的内存进行读写,这一点跟普通的文件映射没有什么不同。
如果进程地址空间的相应位置尚未映射,则会建立到page cache中相应page的映射;
如果page cache中的相应位置还没有分配page,则会分配一个。当然,由于不存在磁盘上的源数据,新分配的page总是空的(特别的,通过read系统调用去读一个尚未分配page的位置时,并不会分配新的page,而是共享ZERO_PAGE);
如果page cache中相应位置的page被回收了,则会先将其恢复;
对于第三个“如果”,tmpfs/shmem和普通文件的page回收及其恢复方式是不同的:
page回收时,跟普通文件的情况一样,内核会通过prio_tree反向映射找到映射这个page的每一个page table,然后将其中对应的pte清空。
不同之处是普通文件的page在确保与磁盘同步(如果page为脏的话需要刷回磁盘)之后就可以丢弃了,而对于tmpfs/shmem的page则需要进行swap-out。
注意,匿名page在被swap-out时,并不是将映射它的pte清空,而是得在pte上填写相应的swap_entry,以便知道page被换出到哪里去,否则再需要这个page的时候就没法swap-in了。
而tmpfs/shmem的page呢?page table中对应的pte被清空,swap_entry会被存放在page cache的radix_tree的对应slot上。
等下一次访问触发page fault时,page需要恢复。
普通文件的page恢复跟page未分配时的情形一样,需要新分配page、然后根据映射的位置重新从磁盘读出相应的数据;
而tmpfs/shmem则是通过映射的位置找到radix_tree上对应的slot,从中得到swap_entry,从而进行swap-in,并将新的page放回page cache;
这里就有个问题了,在page cache的radix_tree的某个slot上,怎么知道里面存放着的是正常的page?还是swap-out后留下的swap_entry?
如果是swap_entry,那么slot上的值将被加上RADIX_TREE_EXCEPTIONAL_ENTRY标记(值为2)。swap_entry的值被左移两位后OR上RADIX_TREE_EXCEPTIONAL_ENTRY,填入slot。
也就是说,如果${slot} & RADIX_TREE_EXCEPTIONAL_ENTRY != 0,则它代表swap_entry,且swap_entry的值是${slot} >> 2;否则它代表page,${slot}就是指向page的指针,当然其值可能是NULL,说明page尚未分配。
那么显然,page的地址值其末两位肯定是0,否则就可能跟RADIX_TREE_EXCEPTIONAL_ENTRY标记冲突了;而swap_entry的值最大只能是30bit或62bit(对应32位或64位机器),否则左移两位就溢出了。
最后以一张图说明一下匿名page、文件映射page、tmpfs/shmem page的回收及恢复过程:

linux 共享内存实现的更多相关文章
- Linux 程序设计1:深入浅出 Linux 共享内存
笔者最近在阅读Aerospike 论文时,发现了Aerospike是利用了Linux 共享内存机制来实现的存储索引快速重建的.这种方式比传统利用索引文件进行快速重启的方式大大提高了效率.(减少了磁盘 ...
- linux 共享内存shm_open实现进程间大数据交互
linux 共享内存shm_open实现进程间大数据交互 read.c #include <sys/types.h> #include <sys/stat.h> #includ ...
- linux 共享内存
共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输.这种高效带来的问题是,我们必须用其他手段来同步进程对共享内存的访问,否则会产生竞态条件.所以,共享内存通常和其他进程间通信方式一起使用 ...
- Linux共享内存(二)
Linux共享内存编程实例 原文链接:http://blog.csdn.net/pcliuguangtao/article/details/6526119 /*共享内存允许两个或多个进程进程共享同一块 ...
- Linux共享内存
1.什么是共享内存在前面讲虚拟内存机制时,有讲到Linux的内存映射机制:初始化虚拟内存区域时,会把虚拟内存和磁盘文件对象对应起来.由于内存映射机制,一个磁盘文件对象可被多个进程共享访问,也可被多个进 ...
- Linux共享内存(一)
inux系统编程我一直看 <GNU/LINUX编程指南>,只是讲的太简单了,通常是书和网络上的资料结合着来掌握才比较全面 .在掌握了书上的内容后,再来都其他资料 . 原文链接 http:/ ...
- 修改linux共享内存大小
这是实际linux系统显示的实际数据: beijibing@bjb-desktop:/proc/sys/kernel$ cat shmmax 33554432 beijibing@bjb-deskt ...
- 【转载】ipcs与Linux共享内存
一.共享内存相关知识 所谓共享内存,就是多个进程间共同地使用同一段物理内存空间,它是通过将同一段物理内存映射到不同进程的 虚拟空间来实现的.由于映射到不同进程的虚拟空间中,不同进程可以直接使用,不需要 ...
- Linux 共享内存 详解
一.什么是共享内存区 共享内存区是最快的可用IPC形式.它允许多个不相关的进程去访问同一部分逻辑内存.如果需要在两个运行中的进程之间传输数据,共享内存将是一种效率极高的解决方案.一旦这样的内存区映射到 ...
随机推荐
- git log --stat常用命令
1,显示被修改文件的修改统计信息,添加或删除了多少行. git log --stat 2,显示最近两条的修改 git log --stat -2 3,显示具体的修改 git log -p -2 4, ...
- linux shell中判断bash脚本输入的参数个数
看下面的一段程序. #!/bin/bash ]; then echo "参数个数为$#个" else echo "没有参数" fi
- Java字节流:InputStream OutputStream
字节输入流:InputStream 类声明: public abstract class InputStream implements Closeable 位于java.io包下,是一个抽象类. 官方 ...
- Unity相关路径
Application.dataPath 只读 在项目根目录下读取文件,但移动端没有访问权限.一般适用于PC端调试用. Application.streamingAssetsPath 在Assets目 ...
- 使用python标准库urllib2访问网页
#访问不需要登录的网页import urllib2target_page_url='http://10.224.110.118/myweb/view.jsp' f = urllib2.urlopen( ...
- zookeeper集群配置与启动
摘要:Zookeeper是一个很好的集群管理工具,被大量用于分布式计算.如Hadoop以及Storm系统中.这里简单记录下Zookeeper集群环境的搭建过程.本文以Ubuntu 12.04 LTS作 ...
- mysql的隐式转化
MySQL隐式转化整理 前几天在微博上看到一篇文章:价值百万的 MySQL 的隐式类型转换感觉写的很不错,再加上自己之前也对MySQL的隐式转化这边并不是很清楚,所以就顺势整理了一下.希望对大家有所帮 ...
- EasyUI中datagrid控件的使用 设置多行表头(两行或多行)
EasyUI中的datagrid控件十分强大,能生成各种复杂的报表,现在因为项目需要,需要生成一个表头两行的表,找了一些说明文档,以下用一个实例来说明一下: 第一种方法: $('#divData'). ...
- SQL server 语句新建用户、对用户授权、删除用户实例
Grant select on tb to db_user --给db_user用户授权 tb表 查询权限 一.命令操作 USE mydb GO --1. 新建测试用户 --1.1 添加登录用户和密码 ...
- tableview侧滑删除
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { ) { retu ...