brk与mmap

2024-09-02 16:32:54 浏览数 (4)

1. 前言

glibc的malloc函数在申请大于128K的内存时使用mmap分配内存,mmap会从堆区和栈区中间的部分划分内存,而在申请小于128K的内存时使用brk从堆上划分内存。

2. brk/sbrk

brk是linux上一个系统调用,而sbrk是一个C库函数

2.1 brk函数原型

代码语言:javascript复制
int brk(void *addr);
参数

参数

解释

addr

要调整到的内存地址

返回值

返回增加的大小

2.2 sbrk函数原型

代码语言:javascript复制
void *sbrk(intptr_t increment);
参数

参数

解释

increment

增加的内存大小

返回值

返回增加之后的program break内存地址

2.3 brk的原理

brk实际是通过改变program break来实现的,这个program break可以理解为“堆顶指针”,意味着程序堆内存使用到了该位置。

而这种内存的分配方式有个问题,看下下图:

假设B已经被free了,但是由于上面有一个C对象,所以program break指针不能简单的向下移动来释放内存。那怎么办呢?

实际上free后不能立即归还内存,只是将这块内存标记为空闲,后续再申请内存时可以复用这块内存。并且两块连续的空闲内存可以合并为一块空闲内存,当最高地址空间的空闲内存大于128K时(可以通过M_TRIM_THRESHOLD选项调节),执行内存紧缩。对于上图来说,如果C也被free了,program break就可以指向A的结束地址了,就能释放一部分内存了。

难道这就是传说中的线性内存分配

3. mmap

3.1 mmap函数原型

代码语言:javascript复制
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
参数

参数

解释

addr

映射的起始地址,通常设置为NULL,由内核来分配

len

指定将文件映射到内存的部分的长度

prot

映射区域的保护方式,通常是下面几个选项的组合

flags

映射区的特性标志位,常用的有以下两个选项

fd

要映射到内存中的文件描述符

offset

文件映射的偏移量,必须是操作系统内存页大小的整数倍

返回值

返回映射区的起始地址

3.2 munmap函数原型

代码语言:javascript复制
// 解除映射
int munmap(void *start, size_t length);
参数

参数

解释

start

mmap返回的映射区的起始地址

length

映射区的大小

返回值

解除成功返回0,失败返回-1

3.3 mmap原理

linxu内核使用vm_area_struct结构来表示一个独立的虚拟内存区域(比如堆、栈、bss段、代码段等),一个进程会有多个vm_area_struct结构体,各个vm_area_struct使用树结构或者链表连接。

vm_area_struct结构包含了该区域的起止地址和其他信息以及一个vm_ops指针,vm_ops指针内部引出所有针对这个区域可用的系统调用函数。

mmap就是创建一个新vm_area_struct结构,并将其与文件磁盘地址做映射。

mmap申请的内存可以通过munmap释放。

4. 总结

方式

内存碎片

适用场景

brk

多,因为不可释放

申请小内存

mmap

无,因为可以直接释放

申请大内存,如果用来申请小内存的话就会创建非常多的vm_area_struct结构体,不划算

5. 参考资料

  • https://www.cnblogs.com/huxiao-tee/p/4660352.html
  • https://blog.csdn.net/yusiguyuan/article/details/39496057

0 人点赞