本篇内容介绍了“Linux的共享内存与tmpfs文件系统是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
创新互联于2013年开始,先为港南等服务建站,港南等地企业,进行企业商务咨询服务。为港南企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
前言
共享内存主要用于进程间通信,Linux有两种共享内存(Shared Memory)机制:
另外,在Linux中不得不提一下内存映射(也可用于进程间通信):
System V共享内存历史悠久,使用也很广范,很多类Unix系统都支持。一般来说,我们在写程序时也通常使用***种。这里不再讨论如何使用它们,关于POSIX共享内存的详细介绍可以参考这里1,这里2。
** 讲到那么多,那么问题来了,共享内存与tmpfs有什么关系? **
POSIX共享内存是基于tmpfs来实现的。实际上,更进一步,不仅PSM(POSIX shared memory),而且SSM(System V shared memory)在内核也是基于tmpfs实现的。
tmpfs介绍
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
POSIX共享内存与Docker
目前Docker将/dev/shm限制为64M,却没有提供参数,这种做法比较糟糕。如果应用使用大内存的POSIX共享内存,必然会导致问题。
“Linux的共享内存与tmpfs文件系统是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!