热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linux下tmpfs是什么文件,你知道Linux的共享内存与tmpfs文件系统是什么样?

前言共享内存主要用于进程间通信,Linux有两种共享内存(SharedMemory)机制:(1)**SystemVsharedmemory(shmget

前言

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

fd02b9a837ff8ab205049350f1b14e9c.png

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

Original shared memory mechanism, still widely used Sharing between unrelated processes.

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

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

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

** Shared mappings – mmap(2) **

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

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

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

** 讲到那么多,那么问题来了,共享内存与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/shm tmpfs defaults 0 0

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

This mount is not needed 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

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

printf("shm_open failed\n");

exit(1);

}

if (ftruncate(fd, MAP_SIZE)

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 http://lwn.net/Articles/516541/

l https://www.kernel.org/doc/Documentation/cgroups/memory.txt

POSIX共享内存与Docker

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

l https://github.com/docker/docker/issues/2606

l https://github.com/docker/docker/pull/4981

总结

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

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



推荐阅读
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了三种方法来实现在Win7系统中显示桌面的快捷方式,包括使用任务栏快速启动栏、运行命令和自己创建快捷方式的方法。具体操作步骤详细说明,并提供了保存图标的路径,方便以后使用。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
author-avatar
毛辰妈妈
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有