2016-01-21 200 views
0

我有一個非常簡單的代碼來測試低內存地址上的mmap。mmap成功,但寫入失敗

unsigned long *p = mmap ((void*)(4096*16), 4096, PROT_READ|PROT_WRITE|PROT_EXEC, 
       MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0); 
    fprintf (stderr, "p=0x%lx\n", (unsigned long)p);` 
    *p = 2554; 
    printf ("p=0x%lx; *p=%ld\n", (unsigned long)p, *p); 

當我運行的代碼,我得到下面的輸出:

p=0x10000 
Segmentation fault (core dumped) 
在dmesg的日誌

,我可以看到下面的打印:

segfault at 10000 ip 00000000004006cc sp 00007fff5845f4c0 error 6 

總體而言,它似乎MMAP是成功的,但寫入操作失敗。我無法解釋這兩個衝突意見。請幫幫我。謝謝。

回答

0

如果您在mmap調用中忽略flags參數中的MAP_GROWSDOWN,則可能會發現不再發生分段故障。

如果檢查mmap電話後/proc/$PID/maps文件,你可能會看到一個奇特(與包含在flagsMAP_GROWSDOWN)。地址似乎比請求的地址高出一頁,並且該映射的大小似乎比請求的頁面小了一頁。簡而言之,該映射的起始地址是4096字節。在MAP_GROWSDOWN的文檔中我沒有發現這種奇怪的現象,它看起來更像是一個bug,而不是一個功能。不管你是否看到特殊的怪異可能取決於你使用的內核版本(我從標籤中假設你正在使用Linux內核)。在任何情況下,在過程處於活動狀態時檢查該文件可能是有教育意義的,即使您的代碼沒有MAP_GROWSDOWN也能按預期工作。

保持進程活動時間足夠長以檢查其maps文件的一種方法是在gdb中設置斷點。在調用mmap的函數中的任何地方都可以,如果你步入足夠遠的地方(剛剛通過mmap調用)。上述路徑名中的$PID旨在表示調用mmap的進程的進程ID。您可以從合適的ps輸出或從的輸出gdb中獲得該進程ID。

爲了解決您的特定問題,mmap調用的成功反映了maps文件中列出的映射(即使該示例中映射的大小爲零),而失敗反映了返回值之間的差異mmap(0x10000)和映射的開始(0x11000)。以4096作爲大小(如你的例子)沒有地址將允許*p的分配,但更大的大小增加4096到返回值mmap會給你一個工作地址(假設你的內核的行爲與我的相同)。如果映射的開始等於mmap返回值(因爲它沒有MAP_GROWSDOWN),則不會有差異。

+0

感謝您的回答。正如你所提到的,它似乎是一個內核bug而不是一個功能。 – strongerwill