официальная спецификация mmap не очень ясна в этом вопросе, но, пожалуйста, обратите внимание на фразу:
Ссылки на [память][,] в пределах диапазона адресов, начинающихся с pa
и продолжающихся len
байтов[,] до целых страниц после конца объекта, должны приводить к доставке сигнала SIGBUS
.
Я расставил несколько запятых и добавил акцент, чтобы прояснить мысль: если вы mmap
имеете область большего размера, чем файл, который ее поддерживает, ОС должна подать сигнал, если вы попытаетесь получить доступ за пределы конец файла, при условии, что вы переходите границу страницы (эта лицензия просто потому, что аппаратное обеспечение не позволяет ОС устанавливать границу между доступной и недоступной памятью, которая не попадает на границу страницы) (SIGBUS
и SIGSEGV
в настоящее время считаются взаимозаменяемыми многими операционными системами).
Вы были на правильном пути со своим подходом «два последовательных вызова», но он не сработал, потому что вы использовали одинаковую длину для обоих вызовов. Если бы вместо этого вы указали размер файла во втором вызове, это сработало бы. Вы можете получить фактический размер файла с помощью системного вызова fstat
. Вам нужно проявить некоторую осторожность, если файл больше, чем желаемая длина выделения. Вот как бы я это написал:
char *map_file(int fd, size_t len)
{
struct stat st;
char *rv;
if (fstat(fd, &st))
return report_error("fstat");
if (st.st_size >= (off_t) len) {
/* If the file is at least as big as expected, just map the
chunk we want. */
rv = mmap(0, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if (rv == MAP_FAILED)
return report_error("mmap");
} else {
/* Otherwise, we must allocate anonymous memory to fill in the gap. */
char *anon;
anon = mmap(0, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
if (anon == MAP_FAILED)
return report_error("mmap (anon)");
rv = mmap(anon, (size_t) st.st_size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED, fd, 0);
if (rv == MAP_FAILED) {
int save_errno = errno;
(void) munmap(anon, len);
errno = save_errno;
return report_error("mmap");
}
}
return rv;
}
В зависимости от вашей более крупной цели вместо этого может иметь смысл увеличить файл, если он не такой большой, как ожидалось, используя ftruncate
:
char *map_file(int fd, size_t len)
{
struct stat st;
char *rv;
if (fstat(fd, &st))
return report_error("fstat");
/* if the file isn't as big as expected, make it bigger */
if (st.st_size < (off_t) len)
if (ftruncate(fd, (off_t) len))
return report_error("ftruncate");
rv = mmap(0, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if (rv == MAP_FAILED)
return report_error("mmap");
return rv;
}
Но, вероятно, если бы это было то, что вам нужно в контексте, вы бы использовали MAP_SHARED
вместо MAP_PRIVATE
.
(Примечание. Не показанная функция report_error
регистрирует сообщение об ошибке, а затем возвращает NULL.)
person
zwol
schedule
18.02.2014