2012-04-18 204 views
3

我與mmap試驗,並與下面的示例代碼來:爲什麼mmap文件導致使用比文件大小更多的內存?

int main() { 

    int fd; 
    char *filename = "/home/manu/file"; 
    struct stat statbuf; 
    int i = 0; 
    char c = *(filename); 

    // Get file descriptor and file length 
    fd = open(filename, O_RDONLY); 
    if (fd == -1) { 
     perror("fopen error"); 
    } 
    if (fstat(fd, &statbuf) < 0) { 
     perror("fstat error"); 
    } 
    printf("File size is %ld\n", statbuf.st_size); 

    // Map the file 
    char* mmapA = (char*) mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, 
      fd, 0); 
    if (mmapA == MAP_FAILED) { 
     perror("mmap error"); 
     return 1; 
    } 

    // Touch all the mapped pages 
    while (i < statbuf.st_size) { 
     c = mmapA[i]; 
     i++; 
    } 
    c++; 

    // Close file descriptor 
    if (close(fd) == -1) { 
     perror("close"); 
     return 1; 
    } 

    //Unmap file 
    munmap(mmapA, statbuf.st_size); 

    return EXIT_SUCCESS; 
} 

的文件大小爲137948個字節= 134,7千字節。 檢查程序的內存我正在使用top,主要是RES和VIRT列。

  1. 只是
  2. 只是mmap電話
  3. mmap調用之前讀取所有映射內存有該文件的有效加載到主內存後(:我在三個不同的地方尋找這些值之後頁面錯誤)

由最高記錄的數值是

  1. VIRT = 1828個RES = 244
  2. VIRT = 1964和RES = 248
  3. VIRT = 1964和RES = 508

1964年至1828年= 136,我想在千字節,因此完美地匹配文件的大小。

但我不明白RES的差異508 - 248 = 260 ..爲什麼它不同於虛擬內存大小和文件大小?

+0

操作系統被編碼爲最常用的最佳用途,或者呼叫聲最大的客戶的典型用途 - 它做了很多事情,可能對您的使用沒有意義。你真的在意嗎? – 2012-04-18 20:44:05

+0

@MartinBeckett不,我根本不在乎;-)這只是爲了學習的目的,我很好奇。 – 2012-04-18 20:47:49

+0

好吧,您可以隨時閱讀源代碼;-)我的猜測是,它總是保留mmap以上的特定內存以防擴展 - 即使在這裏您只能打開只讀。但我不知道,所以我沒有添加這個作爲答案 – 2012-04-18 20:53:16

回答

2

有一點是肯定的:結果取決於系統的狀態,而不僅僅取決於正在運行的應用程序。在我的機器上,在運行程序的前兩次,RES的增加量爲136 kB,但隨後的運行根本不涉及任何增加 - 可能操作系統已將整個文件放入緩存中。有趣的是,運行之間的價值自身差異很大。在第一次運行中,RES的跳躍從344到480kB,但後者運行的RES值始終爲348kB。 SHR也有類似的變化:第一次跳136KB,稍後沒有變化。

在運行應用程序之前,我可以隨意使用dd覆蓋之後用零映射的文件來強制原始情況(使用136 kB跳轉)。

我看着pmaps輸出,但在兩種情況下完全一樣,在致電mmap()後沒有改變。

我不能在這裏再現超大的RES跳躍,但這裏是你可以做的。假設你的二進制文件編譯爲a.out。在mmap()之後插入10秒鐘的睡眠時間,然後在munmap()之前再睡10秒鐘。這給出了一個時間窗口來轉儲有趣的信息。我們將從/proc中讀取哪些文件駐留在內存中。爲了做到這一點,在你的終端中的其他選項卡中打開了兩個選項卡,在一個運行

./a.out 

,然後立即:

for ((i=0;i<4;i++)); do cat /proc/$(ps -fe | egrep '[a]\.out' | awk '{print $2}')/smaps > smaps.$i; sleep 5; done 

這將創建4個快照程序的地圖狀態四個單獨的文件。連續編號的快照之間的差異應顯示在RES大小激增期間發生了哪些變化。在樣品運行期間,我的機器,差異快照1和2之間,以及更改[注意我改變映射文件的名稱,但在這裏並不重要]:

[email protected]:~$ diff -u smaps.{1,2} 
--- smaps.1  2012-04-19 00:01:46.000000000 +0200 
+++ smaps.2  2012-04-19 00:01:51.000000000 +0200 
@@ -84,13 +84,13 @@ 
MMUPageSize:   4 kB 
b782f000-b7851000 r--p 00000000 08:05 429102  /tmp/tempfile 
Size:    136 kB 
-Rss:     0 kB 
-Pss:     0 kB 
+Rss:     136 kB 
+Pss:     136 kB 
Shared_Clean:   0 kB 
Shared_Dirty:   0 kB 
-Private_Clean:   0 kB 
+Private_Clean:  136 kB 
Private_Dirty:   0 kB 
-Referenced:   0 kB 
+Referenced:   136 kB 
Swap:     0 kB 
KernelPageSize:  4 kB 
MMUPageSize:   4 kB 

會發生什麼事是什麼應該:映射文件最初根本不駐留,稍後駐留136 kB。

在您的系統上,diff應該引導您進入RES中額外更改的來源 - 您應該能夠找出Rss值更改的其他文件的名稱。有些條目不是文件,而是其他內存區域,例如,您可能會發現諸如[heap][stack]之類的標記。這也應該證明或反駁nos關於系統庫被加載和堆棧使用增長的建議。

+0

非常感謝這個完整的答案。至於你使用smaps,我在內存映射文件的RSS中只有136kb的差異。因此,我知道想知道頂部或ps(我以前使用的)報告究竟在RSS標籤下面。 – 2012-04-19 19:39:24

+0

'top'是['procps'](http://sourceforge.net/projects/procps/)包的一部分,它也從'/ proc'獲取數據,但顯然來自與smaps不同的地方'。如果您深入研究[code](http://procps.cvs.sourceforge.net/viewvc/procps/procps/),您可能會在'top'中找到RES列的確切來源。 – 2012-04-19 20:54:50

相關問題