Почему операция чтения файла нулевого байта, отображенного в памяти, приводит к 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?
2 ответа
Ты получаешь SIGBUS
при доступе к концу последней всей отображаемой страницы, потому что стандарт POSIX гласит:
mmap()
Функция может быть использована для отображения области памяти, которая больше, чем текущий размер объекта. Доступ к памяти в отображении, но за пределами текущего конца базовых объектов может привести кSIGBUS
сигналы отправляются в процесс.
В случае файла с нулевым байтом вся отображаемая страница находится "за пределами текущего конца базового объекта". Итак, вы получаете SIGBUS
,
Вы НЕ получаете SIGBUS
когда вы выходите за пределы 4-килобайтной страницы, которую вы отобразили, потому что это не входит в ваше отображение. Вы не получаете SIGBUS
доступ к вашему отображению, когда ваш файл больше нуля байтов, потому что отображается вся страница.
Но вы бы получили SIGBUS
если вы сопоставили дополнительные страницы после конца файла, например, сопоставили две страницы по 4 КБ для 1-байтового файла. Если вы получите доступ ко второй странице 4 КБ, вы получите SIGBUS
,
1-байтовый файл не приводит к сбою, потому что mmap
отобразит память кратно размеру страницы и обнулит остаток. Со страницы руководства:
Файл отображается в кратных размеру страницы. Для файла, который не кратен размеру страницы, оставшаяся память обнуляется при отображении, и записи в эту область не записываются в файл. Эффект изменения размера основного файла сопоставления на страницах, которые соответствуют добавленным или удаленным областям файла, не определен.