成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

淺析Linux的共享內存與tmpfs文件系統

系統 Linux
POSIX共享內存是基于tmpfs來實現的。實際上,更進一步,不僅PSM(POSIX shared memory),而且SSM(System V shared memory)在內核也是基于tmpfs實現的。

[[199907]]

前言

共享內存主要用于進程間通信,Linux有兩種共享內存(Shared Memory)機制:

(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:

  1. # mount -size=64M -o remount /dev/shm# df -lh 
  2.  
  3. Filesystem Size Used Avail Use% Mounted on 
  4.  
  5. tmpfs 64M 0 64M 0% /dev/shm 

 

SYSV共享內存的***大小為32M:

  1. # cat /proc/sys/kernel/shmmax 
  2.  
  3. 33554432 

 

(1)創建65M的system V共享內存失敗:

  1. # ipcmk -M 68157440 
  2.  
  3. ipcmk: create share memory failed: Invalid argument 

 

這是正常的。

(2)將shmmax調整為65M

  1. # echo 68157440 > /proc/sys/kernel/shmmax# cat /proc/sys/kernel/shmmax 
  2.  
  3. 68157440# ipcmk -M 68157440 
  4.  
  5. Shared memory id: 0# ipcs -m 
  6.  
  7. ------ Shared Memory Segments -------- 
  8.  
  9. key shmid owner perms bytes nattch status 
  10.  
  11. 0xef46b249 0 root 644 68157440 0 

 

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

(3)創建POSIX共享內存

點擊(此處)折疊或打開

 

  1.     /*gcc -o shmopen shmopen.c -lrt*/#include <unistd.h> 
  2.  
  3.     #include <fcntl.h> 
  4.  
  5.     #include <sys/stat.h> 
  6.  
  7.     #include <sys/types.h> 
  8.  
  9.     #include <sys/mman.h> 
  10.  
  11.     #include <stdio.h> 
  12.  
  13.     #include <stdlib.h> 
  14.  
  15.     #define MAP_SIZE 68157440 
  16.  
  17.     int main(int argc, char *argv[]) 
  18.  
  19.     { 
  20.  
  21.         int fd; 
  22.  
  23.         void* result; 
  24.  
  25.         fd = shm_open("/shm1", O_RDWR|O_CREAT, 0644); 
  26.  
  27.         if(fd < 0){ 
  28.  
  29.             printf("shm_open failed\n"); 
  30.  
  31.             exit(1); 
  32.  
  33.         } 
  34.  
  35.         return 0; 
  36.  
  37.     } 
  38.  
  39.  
  40. # ./shmopen# ls -lh /dev/shm/shm1 
  41.  
  42. -rw-r--r-- 1 root root 65M Mar  3 06:19 /dev/shm/shm1 

 

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

(4)向POSIX SM寫數據

點擊(此處)折疊或打開

  1.     /*gcc -o shmwrite shmwrite.c -lrt*/#include <unistd.h> 
  2.  
  3.     #include <fcntl.h> 
  4.  
  5.     #include <sys/stat.h> 
  6.  
  7.     #include <sys/types.h> 
  8.  
  9.     #include <sys/mman.h> 
  10.  
  11.     #include <stdio.h> 
  12.  
  13.     #include <stdlib.h> 
  14.  
  15.     #define MAP_SIZE 68157440 
  16.  
  17.     int main(int argc, char *argv[]) 
  18.  
  19.     { 
  20.  
  21.         int fd; 
  22.  
  23.         void* result; 
  24.  
  25.         fd = shm_open("/shm1", O_RDWR|O_CREAT, 0644); 
  26.  
  27.         if(fd < 0){ 
  28.  
  29.              printf("shm_open failed\n"); 
  30.  
  31.              exit(1); 
  32.  
  33.         } 
  34.  
  35.         if (ftruncate(fd, MAP_SIZE) < 0){ 
  36.  
  37.             printf("ftruncate failed\n"); 
  38.  
  39.             exit(1); 
  40.  
  41.         } 
  42.  
  43.         result = mmap(NULL, MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 
  44.  
  45.         if(result == MAP_FAILED){ 
  46.  
  47.             printf("mapped failed\n"); 
  48.  
  49.             exit(1); 
  50.  
  51.         } 
  52.  
  53.         /* ... operate result pointer */ 
  54.  
  55.         printf("memset\n"); 
  56.  
  57.         memset(result, 0, MAP_SIZE); 
  58.  
  59.         //shm_unlink("/shm1"); 
  60.  
  61.         return 0; 
  62.  
  63.     } 
  64.  
  65.  
  66. # ./shmwrite 
  67.  
  68. memset 
  69.  
  70. Bus error 

 

可以看到,寫65M的數據會報Bus error錯誤。

但是,卻可以在/dev/shm創建新的文件:

  1. # ls -lh /dev/shm/ -lh 
  2.  
  3. 總用量 64M 
  4.  
  5. -rw-r--r-- 1 root root 65M 3月 3 15:23 shm1 
  6.  
  7. -rw-r--r-- 1 root root 65M 3月 3 15:24 shm2 
  8.  
  9. 這很正常,ls顯示的是inode->size。 
  10.  
  11. # stat /dev/shm/shm2 
  12.  
  13. File: "/dev/shm/shm2" 
  14.  
  15. Size: 68157440 Blocks: 0 IO Block: 4096 普通文件 
  16.  
  17. Device: 10h/16d Inode: 217177 Links: 1 
  18.  
  19. Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) 
  20.  
  21. Access: 2015-03-03 15:24:28.025985167 +0800 
  22.  
  23. Modify: 2015-03-03 15:24:28.025985167 +0800 
  24.  
  25. Change: 2015-03-03 15:24:28.025985167 +0800 

 

(5)向SYS V共享內存寫數據

將System V共享內存的***值調整為65M(/dev/shm仍然為64M)。

  1. # cat /proc/sys/kernel/shmmax 
  2.  
  3. 68157440 

 

點擊(此處)折疊或打開

 

  1. /*gcc -o shmv shmv.c*/#include <sys/ipc.h> 
  2.  
  3. #include <sys/shm.h> 
  4.  
  5. #include <sys/types.h> 
  6.  
  7. #include <unistd.h> 
  8.  
  9. #define MAP_SIZE 68157440 
  10.  
  11. int main(int argc, char** argv){ 
  12.  
  13. int shm_id,i; 
  14.  
  15. key_t key
  16.  
  17. char temp
  18.  
  19. char *p_map; 
  20.  
  21. charname = "/dev/shm/shm3"
  22.  
  23. key = ftok(name,0); 
  24.  
  25. if(key==-1) 
  26.  
  27. perror("ftok error"); 
  28.  
  29. shm_id=shmget(key,MAP_SIZE,IPC_CREAT); 
  30.  
  31. if(shm_id==-1) 
  32.  
  33.  
  34. perror("shmget error"); 
  35.  
  36. return
  37.  
  38.  
  39. p_map=(char*)shmat(shm_id,NULL,0); 
  40.  
  41. memset(p_map, 0, MAP_SIZE); 
  42.  
  43. if(shmdt(p_map)==-1) 
  44.  
  45. perror(" detach error "); 
  46.  
  47.  
  48. #./shmv 

 

卻可以正常執行。

(7)結論

雖然System V與POSIX共享內存都是通過tmpfs實現,但是受的限制卻不相同。也就是說/proc/sys/kernel/shmmax只會影響SYS V共享內存,/dev/shm只會影響Posix共享內存。實際上,System V與Posix共享內存本來就是使用的兩個不同的tmpfs實例(instance)。

內核分析

內核在初始化時,會自動mount一個tmpfs文件系統,掛載為shm_mnt:

點擊(此處)折疊或打開

  1. //mm/shmem.cstatic struct file_system_type  
  2.  
  3. shmem_fs_type = { 
  4.  
  5.     .owner = THIS_MODULE, 
  6.  
  7.    .name = "tmpfs"
  8.  
  9.     .get_sb = shmem_get_sb, 
  10.  
  11.     .kill_sb = kill_litter_super, 
  12.  
  13. }; 
  14.  
  15.  
  16. int __init shmem_init(void) { 
  17.  
  18.     ... 
  19.  
  20.     error = register_filesystem(&shmem_fs_type); 
  21.  
  22.     if (error)  
  23.  
  24.     { 
  25.  
  26.         printk(KERN_ERR "Could not register tmpfs\n"); 
  27.  
  28.         goto out2; 
  29.  
  30.     } 
  31.  
  32.     ///掛載tmpfs(用于SYS V)  
  33.  
  34.     shm_mnt = vfs_kern_mount(&shmem_fs_type, MS_NOUSER,shmem_fs_type.nameNULL); 

 

/dev/shm的mount與普通文件mount的流程類似,不再討論。但是,值得注意的是,/dev/shm默認的大小為當前物理內存的1/2:

  1. shmem_get_sb –> shmem_fill_super 

點擊(此處)折疊或打開

  1. //mem/shmem.c 
  2.  
  3. int shmem_fill_super(struct super_block *sb, void *data, int silent) 
  4.  
  5.  
  6.     ... 
  7.  
  8. #ifdef CONFIG_TMPFS  
  9.  
  10. /* 
  11.  
  12. * Per default we only allow half of the physical ram per 
  13.  
  14. * tmpfs instance, limiting inodes to one per page of lowmem; 
  15.  
  16. * but the internal instance is left unlimited. 
  17.  
  18. */ 
  19.  
  20.     if (!(sb->s_flags & MS_NOUSER)) {///內核會設置MS_NOUSER  
  21.  
  22.         sbinfo->max_blocks = shmem_default_max_blocks(); 
  23.  
  24.         sbinfo->max_inodes = shmem_default_max_inodes(); 
  25.  
  26.         if (shmem_parse_options(data, sbinfo, false)) { 
  27.  
  28.             err = -EINVAL; 
  29.  
  30.             goto failed; 
  31.  
  32.         } 
  33.  
  34.     } 
  35.  
  36.     sb->s_export_op = &shmem_export_ops; 
  37.  
  38. #else 
  39.  
  40. ... 
  41.  
  42.  
  43. #ifdef CONFIG_TMPFS 
  44.  
  45. static unsigned long shmem_default_max_blocks(void) { 
  46.  
  47.     return totalram_pages / 2; 
  48.  

 

可以看到:由于內核在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錯誤:

點擊(此處)折疊或打開

  1. static const struct vm_operations_struct shmem_vm_ops = { 
  2.  
  3.     .fault = shmem_fault, 
  4.  
  5. #ifdef CONFIG_NUMA  
  6.  
  7.     .set_policy = shmem_set_policy, 
  8.  
  9.     .get_policy = shmem_get_policy, 
  10.  
  11. #endif 
  12.  
  13. }; 
  14.  
  15.  
  16. static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 
  17.  
  18.  
  19.     struct inode *inode = vma->vm_file->f_path.dentry->d_inode; 
  20.  
  21.     int error; 
  22.  
  23.     int ret = VM_FAULT_LOCKED; 
  24.  
  25.     error = shmem_getpage(inode, vmf->pgoff, &vmf->page, SGP_CACHE, &ret); 
  26.  
  27.     if (error) 
  28.  
  29.         return ((error == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS); 
  30.  
  31.     return ret; 
  32.  
  33.  
  34.  
  35. shmem_getpage –> shmem_getpage_gfp: 
  36.  
  37. /* 
  38.  
  39.  * shmem_getpage_gfp - find page in cache, or get from swap, or allocate 
  40.  
  41.  * 
  42.  
  43.  * If we allocate a new one we do not mark it dirty. That's up to the 
  44.  
  45.  * vm. If we swap it in we mark it dirty since we also free the swap 
  46.  
  47.  * entry since a page cannot live in both the swap and page cache 
  48.  
  49.  */ 
  50.  
  51. static int shmem_getpage_gfp(struct inode *inode, pgoff_t index
  52.  
  53. struct page **pagep, enum sgp_type sgp, gfp_t gfp, int *fault_type)  
  54.  
  55.  
  56.     ... 
  57.  
  58.     if (sbinfo->max_blocks) { ///dev/shm會有該值  
  59.  
  60.         if (percpu_counter_compare(&sbinfo->used_blocks,sbinfo->max_blocks) >= 0) { 
  61.  
  62.             error = -ENOSPC; 
  63.  
  64.             goto unacct; 
  65.  
  66.         } 
  67.  
  68.     percpu_counter_inc(&sbinfo->used_blocks); 
  69.  
  70.     } 
  71.  
  72.     //分配一個物理PAGE 
  73.  
  74.     page = shmem_alloc_page(gfp, info, index); 
  75.  
  76.     if (!page) { 
  77.  
  78.         error = -ENOMEM; 
  79.  
  80.         goto decused; 
  81.  
  82.     } 
  83.  
  84.     SetPageSwapBacked(page); 
  85.  
  86.     __set_page_locked(page); 
  87.  
  88.     error = mem_cgroup_cache_charge(page, current->mm,gfp & GFP_RECLAIM_MASK); ///mem_cgroup檢查 
  89.  
  90. if (!error) 
  91.  
  92.     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共享內存的***值(所有之和)。 

責任編輯:龐桂玉 來源: 嵌入式Linux中文站
相關推薦

2020-10-29 08:34:48

Linux - tmp

2021-01-22 10:40:08

Linux文件內存

2011-01-13 13:18:38

Linux網絡文件

2010-04-22 14:42:34

Aix操作系統

2011-01-13 14:10:30

Linux文件系統

2020-07-22 14:53:06

Linux系統虛擬文件

2017-04-25 15:50:02

sparse傳輸處理

2021-06-06 16:55:22

Linux文件系統

2021-04-12 05:44:44

Linux文件系統

2020-09-25 07:48:12

Linux系統文件

2011-01-11 10:29:35

Linux文件

2012-05-10 13:49:44

Linux文件系統

2018-09-29 11:02:52

WindowsLinux互訪

2014-08-22 15:50:52

WindowsLinux

2010-05-19 15:23:51

Linux 2.6.3Ceph分布式文件系統

2009-12-22 15:12:33

Linux擴展文件系統

2009-12-25 09:58:46

linux劃分文件系統

2009-12-14 13:14:57

2021-11-01 13:38:55

Linux文件系統

2021-03-17 21:34:44

Linux內存管理
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产乱码精品一区二区三区中文 | 一级在线 | 成人精品一区亚洲午夜久久久 | 国产色网 | 激情网站 | 国产欧美日韩在线一区 | 国产午夜精品一区二区三区四区 | 电影91久久久 | 欧美性区| 久久精品成人 | 欧美一区二区在线播放 | 欧美亚洲免费 | 日本特黄a级高清免费大片 特黄色一级毛片 | 另类二区 | 91精品国产综合久久婷婷香蕉 | 粉嫩高清一区二区三区 | 草久久| 精品久久久久久久久久 | 亚洲综合热 | 奇米av| 日韩理论电影在线观看 | 美日韩免费视频 | 精品欧美 | 国产色爽| 亚洲在线视频 | 久久精品91久久久久久再现 | 日日夜夜精品视频 | 久久精品91 | 中文字幕91av | 国产夜恋视频在线观看 | www.夜夜骑.com| 在线成人 | 久久国产精品一区 | 欧美日韩在线视频一区 | 国产色网| 久久99精品久久久久久青青日本 | 亚洲女人天堂成人av在线 | 中文在线视频观看 | 精品一区二区三区91 | 国产午夜影院 | 黄色网址av |