[英]Why does a read operation on a memory mapped zero byte file lead to SIGBUS?
這是我寫的示例代碼。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
int main()
{
int fd;
long pagesize;
char *data;
if ((fd = open("foo.txt", O_RDONLY)) == -1) {
perror("open");
return 1;
}
pagesize = sysconf(_SC_PAGESIZE);
printf("pagesize: %ld\n", pagesize);
data = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0);
printf("data: %p\n", data);
if (data == (void *) -1) {
perror("mmap");
return 1;
}
printf("%d\n", data[0]);
printf("%d\n", data[1]);
printf("%d\n", data[2]);
printf("%d\n", data[4096]);
printf("%d\n", data[4097]);
printf("%d\n", data[4098]);
return 0;
}
如果我向該程序提供零字節foo.txt,它將以SIGBUS終止。
$ > foo.txt && gcc foo.c && ./a.out
pagesize: 4096
data: 0x7f8d882ab000
Bus error
如果我為這個程序提供一個字節的foo.txt,那么就沒有這樣的問題了。
$ printf A > foo.txt && gcc foo.c && ./a.out
pagesize: 4096
data: 0x7f5f3b679000
65
0
0
48
56
10
mmap(2)提到以下內容。
使用映射區域可能會產生以下信號:
SIGSEGV嘗試寫入映射為只讀的區域。
SIGBUS嘗試訪問與文件不對應的緩沖區的一部分(例如,超出文件末尾,包括另一個進程截斷文件的情況)。
因此,如果我理解正確,即使第二個測試用例(1字節文件)應該導致SIGBUS,因為data[1]
和data[2]
試圖訪問不對應的緩沖區( data
)的一部分文件。
你能幫助我理解為什么只有一個零字節文件導致這個程序失敗並使用SIGBUS嗎?
當訪問過去整個映射頁面的末尾時,您會收到SIGBUS
,因為POSIX標准指出 :
mmap()
函數可用於映射大於對象當前大小的內存區域。 映射中的內存訪問但超出底層對象的當前末尾可能導致SIGBUS
信號被發送到進程。
使用零字節文件,您映射的整個頁面“超出了底層對象的當前末尾”。 所以你得到了SIGBUS
。
當你超越你繪制的4kB頁面時,你不會得到一個SIGBUS
,因為那不在你的映射中。 當文件大於零字節時,您不會獲得SIGBUS
訪問映射,因為整個頁面都已映射。
但是,如果您將其他頁面映射到文件末尾,則會得到一個SIGBUS
,例如為一個1字節的文件映射兩個 4kB頁面。 如果您訪問第二個4kB頁面,您將獲得SIGBUS
。
1字節文件不會導致崩潰,因為mmap
將以頁面大小的倍數映射內存,並將剩余部分歸零。 從手冊頁:
文件以頁面大小的倍數映射。 對於不是頁面大小倍數的文件,剩余內存在映射時歸零,並且寫入該區域不會寫入文件。 未指定更改對應於文件的添加或移除區域的頁面上的映射的基礎文件的大小的效果。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.