为何MIPS32汇编中调用mmap会返回ENODEV错误?
问题描述
我一直在尝试实现Linux下MIPS32汇编的内存管理,编写了一段用mmap分配8字节内存的代码:
.text .global main main: addi $sp, $sp, -32 add $a0, $zero, $zero addi $a1, $zero, 8 addi $a2, $zero, 1 addi $a3, $zero, 2 sw $zero, 16($sp) sw $zero, 20($sp) addi $v0, $zero, 4090 syscall lw $a0, ($v0) add $a0, $zero, $zero addi $v0, $zero, 4001 syscall
用gdb运行时,系统调用始终返回0x13,对应我的系统中的ENODEV错误。根据mmap手册页,这个错误会在指定文件的底层文件系统不支持内存映射时触发。我设置的参数如下:
- 第1个参数:
void addr[.length] = NULL = 0 - 第2个参数:
size_t length = 8 - 第3个参数:
int prot = PROT_READ = 1 - 第4个参数:
int flags = MAP_PRIVATE = 2 - 第5个参数(存在16($sp)处):
int fd = 0 - 第6个参数(存在20($sp)处):
off_t offset = 0
问题原因与解决方法
问题核心在于你用了文件描述符0(标准输入)做mmap,但标准输入对应的终端设备不支持内存映射,因此触发ENODEV错误。
如果只是要分配匿名内存(无需映射到文件),需修改两处:
- 在
flags参数中加入MAP_ANONYMOUS(值为0x20),即MAP_PRIVATE | MAP_ANONYMOUS,对应十进制数值34; - 将文件描述符
fd设为-1,而非0。
修改后的代码如下:
.text .global main main: addi $sp, $sp, -32 add $a0, $zero, $zero # addr = NULL addi $a1, $zero, 8 # length = 8 addi $a2, $zero, 1 # prot = PROT_READ addi $a3, $zero, 34 # flags = MAP_PRIVATE | MAP_ANONYMOUS (2 + 32) addi $t0, $zero, -1 sw $t0, 16($sp) # fd = -1 sw $zero, 20($sp) # offset = 0 addi $v0, $zero, 4090 # mmap系统调用号(MIPS32 Linux为4090) syscall # 检查返回值:若v0为MAP_FAILED(0xffffffff)则代表分配失败 li $t0, 0xffffffff beq $v0, $t0, exit # 此处可对分配的内存进行操作,例如写入数据 # sw $zero, ($v0) exit: add $a0, $zero, $zero # 退出状态码0 addi $v0, $zero, 4001 # exit系统调用号 syscall
额外说明:
- MIPS32 Linux系统调用的参数规则:前4个参数用
$a0-$a3传递,第5、6个参数需存入栈的$sp+16和$sp+20位置,你之前的栈位置处理是正确的; - 若确实需要映射文件,需确保打开的是普通磁盘文件(通过open系统调用获取合法fd),而非终端、管道这类不支持mmap的设备。
内容的提问来源于stack exchange,提问作者Kseniya Kolokolkina
相关产品推荐
相关产品推荐

