当前位置: 首页 > article >正文

malloc底层原理 brk,sbrk,mmap

brk() 系统调用

brk()Linux 系统调用,用于 调整进程的堆(heap)大小。它用于管理进程的 数据段(Data Segment),从而影响 malloc()free() 的底层实现


1. brk() 的作用

  • 增加或减少堆的大小
  • 用于 sbrk(),它是 malloc() 申请堆内存的底层机制
  • 影响 malloc()free(),但不适用于 mmap() 分配的内存

示例

#include <unistd.h>
#include <stdio.h>

int main() {
    void *heap1 = sbrk(0); // 获取当前堆顶
    printf("Heap start: %p\n", heap1);

    brk(heap1 + 4096); // 增加 4KB
    void *heap2 = sbrk(0);
    printf("Heap after brk(): %p\n", heap2);

    return 0;
}

输出

Heap start: 0x55aebc756000
Heap after brk(): 0x55aebc757000

brk() 增加了 4KB 的堆空间。


2. sbrk()brk() 的关系

  • sbrk() 调用 brk(),以相对方式调整堆
  • brk(new_end) 直接设置堆顶,而 sbrk(size) 相对移动堆顶。

示例

#include <unistd.h>
#include <stdio.h>

int main() {
    void *heap1 = sbrk(0);
    printf("Heap start: %p\n", heap1);

    sbrk(4096); // 增加 4KB
    void *heap2 = sbrk(0);
    printf("Heap after sbrk(4096): %p\n", heap2);

    sbrk(-4096); // 归还 4KB
    void *heap3 = sbrk(0);
    printf("Heap after sbrk(-4096): %p\n", heap3);

    return 0;
}

输出

Heap start: 0x5634b2712000
Heap after sbrk(4096): 0x5634b2713000
Heap after sbrk(-4096): 0x5634b2712000

sbrk(4096) 增加了 4KB,sbrk(-4096) 归还 4KB。


3. brk()malloc() 中的作用

malloc() 可能使用 brk() 来分配内存:

  1. 检查 free list(空闲块链表),如果没有足够的空闲块:
  2. 调用 sbrk(size)brk(new_end) 增加堆空间:
    void* malloc(size_t size) {
        if (size == 0) return NULL;
        void* new_heap = sbrk(size);
        if (new_heap == (void*)-1) return NULL; // 分配失败
        return new_heap;
    }
    

4. brk() vs mmap()

方法适用场景分配方式是否可释放
brk() / sbrk()小块分配(≤128KB)调整堆大小❌ 只能扩展,不能归还
mmap()大块分配(>128KB)匿名映射内存munmap() 释放

示例

void* bigAlloc = mmap(NULL, 1024 * 1024, PROT_READ | PROT_WRITE,
                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  • mmap() 适用于大块内存分配,而 brk() 适用于 小块动态分配

5. 为什么 free() 不归还内存?

  • free() 将内存块加入 free list,但不会调用 brk(-size)
  • 只有当释放的内存块正好在堆顶free() 可能调用 brk() 归还内存。

示例

void* p1 = malloc(1000);
void* p2 = malloc(1000);
free(p2);  // 可能归还
free(p1);  // 不能归还,因 p2 仍在堆中

6. 总结

  • brk() 直接设置堆顶,sbrk() 以相对方式移动堆顶。
  • malloc() 可能使用 brk() 扩展堆,但释放时不会立刻归还。
  • mmap() 适用于大块分配,而 brk() 适用于小块管理。

mmap() 系统调用

mmap() 是 Linux 提供的一种 内存映射 机制,它可以 直接将文件或匿名内存映射到进程地址空间,用于 高效的大块内存分配


1. mmap() 的作用

  • 文件映射:将 文件 映射到进程的虚拟地址空间,可用于 文件 I/O 加速
  • 匿名映射:分配一块 无需关联文件 的内存,类似 malloc(),但 支持 munmap() 释放
  • 共享内存:多个进程可使用 mmap() 共享一块内存,实现 进程间通信(IPC)
  • 大块内存分配malloc() 可能使用 mmap()大于 128KB 的分配 申请内存。

2. mmap() 的函数原型

#include <sys/mman.h>
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
参数作用
addr建议映射的地址(通常填 NULL,由系统分配)
length映射的大小(字节数)
prot保护权限,如 PROT_READ(可读)、PROT_WRITE(可写)
flags控制标志,如 MAP_PRIVATE(私有映射)、MAP_SHARED(共享映射)
fd文件描述符(-1 表示匿名映射)
offset文件偏移量(fd=-1 时填 0

返回值

  • 成功:返回 映射的起始地址
  • 失败:返回 MAP_FAILED(void*)-1

3. mmap() 用于匿名内存分配

mmap() 可用于代替 malloc() 申请内存,并且可以 释放,而 brk() 只能扩展堆,无法释放。

示例:使用 mmap() 申请 1MB 内存

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    size_t size = 1024 * 1024;  // 1MB
    void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    if (ptr == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }

    printf("Memory allocated at: %p\n", ptr);

    // 使用 mmap 分配的内存
    *(int*)ptr = 42;
    printf("Value at ptr: %d\n", *(int*)ptr);

    // 释放内存
    munmap(ptr, size);
    return 0;
}

输出

Memory allocated at: 0x7f9c3c12d000
Value at ptr: 42

mmap() 成功 分配 1MB 内存,并能 munmap() 释放


4. mmap() 用于文件映射

可以将 文件内容映射到内存,实现 高效文件 I/O,避免 read()/write() 系统调用开销

示例:映射文件到内存

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>

int main() {
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }

    struct stat st;
    fstat(fd, &st); // 获取文件大小
    size_t size = st.st_size;

    // 将文件映射到内存
    void* ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }

    printf("File content:\n%.*s\n", (int)size, (char*)ptr);

    munmap(ptr, size); // 释放映射
    close(fd);
    return 0;
}

✅ 读取 test.txt 直接映射到内存,避免 read() 系统调用,提高 I/O 性能


5. mmap() 的常用 flags 选项

标志作用
MAP_ANONYMOUS匿名映射(不与文件关联,fd=-1 时使用)
MAP_PRIVATE私有映射(修改不会影响原文件)
MAP_SHARED共享映射(多个进程共享)
MAP_FIXED固定映射地址(如果地址不可用,失败)

6. mmap() vs. malloc()

特性mmap()malloc()
适用场景大块内存(>128KB小块内存
底层实现mmap() 系统调用sbrk() 调整堆
释放方式munmap(ptr, size)free(ptr)
是否影响 brk()❌ 不影响堆✅ 影响 brk()
性能适合大对象,减少碎片适合小对象,管理灵活

示例

void* largeAlloc = mmap(NULL, 1024 * 1024, PROT_READ | PROT_WRITE,
                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

mmap() 适合 大块分配(如 1MB)。


7. mmap() vs. brk()

特性mmap()brk() (sbrk())
适用场景大块内存分配(如 1MB)小块内存管理(如 malloc()
是否可释放✅ 可 munmap() 释放❌ 只能扩展,不能收缩
线程安全✅ 线程安全⚠️ sbrk() 影响全局堆,不推荐多线程使用

大对象用 mmap(),小对象用 malloc()


8. mmap() 共享内存(IPC)

多个进程可以通过 mmap() 共享一块内存,实现 进程间通信(IPC)

示例

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main() {
    int fd = shm_open("/my_shared_memory", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, 1024); // 设置共享内存大小

    void* ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }

    strcpy(ptr, "Hello from mmap!");
    printf("Shared Memory Written: %s\n", (char*)ptr);

    munmap(ptr, 1024);
    close(fd);
    shm_unlink("/my_shared_memory"); // 删除共享内存
    return 0;
}

✅ 进程 A 写入共享内存,进程 B 读取共享数据


9. 结论

  • mmap() 用于 高效文件 I/O、匿名内存、共享内存
  • mmap() 适合大块内存,而 malloc() 适合小对象。
  • mmap() 可以释放内存,而 brk() 不能收缩。
  • 高性能应用(数据库、服务器)常用 mmap() 代替 malloc()

http://www.kler.cn/a/541625.html

相关文章:

  • java.io.InvalidClassException
  • QT实现多线程的方法
  • Mp4视频播放机无法播放视频-批量修改视频分辨率(帧宽、帧高)
  • 怎么查看电脑显存大小(查看电脑配置)
  • 手写一个C++ Android Binder服务及源码分析
  • 【AI-27】DPO和PPO的区别
  • Spark 源码 | 脚本分析总结
  • 【Qt之·类QTextCursor】
  • 深入浅出:图解Vue 3生命周期的全流程
  • 红外皮秒激光器:开启超快激光技术新时代
  • 算法03-基数排序
  • 【AI知识点】苦涩的教训 The Bitter Lesson by Rich Sutton(2019)
  • Vite打包路径base配置项设置
  • JVM ①-类加载 || 内存区域
  • Redis 数据类型 List 列表
  • 【Python深入浅出】Python3正则表达式:开启高效字符串处理大门
  • 【AI学习】DeepSeek为什么强?
  • windows生成SSL的PFX格式证书
  • arcgis界址点编号工具开发原理(西北角顺时针)
  • 生成式语言模型技术全解析
  • 笔记:蓝桥杯python搜索(3-2)——DFS剪支和记忆化搜索
  • 车载测试工具 --- CANoe VH6501 进行Not Acknowledge (NAck) 测试
  • HTTP 请求头、响应头常见字段分析
  • Lisp语言的Web开发
  • Docker 1. 基础使用
  • MYSQL判断函数