亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 5941 | 回復: 1
打印 上一主題 下一主題

Linux虛擬內(nèi)存實現(xiàn)原理 [復制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2012-02-22 14:47 |只看該作者 |倒序瀏覽
Linux虛擬內(nèi)存實現(xiàn)原理





下面是一篇翻譯文章,原文出自MongoDB的核心開發(fā)工程師 Kristina Chodorow 的個人博客,由NoSQLFan翻譯整理。

我們都知道,MongoDB 使用內(nèi)存映射的方式來進行數(shù)據(jù)文件的存取操作。本文的目的就在于描述操作系統(tǒng)虛擬內(nèi)存的使用及內(nèi)存映射的內(nèi)部實現(xiàn)。

以下是譯文

當你運行一個程序,程序中有許多東西需要存儲,堆、棧以及各種功能庫。而這一切在你寫程序時可能都不需要自己控制,Linux內(nèi)核會幫你完成這些存儲的調(diào)度,你只需要告訴它你需要做什么,內(nèi)核就會在合適的地方給你分配內(nèi)存空間。本文主要通過幾個實例程序的內(nèi)存使用研究,來為大家展示Linux的內(nèi)存使用狀況。

第一個例子:下面一段程序會打印出程序的pid(進程號)后掛起。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
  printf("run `pmap %d`\n", getpid());
  pause();
}將上面代碼保存成文件 mem_munch.c 然后運行下面程序編譯并執(zhí)行:

$ gcc mem_munch.c -o mem_munch
$ ./mem_munch
run `pmap 25681`上面進程號是25681,可能你試驗的結(jié)果會不太一樣。

下面我們通過pmap命令來查看一下這個小程序的內(nèi)存使用情況
  1. $ pmap 25681
  2. 25681:   ./mem_munch
  3. 0000000000400000      4K r-x--  /home/user/mem_munch
  4. 0000000000600000      4K r----  /home/user/mem_munch
  5. 0000000000601000      4K rw---  /home/user/mem_munch
  6. 00007fcf5af88000   1576K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
  7. 00007fcf5b112000   2044K -----  /lib/x86_64-linux-gnu/libc-2.13.so
  8. 00007fcf5b311000     16K r----  /lib/x86_64-linux-gnu/libc-2.13.so
  9. 00007fcf5b315000      4K rw---  /lib/x86_64-linux-gnu/libc-2.13.so
  10. 00007fcf5b316000     24K rw---    [ anon ]
  11. 00007fcf5b31c000    132K r-x--  /lib/x86_64-linux-gnu/ld-2.13.so
  12. 00007fcf5b512000     12K rw---    [ anon ]
  13. 00007fcf5b539000     12K rw---    [ anon ]
  14. 00007fcf5b53c000      4K r----  /lib/x86_64-linux-gnu/ld-2.13.so
  15. 00007fcf5b53d000      8K rw---  /lib/x86_64-linux-gnu/ld-2.13.so
  16. 00007fff7efd8000    132K rw---    [ stack ]
  17. 00007fff7efff000      4K r-x--    [ anon
復制代碼
]
ffffffffff600000      4K r-x--    [ anon ]
total             3984K上面的結(jié)果是這個程序的內(nèi)存使用情況,其實更確切的說是這個程序認為它使用內(nèi)存的情況。從上面的結(jié)果我們能看到,當你訪問libc庫時,實際上是對內(nèi)存地址00007fcf5af88000的訪問,當你訪問ld庫時,實際上是對內(nèi)存地址00007fcf5b31c000的訪問。

上面的輸出可能還比較抽象,下面我們修改一下上面的程序,我們在程序的堆和棧上各放一塊數(shù)據(jù)。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int main() {
  int on_stack, *on_heap;

  //局部變量是放在棧上的,所以 on_stack 的地址就是棧的初始地址
  on_stack = 42;
  printf("stack address: %p\n", &on_stack);

  //malloc 的內(nèi)存是在堆上分配的
  on_heap = (int*)malloc(sizeof(int));
  printf("heap address: %p\n", on_heap);

  printf("run `pmap %d`\n", getpid());
  pause();
}編譯運行:

  1. $ ./mem_munch
  2. stack address: 0x7fff497670bc
  3. heap address: 0x1b84010
  4. run `pmap 11972`然后再用pmap命令查看一下內(nèi)存使用:

  5. $ pmap 11972
  6. 11972:   ./mem_munch
  7. 0000000000400000      4K r-x--  /home/user/mem_munch
  8. 0000000000600000      4K r----  /home/user/mem_munch
  9. 0000000000601000      4K rw---  /home/user/mem_munch
  10. 0000000001b84000    132K rw---    [ anon ]
  11. 00007f3ec4d98000   1576K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
  12. 00007f3ec4f22000   2044K -----  /lib/x86_64-linux-gnu/libc-2.13.so
  13. 00007f3ec5121000     16K r----  /lib/x86_64-linux-gnu/libc-2.13.so
  14. 00007f3ec5125000      4K rw---  /lib/x86_64-linux-gnu/libc-2.13.so
  15. 00007f3ec5126000     24K rw---    [ anon ]
  16. 00007f3ec512c000    132K r-x--  /lib/x86_64-linux-gnu/ld-2.13.so
  17. 00007f3ec5322000     12K rw---    [ anon ]
  18. 00007f3ec5349000     12K rw---    [ anon ]
  19. 00007f3ec534c000      4K r----  /lib/x86_64-linux-gnu/ld-2.13.so
  20. 00007f3ec534d000      8K rw---  /lib/x86_64-linux-gnu/ld-2.13.so
  21. 00007fff49747000    132K rw---    [ stack ]
  22. 00007fff497bb000      4K r-x--    [ anon ]
  23. ffffffffff600000      4K r-x--    [ anon ]
復制代碼
total             4116K這次多出了上面紅色的一行內(nèi)容,紅色內(nèi)容就是堆的起始位置:

0000000001b84000    132K rw---    [ anon ]在我們程序運行的輸出里也有一行紅色的輸出,這是這個地址在程序中的內(nèi)存地址:

heap address: 0x1b84010這兩個地址基本上是一樣的,其中的anon是Anonymous的縮寫,表明這段內(nèi)存是沒有文件映射的。

我們再看上面綠色的兩行,與上面相對應,這兩行分別是用pmap 和應用程序看到的棧起始地址:

00007fff49747000    132K rw---    [ stack ]stack address: 0x7fff497670bc上面說到的內(nèi)存使用,都只是程序認為自己對內(nèi)存的使用,實際上程序在分配內(nèi)存是不知道系統(tǒng)內(nèi)存的狀態(tài)的。所以上面的輸出都只是從程序自己的角度看到的內(nèi)存使用狀況。比如在上面的例子中,我們看到程序的內(nèi)存地址空間是從0×0000000000400000到0xffffffffff600000的所有地址(而0xffffffffff600000到0×00007fffffffffffffff之間的地址是有特殊用處的,這里不多講)。這樣算下來,我們總共可以使用的內(nèi)存空間有1千萬TB。

但是實際上目前沒有硬件能有1千萬TB的物理內(nèi)存。為什么操作系統(tǒng)會如此設(shè)計呢?原因有很多,可以看這里,但也正因此,我們可以使用遠遠超出物理內(nèi)存大小的內(nèi)存空間。

內(nèi)存映射
內(nèi)存映射的原理就是讓操作系統(tǒng)將一個文件映射到一段內(nèi)存中,然后在操作這個文件內(nèi)存就可以像操作內(nèi)存一樣。比如我們創(chuàng)建一個完全內(nèi)容隨機的文件,然后將它用內(nèi)存映射的方式映射到一段內(nèi)存空間中。那么我們在這段內(nèi)存中隨便取一位就相當于取到了一個隨機數(shù)。下面就讓我們來做這個實驗,先用下面命令生成一個內(nèi)容隨機的文件。
  1. $ dd if=/dev/urandom bs=1024 count=1000000 of=/home/user/random
  2. 1000000+0 records in
  3. 1000000+0 records out
  4. 1024000000 bytes (1.0 GB) copied, 123.293 s, 8.3 MB/s
  5. $ ls -lh random
  6. -rw-r--r-- 1 user user 977M 2011-08-29 16:46 random然后我們用下面程序來將這個文件內(nèi)容映射到內(nèi)存,再從中取出隨機數(shù)

  7. #include <stdio.h>
  8. #include <unistd.h>
  9. #include <sys/types.h>
  10. #include <stdlib.h>
  11. #include <sys/mman.h>

  12. int main() {
  13.   char *random_bytes;
  14.   FILE *f;
  15.   int offset = 0;

  16.   // open "random" for reading
  17.   f = fopen("/home/user/random", "r");
  18.   if (!f) {
  19.     perror("couldn't open file");
  20.     return -1;
  21.   }

  22.   // we want to inspect memory before mapping the file
  23.   printf("run `pmap %d`, then press ", getpid());
  24.   getchar();

  25.   random_bytes = mmap(0, 1000000000, PROT_READ, MAP_SHARED, fileno(f), 0);

  26.   if (random_bytes == MAP_FAILED) {
  27.     perror("error mapping the file");
  28.     return -1;
  29.   }

  30.   while (1) {
  31.     printf("random number: %d (press  for next number)", *(int*)(random_bytes+offset));
  32.     getchar();

  33.     offset += 4;
  34.   }
  35. }然后運行這個程序:

  36. $ ./mem_munch
  37. run `pmap 12727`, then press下面我們通過一次次的按下回車鍵來從這個文件中讀取隨機數(shù),按下幾次后我們可以再通過pmap來查看其內(nèi)存空間的情況:

  38. $ pmap 12727
  39. 12727:   ./mem_munch
  40. 0000000000400000      4K r-x--  /home/user/mem_munch
  41. 0000000000600000      4K r----  /home/user/mem_munch
  42. 0000000000601000      4K rw---  /home/user/mem_munch
  43. 000000000147d000    132K rw---    [ anon ]
  44. 00007fe261c6f000 976564K r--s-  /home/user/random
  45. 00007fe29d61c000   1576K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
  46. 00007fe29d7a6000   2044K -----  /lib/x86_64-linux-gnu/libc-2.13.so
  47. 00007fe29d9a5000     16K r----  /lib/x86_64-linux-gnu/libc-2.13.so
  48. 00007fe29d9a9000      4K rw---  /lib/x86_64-linux-gnu/libc-2.13.so
  49. 00007fe29d9aa000     24K rw---    [ anon ]
  50. 00007fe29d9b0000    132K r-x--  /lib/x86_64-linux-gnu/ld-2.13.so
  51. 00007fe29dba6000     12K rw---    [ anon ]
  52. 00007fe29dbcc000     16K rw---    [ anon ]
  53. 00007fe29dbd0000      4K r----  /lib/x86_64-linux-gnu/ld-2.13.so
  54. 00007fe29dbd1000      8K rw---  /lib/x86_64-linux-gnu/ld-2.13.so
  55. 00007ffff29b2000    132K rw---    [ stack ]
  56. 00007ffff29de000      4K r-x--    [ anon ]
  57. ffffffffff600000      4K r-x--    [ anon ]
復制代碼
total           980684K上面的輸出和之前的大同小異,但是多出了上面紅色的一行。這是我們上面的隨機文件映射到內(nèi)存中的內(nèi)存。我們再使用pmap -x 選項來查看一下程序的內(nèi)存使用,會得到下面的內(nèi)容,其中RSS(resident set size)列表示真實占用的內(nèi)存。
  1. pmap -x 12727
  2. 12727:   ./mem_munch
  3. Address           Kbytes     RSS   Dirty Mode   Mapping
  4. 0000000000400000       0       4       0 r-x--  mem_munch
  5. 0000000000600000       0       4       4 r----  mem_munch
  6. 0000000000601000       0       4       4 rw---  mem_munch
  7. 000000000147d000       0       4       4 rw---    [ anon ]
  8. 00007fe261c6f000       0       4       0 r--s-  random
  9. 00007fe29d61c000       0     288       0 r-x--  libc-2.13.so
  10. 00007fe29d7a6000       0       0       0 -----  libc-2.13.so
  11. 00007fe29d9a5000       0      16      16 r----  libc-2.13.so
  12. 00007fe29d9a9000       0       4       4 rw---  libc-2.13.so
  13. 00007fe29d9aa000       0      16      16 rw---    [ anon ]
  14. 00007fe29d9b0000       0     108       0 r-x--  ld-2.13.so
  15. 00007fe29dba6000       0      12      12 rw---    [ anon ]
  16. 00007fe29dbcc000       0      16      16 rw---    [ anon ]
  17. 00007fe29dbd0000       0       4       4 r----  ld-2.13.so
  18. 00007fe29dbd1000       0       8       8 rw---  ld-2.13.so
  19. 00007ffff29b2000       0      12      12 rw---    [ stack ]
  20. 00007ffff29de000       0       4       0 r-x--    [ anon ]
  21. ffffffffff600000       0       0       0 r-x--    [ anon ]
  22. ----------------  ------  ------  ------
復制代碼
total kB          980684     508     100如果你的虛擬內(nèi)存占用(上面的Kbytes列)都是0,不用擔心,這是一個在Debian/Ubuntu系統(tǒng)上pmap -x命令的bug。最后一行輸出的總占用量是正確的。

現(xiàn)在你可以看一下RSS那一列,這就是實際內(nèi)存占用。在random文件上,你的程序?qū)嶋H上可以訪問在00007fe261c6f000之前的數(shù)十億字節(jié)的內(nèi)存地址,但是只要你訪問的地址超過4KB,那么操作系統(tǒng)就會去磁盤上查找內(nèi)容。也就是說實際上只有4KB的物理內(nèi)存被使用了。只有訪問這4KB的東西時,才是真正的內(nèi)存操作。其它部分雖然你使用的也是內(nèi)存操作函數(shù)來訪問它,但是由于它沒有被加載到內(nèi)存中,所以在這些內(nèi)容被訪問的時候,操作系統(tǒng)會先去磁盤讀random中讀取內(nèi)容到內(nèi)存中。

如果我們把程序再修改一下,修改成下面這樣,讓程序把整個random文件都訪問一遍。
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <stdlib.h>
  5. #include <sys/mman.h>

  6. int main() {
  7.   char *random_bytes;
  8.   FILE *f;
  9.   int offset = 0;

  10.   // open "random" for reading
  11.   f = fopen("/home/user/random", "r");
  12.   if (!f) {
  13.     perror("couldn't open file");
  14.     return -1;
  15.   }

  16.   random_bytes = mmap(0, 1000000000, PROT_READ, MAP_SHARED, fileno(f), 0);

  17.   if (random_bytes == MAP_FAILED) {
  18.     printf("error mapping the file\n");
  19.     return -1;
  20.   }

  21.   for (offset = 0; offset < 1000000000; offset += 4) {
  22.     int i = *(int*)(random_bytes+offset);

  23.     // to show we're making progress
  24.     if (offset % 1000000 == 0) {
  25.       printf(".");
  26.     }
  27.   }

  28.   // at the end, wait for signal so we can check mem
  29.   printf("\ndone, run `pmap -x %d`\n", getpid());
  30.   pause();
復制代碼
}現(xiàn)在我們的pmap -x命令就會得到如下輸出:
  1. $ pmap -x 5378
  2. 5378:   ./mem_munch
  3. Address           Kbytes     RSS   Dirty Mode   Mapping
  4. 0000000000400000       0       4       4 r-x--  mem_munch
  5. 0000000000600000       0       4       4 r----  mem_munch
  6. 0000000000601000       0       4       4 rw---  mem_munch
  7. 0000000002271000       0       4       4 rw---    [ anon ]
  8. 00007fc2aa333000       0  976564       0 r--s-  random
  9. 00007fc2e5ce0000       0     292       0 r-x--  libc-2.13.so
  10. 00007fc2e5e6a000       0       0       0 -----  libc-2.13.so
  11. 00007fc2e6069000       0      16      16 r----  libc-2.13.so
  12. 00007fc2e606d000       0       4       4 rw---  libc-2.13.so
  13. 00007fc2e606e000       0      16      16 rw---    [ anon ]
  14. 00007fc2e6074000       0     108       0 r-x--  ld-2.13.so
  15. 00007fc2e626a000       0      12      12 rw---    [ anon ]
  16. 00007fc2e6290000       0      16      16 rw---    [ anon ]
  17. 00007fc2e6294000       0       4       4 r----  ld-2.13.so
  18. 00007fc2e6295000       0       8       8 rw---  ld-2.13.so
  19. 00007fff037e6000       0      12      12 rw---    [ stack ]
  20. 00007fff039c9000       0       4       0 r-x--    [ anon ]
  21. ffffffffff600000       0       0       0 r-x--    [ anon ]
復制代碼
----------------  ------  ------  ------
total kB          980684  977072     104我們可以看到,random文件映射實際占用內(nèi)存量已經(jīng)和random文件大小一致了,也就是也random文件通過循環(huán)訪問,其內(nèi)容已經(jīng)完全加載到內(nèi)存中了,F(xiàn)在我們再訪問random文件的任何部分,實際上都是內(nèi)存操作。而不會穿透到磁盤。

話說回來,這就是為什么MongoDB的內(nèi)存使用,可以遠遠超出操作系統(tǒng)物理內(nèi)存大小。

來源:www.snailinaturtleneck.com


論壇徽章:
0
2 [報告]
發(fā)表于 2012-02-24 17:38 |只看該作者
謝謝分享
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP