2)提供进程间共享内存及相互通信的方式。不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。
2、问题现象
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
内核申请物理地址,用户态获取并做mmap映射后虚拟地址指针全F,地址映射失败。
3、定位过程
已知:映射成功时,mmap() 返回被映射区的指针。映射失败时,mmap() 返回 MAP_FAILED((void *)-1)即全F,error 被设为以下的某个值:
返回值 含义
EACCES 访问出错
EAGAIN 文件已被锁定,或者太多的内存已被锁定
EBADF fd 不是有效的文件描述词
EINVAL 一个或者多个参数无效
ENFILE 已达到系统对打开文件的限制
ENODEV 指定文件所在的文件系统不支持内存映射
ENOMEM 内存不足,或者进程已超出最大内存映射数量
EPERM 权能不足,操作不允许
ETXTBSY 已写的方式打开文件,同时指定 MAP_DENYWRITE 标志
SIGSEGV 试着向只读区写入
SIGBUS 试着访问不属于进程的内存区
在mmap失败下面增加如下打印:
printf(“mmap error : %d-%s.”,errno,strerror(errno));
1
重新运行程序得到失败原因前后有两种:
#define EPERM 1 /* Operation not permitted */
mmap error :1-Operation not permitted.
#define EINVAL 22 /* Invalid argument */
mmap error :22-Invalid argument.
4、问题原因
内存设备节点 /dev/mem 作为可随机读写的字符设备,在内核源码的 drivers/char/mem.c 中初始化(/dev/kmem, /dev/null, /dev/zero, /dev/random, /dev/urandom 等设备节点也都在这个文件中定义),对 /dev/mem 的 mmap 操作定义为 mmap_mem()。在 mmap_mem() 中返回 -EPERM 的代码片段如下:
if (!range_is_allowed(vma->vm_pgoff, size))
return -EPERM;
1
2
当定义了 CONFIG_STRICT_DEVMEM 时,在 range_is_allowed() 中会逐页调用 devmem_is_allowed() 检查是否可访问:
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
u64 from = ((u64)pfn) << PAGE_SHIFT;
u64 to = from + size;
u64 cursor = from;
while (cursor < to) {
if (!devmem_is_allowed(pfn)) {
printk(KERN_INFO
"Program %s tried to access /dev/mem between %Lx->%Lx.\n",
current->comm, from, to);
return 0;
}
cursor += PAGE_SIZE;
pfn++;
}
return 1;
}
devmem_is_allowed() 返回 1 表示允许访问,0 表示不允许访问。
5、修改方法
1)关闭内核 CONFIG_STRICT_DEVMEM 配置选项即可。
从前面的说明中可以看出,几个检查函数都是在定义了 CONFIG_STRICT_DEVMEM 才生效的,因此最简单的方法,就是将内核配置中 CONFIG_STRICT_DEVMEM 选项关闭并重新编译内核,这也是我们的应用系统所采用的方法。
2)修改内核为CONFIG_ARM64_4K_PAGES 4K页大小
有的内核对CONFIG_ARCH_MMAP_RND_BITS 基地址位数有限制,此选项在arch/arm64/Kconfig里定义并和page页大小设置有关联,mmap在64K页下会失败。
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!