如何用NASM汇编实现自定义malloc?对brk与mmap存疑
实现x64 Linux下的自定义malloc(NASM汇编)
核心原理澄清
Linux下动态内存分配依赖两个核心系统调用:
brk/sbrk:调整进程堆空间的末端地址(program break),堆是低地址向高地址增长的连续区域mmap:不仅能关联文件,更常用的是匿名映射(通过MAP_ANONYMOUS标志),用于分配大块内存,避免堆碎片化——这是你之前对mmap的误解点
初始堆指针的获取
不存在“一开始没有可用指针”的问题:调用brk(0)就能获取当前堆的末端地址(program break),这就是初始堆指针。后续扩展堆时,只需调用brk(new_break)将末端地址设置到更高位置,中间的空间即可用于分配。
分步实现代码
1. x64 Linux系统调用号
系统调用前需将对应编号存入rax寄存器:
- brk:
12 - mmap:
9
2. 基础brk版malloc(无free)
先实现一个仅支持内存分配的简单版本,处理小内存请求:
; 全局变量:保存当前堆末端地址 section .bss current_break resq 1 ; x64为8字节,用resq定义 section .text global my_malloc my_malloc: push rbp mov rbp, rsp ; 将请求大小对齐到8字节(x64内存对齐要求) add rdi, 7 and rdi, ~7 ; 初始化current_break(首次调用时) cmp qword [current_break], 0 jne .heap_init_done ; 调用brk(0)获取初始堆末端地址 mov rax, 12 mov rdi, 0 syscall mov qword [current_break], rax .heap_init_done: ; 计算新堆末端地址:current_break + 分配大小 mov rsi, rax ; 保存原堆末端(即本次分配的起始地址) add rax, rdi ; new_break = current_break + size ; 调用brk扩展堆 mov rdi, rax mov rax, 12 syscall ; 检查扩展是否成功(返回值等于new_break则成功) cmp rax, rdi jne .alloc_fail ; 返回分配的内存起始地址 mov rax, rsi ; 更新全局堆末端地址 mov qword [current_break], rdi leave ret .alloc_fail: ; 分配失败返回0 mov rax, 0 leave ret
3. 加入mmap处理大块内存
当请求内存超过阈值(比如128KB,参考glibc的策略),改用mmap匿名映射更高效:
; 定义大块内存阈值:128KB section .data MMAP_THRESHOLD equ 131072 section .bss current_break resq 1 section .text global my_malloc my_malloc: push rbp mov rbp, rsp ; 对齐请求大小到8字节 add rdi, 7 and rdi, ~7 ; 判断是否使用mmap分配大块内存 cmp rdi, MMAP_THRESHOLD jge .use_mmap ; brk分配分支(同基础版逻辑) cmp qword [current_break], 0 jne .heap_init_done mov rax, 12 mov rdi, 0 syscall mov qword [current_break], rax .heap_init_done: mov rsi, rax add rax, rdi mov rdi, rax mov rax, 12 syscall cmp rax, rdi jne .alloc_fail mov rax, rsi mov qword [current_break], rdi leave ret .use_mmap: ; mmap匿名映射参数: ; rdi=0(让内核选择映射地址) ; rsi=分配大小 ; rdx=PROT_READ|PROT_WRITE (0x3) ; r10=MAP_PRIVATE|MAP_ANONYMOUS (0x22) ; r8=-1(匿名映射无需文件描述符) ; r9=0(文件偏移量,无效) mov rax, 9 mov rsi, rdi mov rdi, 0 mov rdx, 0x3 mov r10, 0x22 mov r8, -1 mov r9, 0 syscall ; 检查是否失败(返回值为-1则失败) cmp rax, -1 je .alloc_fail ; 成功则返回映射地址 leave ret .alloc_fail: mov rax, 0 leave ret
关键注意事项
- 内存对齐:x64环境下所有指针和数据结构必须对齐到8字节,否则会引发性能问题或错误
- 系统调用校验:brk成功返回新的program break,失败返回-1;mmap成功返回映射地址,失败返回
MAP_FAILED(即-1) - 后续扩展:若要实现
free,需在每个分配块的头部记录块大小和类型(brk分配/mmap分配),这样free时才能判断是调用brk收缩堆还是munmap释放映射
内容的提问来源于stack exchange,提问作者R-Rothrock
相关产品推荐
相关产品推荐

