浅析Linux的共享内存与tmpfs文件系统

前言

共享内存主要用于进程间通信,Linux有两种共享内存(Shared Memory)机制:

(1)** System V shared memory(shmget/shmat/shmdt) **

Original shared memory mechanism, still widely usedSharing between unrelated processes.

(2)**POSIX shared memory(shm_open/shm_unlink) **

Sharing between unrelated processes, without overhead of filesystem I/OIntended to be simpler and better than older APIs.

另外,在Linux中不得不提一下内存映射(也可用于进程间通信):

** Shared mappings – mmap(2) **

lShared anonymous mappings:Sharing between related processes only (related via fork())

lShared file mappings:Sharing between unrelated processes, backed by file in filesystem

System V共享内存历史悠久,使用也很广范,很多类Unix系统都支持。一般来说,我们在写程序时也通常使用第一种。这里不再讨论如何使用它们,关于POSIX共享内存的详细介绍可以参考,。

** 讲到那么多,那么问题来了,共享内存与tmpfs有什么关系?**

The POSIX shared memory object implementation on Linux 2.4 makes use of a dedicated filesystem, which is normally mounted under /dev/shm.

从这里可以看到,POSIX共享内存是基于tmpfs来实现的。实际上,更进一步,不仅PSM(POSIX shared memory),而且SSM(System V shared memory)在内核也是基于tmpfs实现的。

tmpfs介绍

下面是内核文档中关于tmpfs的介绍:

tmpfs has the following uses:

1) There is always a kernel internal mount which you will not see at all. This is used for shared anonymous mappings and SYSV shared memory.

This mount does not depend on CONFIG_TMPFS. If CONFIG_TMPFS is not set, the user visible part of tmpfs is not build. But the internal mechanisms are always present.

2) glibc 2.2 and above expects tmpfs to be mounted at /dev/shm for POSIX shared memory (shm_open, shm_unlink). Adding the following line to /etc/fstab should take care of this:

tmpfs/dev/shmtmpfsdefaults0 0

Remember to create the directory that you intend to mount tmpfs on if necessary.

This mount isnotneeded for SYSV shared memory. The internal mount is used for that. (In the 2.3 kernel versions it was necessary to mount the predecessor of tmpfs (shm fs) to use SYSV shared memory)

从这里可以看到tmpfs主要有两个作用:

(1)用于SYSV共享内存,还有匿名内存映射;这部分由内核管理,用户不可见;

(2)用于POSIX共享内存,由用户负责mount,而且一般mount到/dev/shm;依赖于CONFIG_TMPFS;

到这里,我们可以了解,SSM与PSM之间的区别,也明白了/dev/shm的作用。

下面我们来做一些测试:

测试

我们将/dev/shm的tmpfs设置为64M:

# mount -size=64M -o remount /dev/shm# df -lh

Filesystem                  Size  Used Avail Use% Mounted on

tmpfs                          64M     0   64M   0% /dev/shm

SYSV共享内存的最大大小为32M:

# cat /proc/sys/kernel/shmmax

33554432

(1)创建65M的system V共享内存失败:

# ipcmk -M 68157440

ipcmk: create share memory failed: Invalid argument

这是正常的。

(2)将shmmax调整为65M

# echo 68157440 > /proc/sys/kernel/shmmax# cat /proc/sys/kernel/shmmax

68157440# ipcmk -M 68157440

Shared memory id: 0# ipcs -m

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status

0xef46b249 0          root       644        68157440   0

可以看到system v共享内存的大小并不受/dev/shm的影响。

(3)创建POSIX共享内存

点击(此处)折叠或打开

/*gcc -o shmopen shmopen.c -lrt*/#include

#include

#include

#include

#include

#include

#include

#define MAP_SIZE 68157440

int main(int argc, char *argv[])

{

int fd;

void* result;

fd = shm_open("/shm1", O_RDWR|O_CREAT, 0644);

if(fd < 0){

printf("shm_open failed\n");

exit(1);

}

return 0;

}

# ./shmopen# ls -lh /dev/shm/shm1

-rw-r--r-- 1 root root 65M Mar  3 06:19 /dev/shm/shm1

仅管/dev/shm只有64M,但创建65M的POSIX SM也可以成功。

(4)向POSIX SM写数据

点击(此处)折叠或打开

/*gcc -o shmwrite shmwrite.c -lrt*/#include

#include

#include

#include

#include

#include

#include

#define MAP_SIZE 68157440

int main(int argc, char *argv[])

{

int fd;

void* result;

fd = shm_open("/shm1", O_RDWR|O_CREAT, 0644);

if(fd < 0){

printf("shm_open failed\n");

exit(1);

}

if (ftruncate(fd, MAP_SIZE) < 0){

printf("ftruncate failed\n");

exit(1);

}

result = mmap(NULL, MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

if(result == MAP_FAILED){

printf("mapped failed\n");

exit(1);

}

/* ... operate result pointer */

printf("memset\n");

memset(result, 0, MAP_SIZE);

//shm_unlink("/shm1");

return 0;

}

# ./shmwrite

memset

Bus error

可以看到,写65M的数据会报Bus error错误。

但是,却可以在/dev/shm创建新的文件:

# ls -lh /dev/shm/ -lh

总用量 64M

-rw-r--r-- 1 root root 65M 3月   3 15:23 shm1

-rw-r--r-- 1 root root 65M 3月   3 15:24 shm2

这很正常,ls显示的是inode->size。

# stat /dev/shm/shm2

File:"/dev/shm/shm2"

Size: 68157440        Blocks: 0          IO Block: 4096   普通文件

Device: 10h/16d Inode: 217177      Links: 1

Access:(0644/-rw-r--r--)Uid:(0/    root)Gid:(0/    root)

Access: 2015-03-03 15:24:28.025985167 +0800

Modify: 2015-03-03 15:24:28.025985167 +0800

Change: 2015-03-03 15:24:28.025985167 +0800

(5)向SYS V共享内存写数据

将System V共享内存的最大值调整为65M(/dev/shm仍然为64M)。

# cat /proc/sys/kernel/shmmax

68157440

点击(此处)折叠或打开

/*gcc -o shmv shmv.c*/#include

#include

#include

#include

#define MAP_SIZE 68157440

int main(int argc, char** argv){

int shm_id,i;

key_t key;

char temp;

char *p_map;

char* name = "/dev/shm/shm3";

key = ftok(name,0);

if(key==-1)

perror("ftok error");

shm_id=shmget(key,MAP_SIZE,IPC_CREAT);

if(shm_id==-1)

{

perror("shmget error");

return;

}

p_map=(char*)shmat(shm_id,NULL,0);

memset(p_map, 0, MAP_SIZE);

if(shmdt(p_map)==-1)

perror(" detach error ");

}

#./shmv

却可以正常执行。

(7)结论

虽然System V与POSIX共享内存都是通过tmpfs实现,但是受的限制却不相同。也就是说/proc/sys/kernel/shmmax只会影响SYS V共享内存,/dev/shm只会影响Posix共享内存。实际上,System V与Posix共享内存本来就是使用的两个不同的tmpfs实例(instance)。

内核分析

内核在初始化时,会自动mount一个tmpfs文件系统,挂载为shm_mnt:

点击(此处)折叠或打开

//mm/shmem.cstatic struct file_system_type

shmem_fs_type = {

.owner = THIS_MODULE,

.name = "tmpfs",

.get_sb = shmem_get_sb,

.kill_sb = kill_litter_super,

};

int __init shmem_init(void){

...

error = register_filesystem(&shmem_fs_type);

if (error)

{

printk(KERN_ERR "Could not register tmpfs\n");

goto out2;

}

///挂载tmpfs(用于SYS V)

shm_mnt = vfs_kern_mount(&shmem_fs_type, MS_NOUSER,shmem_fs_type.name, NULL);

/dev/shm的mount与普通文件mount的流程类似,不再讨论。但是,值得注意的是,/dev/shm默认的大小为当前物理内存的1/2:

shmem_get_sb –> shmem_fill_super

点击(此处)折叠或打开

//mem/shmem.c

int shmem_fill_super(struct super_block *sb, void *data, int silent)

{

...

#ifdef CONFIG_TMPFS

/*

* Per default we only allow half of the physical ram per

* tmpfs instance, limiting inodes to one per page of lowmem;

* but the internal instance is left unlimited.

*/

if (!(sb->s_flags & MS_NOUSER)) {///内核会设置MS_NOUSER

sbinfo->max_blocks = shmem_default_max_blocks();

sbinfo->max_inodes = shmem_default_max_inodes();

if (shmem_parse_options(data, sbinfo, false)) {

err = -EINVAL;

goto failed;

}

}

sb->s_export_op = &shmem_export_ops;

#else

...

#ifdef CONFIG_TMPFS

static unsigned long shmem_default_max_blocks(void){

return totalram_pages / 2;

}

可以看到:由于内核在mount tmpfs时,指定了MS_NOUSER,所以该tmpfs没有大小限制,因此,SYS V共享内存能够使用的内存空间只受/proc/sys/kernel/shmmax限制;而用户通过挂载的/dev/shm,默认为物理内存的1/2。

注意CONFIG_TMPFS.

另外,在/dev/shm创建文件走VFS接口,而SYS V与匿名映射却是通过shmem_file_setup实现:

SIGBUS

当应用访问共享内存对应的地址空间,如果对应的物理PAGE还没有分配,就会调用fault方法,分配失败,就会返回OOM或者BIGBUS错误:

点击(此处)折叠或打开

static const struct vm_operations_struct shmem_vm_ops = {

.fault = shmem_fault,

#ifdef CONFIG_NUMA

.set_policy = shmem_set_policy,

.get_policy = shmem_get_policy,

#endif

};

static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)

{

struct inode *inode = vma->vm_file->f_path.dentry->d_inode;

int error;

int ret = VM_FAULT_LOCKED;

error = shmem_getpage(inode, vmf->pgoff, &vmf->page, SGP_CACHE, &ret);

if (error)

return ((error == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS);

return ret;

}

shmem_getpage –> shmem_getpage_gfp:

/*

* shmem_getpage_gfp - find page in cache, or get from swap, or allocate

*

* If we allocate a new one we do not mark it dirty. That's up to the

* vm. If we swap it in we mark it dirty since we also free the swap

* entry since a page cannot live in both the swap and page cache

*/

static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,

struct page **pagep, enum sgp_type sgp, gfp_t gfp, int *fault_type)

{

...

if (sbinfo->max_blocks) { ///dev/shm会有该值

if (percpu_counter_compare(&sbinfo->used_blocks,sbinfo->max_blocks)>=0){

error = -ENOSPC;

goto unacct;

}

percpu_counter_inc(&sbinfo->used_blocks);

}

//分配一个物理PAGE

page = shmem_alloc_page(gfp, info, index);

if (!page) {

error = -ENOMEM;

goto decused;

}

SetPageSwapBacked(page);

__set_page_locked(page);

error = mem_cgroup_cache_charge(page, current->mm,gfp&GFP_RECLAIM_MASK);///mem_cgroup检查

if (!error)

error = shmem_add_to_page_cache(page, mapping, index,gfp,NULL);

共享内存与CGROUP

目前,共享内存的空间计算在第一个访问共享内存的group,参考:

l

lhttps://www.kernel.org/doc/Documentation/cgroups/memory.txt

POSIX共享内存与Docker

目前Docker将/dev/shm限制为64M,却没有提供参数,这种做法比较糟糕。如果应用使用大内存的POSIX共享内存,必然会导致问题。 参考:

l

l

总结

(1)POSIX共享内存与SYS V共享内存在内核都是通过tmpfs实现,但对应两个不同的tmpfs实例,相互独立。

(2)通过/proc/sys/kernel/shmmax可以限制SYS V共享内存(单个)的最大值,通过/dev/shm可以限制POSIX共享内存的最大值(所有之和)。

linux查看共享内存max,浅析Linux的共享内存与tmpfs文件系统相关推荐

  1. linux查看存储类型及型号,linux怎么看内存型号

    Linux可安装在各种计算机硬件设备中,比如手机.平板电脑.路由器.视频游戏控制台.台式计算机.大型机和超级计算机.下面是学习啦小编带来的关于linux怎么看内存型号的内容,欢迎阅读! linux怎么 ...

  2. linux查看网卡连接哪个cpu,Linux查看CPU/内存/网卡/操作系统信息

    测试机器的硬件信息: 查看CPU信息(型号) # cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c 8 Intel(R) Xeon(R) CP ...

  3. linux查看cpu核心电压,来感受Linux命令行的“真香定律”

    Shell看起来只是一个黑黑的命令框,刚开始接触会觉得很丑,毕竟与Win/Mac的华丽界面比起来,命令行终端直接可以丑拒了.但是,实际上它的功能要强大得多,毕竟Linux一开始就是广泛应用于服务器,通 ...

  4. Linux查看WAS的jvm信息,linux 下使用命令查看jvm信息

    java程序员除了编写业务代码之外,特别是项目上线之后,更需要关注的是系统的性能表现,这个时候就需要了解一下jvm的性能表现了,可以借助于java虚拟机自带的一些分析工具,主要有三个常用的命令. 1. ...

  5. linux查看进程运行日志文件,【Linux】常用指令、ps查看进程、kill杀进程、启动停止tomcat命令、查看日志、查看端口、find查找文件...

    1.说出 10 个 linux 常用的指令 1) ls 查看目录中的文件 2)cd /home 进入 '/ home' 目录:cd .. 返回上一级目录:cd ../.. 返回上两级目录 3)mkdi ...

  6. linux查看某进程的连接,linux下查看指定进程的所有连接信息(转)

    定位某个进程的网络故障时经常需要用到的一个功能就是查找所有连接的信息.通常查找某个端口的连接信息使用 ss 或者 netstat 可以轻松拿到,如果是主动与别的机器建立的连接信息则可以通过 lsof ...

  7. linux查看服务器品牌和型号,linux 查看服务器型号

    linux 查看服务器型号 内容精选 换一换 登录弹性云服务器查询磁盘设备信息,发现磁盘的设备名称与控制台上显示的挂载点不一致,不清楚磁盘具体挂载在哪个设备上或磁盘对应的逻辑卷标识.本节操作介绍如何根 ...

  8. linux查看日历命令_在Linux中使用命令行日历和日期功能

    linux查看日历命令 我一直对历史日期感兴趣,并确定事件发生在一周的实际哪一天. 独立宣言在一周的哪一天签署? 我是星期几出生的? 1876年7月4日是星期几? 我知道您可以使用搜索引擎来回答许多这 ...

  9. linux查看每个进程的iops,linux 查看iops

    回 19楼(aston008) 的帖子 关于MySQL 5.7,不支持只是暂时的,希望你能谅解,今年一定会做,今年根据大家的要求情况,适当将计划提前排期完成,具体的时间点由于以前需求不强烈还未定,如果 ...

最新文章

  1. 用专业的说一句情话 计算机,用电脑专业说一句情话
  2. mysql二进制包下的support-files文件夹
  3. Windows下Python安装及pycharm,pip下载和安装第三方库
  4. QT-X11-3.1.2.tar.bz2的使用
  5. 《spring揭秘》读书笔记三
  6. 离散数学实验题目-关系
  7. VTK:模型之Finance
  8. call()和apply()方法(切换上下文)
  9. php对象数组转数组_php 数组对象互相转换
  10. html的title设置,动态设置html的title
  11. java linux driver,JAVA:使用GeckoDriver在Linux上运行Selenium测试:驱动程序不可执行
  12. 电摩测试速度什么软件,速度最快的4款新电动车,你更看好谁?为什么呢?
  13. 部署ISA2006标准版防火墙
  14. Aliyun平台Nginx+Mysql+Redis部署easyboot
  15. Luogu P1530 分数化小数 Fractions to Decimals(模拟)
  16. 服务器电脑用哪个系统好,电脑系统哪个好用?电脑系统有几种版本
  17. StarUML的使用
  18. 典型电子商务系统前台后台功能分析
  19. idea打开文件关联目录
  20. 地平线检测horizon line detection

热门文章

  1. uni-app H5跨域问题解决方案(CORS、Cross-Origin) VUE axios 跨域问题 No ‘Access-Control-Allow-Origin‘ header is pres
  2. Linux编程(4)_gcc
  3. 一文了解Innodb中的锁
  4. java多线程-生产者消费者模式
  5. java接口自动化(四) - 企业级代码管理工具Git的应用
  6. android 3.0 m3u8,在Android中播放m3u8视频
  7. Failed to resolve loader: less-loader
  8. matlab编写文件格式,MATLAB程序设计教程(4)——MATLAB文件操作
  9. python 多帧 超分辨_利用python-opencv生成视频帧数控制,和常见错误总结
  10. 计算机应用技术老师全国计算机应用技术考试,全国计算机应用证书 全国计算机应用技术证书考试作业.doc...