linux - 为什么 mremap 只访问 4096 字节内存?
问题描述
下面的代码开始 mmap(initlen=10), 后来 remap(nsize=400000) 和访问 remap 内存地址访问 0x7ffff7f8c000 到 0x7ffff7f8cfff 是可以的,但是在 0x7ffff7f8d000 导致访问错误
#define _GNU_SOURCE
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
int main()
{
int i;
char *p;
void *base;
const int initlen = 10;
const int nsize = 400000;
const char *fname = "/tmp/task.0";
int fd = open(fname, O_CREAT|O_RDWR, 0600);
if (fd == -1) {
return 1;
}
if (ftruncate(fd, initlen) < 0) {
return 1;
}
base = mmap(NULL, initlen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) {
return 1;
}
// now remap big page.
base = mremap(base, initlen, nsize, MREMAP_MAYMOVE);
if (base == MAP_FAILED) {
printf("mremap fail, %s\n", strerror(errno));
return 1;
}
p = base;
for (i = 0; i < nsize; i++) {
printf("%p\n", p);
*p = i % CHAR_MAX;
++p;
}
return 0;
}
gdb显示“/tmp/task.0”区域为0x7ffff7f8c000-0x7ffff7fee000后的mremap,在0x7ffff7f8d000处访问出错,为什么???
37 base = mremap(base, initlen, nsize, MREMAP_MAYMOVE);
(gdb)
38 if (base == MAP_FAILED) {
(gdb)
43 p = base;
(gdb)
44 for (i = 0; i < nsize; i++) {
(gdb) p base
$1 = (void *) 0x7ffff7f8c000
(gdb) i proc mappings
process 3333
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 /tmp/a.out
0x600000 0x601000 0x1000 0x0 /tmp/a.out
0x601000 0x602000 0x1000 0x1000 /tmp/a.out
0x7ffff7a0e000 0x7ffff7bd1000 0x1c3000 0x0 /usr/lib64/libc-2.17.so
0x7ffff7bd1000 0x7ffff7dd0000 0x1ff000 0x1c3000 /usr/lib64/libc-2.17.so
0x7ffff7dd0000 0x7ffff7dd4000 0x4000 0x1c2000 /usr/lib64/libc-2.17.so
0x7ffff7dd4000 0x7ffff7dd6000 0x2000 0x1c6000 /usr/lib64/libc-2.17.so
0x7ffff7dd6000 0x7ffff7ddb000 0x5000 0x0
0x7ffff7ddb000 0x7ffff7dfd000 0x22000 0x0 /usr/lib64/ld-2.17.so
0x7ffff7f8c000 0x7ffff7fee000 0x62000 0x0 /tmp/task.0
0x7ffff7fee000 0x7ffff7ff1000 0x3000 0x0
0x7ffff7ff9000 0x7ffff7ffa000 0x1000 0x0
0x7ffff7ffa000 0x7ffff7ffc000 0x2000 0x0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x21000 /usr/lib64/ld-2.17.so
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x22000 /usr/lib64/ld-2.17.so
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
Program received signal SIGBUS, Bus error.
0x0000000000400831 in main () at ./test.c:46
46 *p = i % CHAR_MAX;
(gdb) p p
$2 = 0x7ffff7f8d000 <Address 0x7ffff7f8d000 out of bounds>
解决方案
它与mremap
. 这就是文件映射的工作方式。SIGBUS 意味着您试图访问超过其 EOF 的文件区域(请参阅手册页 mmap(3))。
SIGBUS 试图访问与文件不对应的缓冲区部分(例如,超出文件末尾,包括另一个进程截断了文件的情况)。
这与 SIGSEGV 不同,SIGSEGV 在您尝试访问进程中不存在的虚拟地址或发生保护错误时发送(例如,尝试写入只读地址)。
要回答为什么您可以在不获取 SIGBUS 的情况下访问前 0xfff 字节的问题(即使文件大小只有 10 个字节),这是因为内存管理是根据 4096 字节页面进行的。但是请注意,即使您可以访问字节 10..4095,该区域也不受文件支持。您写入这些字节的所有内容都不会写入文件。
推荐阅读
- django - 离子到 Django POST 数据自动名称更改
- solr - Solr 的 Lucene 查询中的禁用字符列表
- r - R中列的条件计算
- hyperledger-fabric - 启动这个超级账本结构应用程序 - 业务问题
- python - Pandas - 绘制两个数据框,一个作为标记
- r - 您可以在 rmarkdown 或 bookdown 中将 natbib 的引用样式更改为数字吗?
- java - Android Studio:应用程序未保存到数据库/从数据库读取
- angularjs - 使用 AngularJS 动态加载 Google Maps API
- google-sheets - 表格:数组结果未展开
- javascript - concat1D 中的错误:张量 [23] 的等级必须与其余的等级相同