图解Linux下C内存分配 by using gpt

我们可以通过GPT来详细地图解Linux上的C内存分配。这个过程可以进一步细化,只要你愿意。

最小的 C 代码示例

以下代码使用了标准 C 库函数 malloc 分配一块内存:

#include <stdlib.h>
#include <stdio.h>

int main() {
    int *ptr = (int *)malloc(sizeof(int) * 10); // 分配10个整数大小的内存
    if (ptr == NULL) {
        perror("Memory allocation failed");
        return 1;
    }
    free(ptr); // 释放内存
    return 0;
}

系统调用的分叉情况

brk 和 sbrk 系统调用接口

brksbrk 是用于调整数据段(heap)大小的系统调用。以下是它们的接口说明:

brk

#include <unistd.h>

int brk(void *end_data_segment);
  • end_data_segment: 指向新的数据段末尾的指针。
  • 返回值: 成功时返回 0,失败时返回 -1 并设置 errno。

sbrk

#include <unistd.h>

void *sbrk(intptr_t increment);
  • increment: 增加或减少数据段大小的字节数。
  • 返回值: 成功时返回之前的程序 break 地址,失败时返回 (void *) -1 并设置 errno。

直接从 C 代码调用 brk 和 sbrk 的示例

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

int main() {
    void *initial_brk = sbrk(0); // 获取当前的程序 break 地址
    printf("Initial program break: %p\n", initial_brk);

    // 增加数据段大小
    if (sbrk(1024) == (void *) -1) {
        perror("sbrk failed");
        return 1;
    }

    void *new_brk = sbrk(0); // 获取新的程序 break 地址
    printf("New program break: %p\n", new_brk);

    // 恢复原来的数据段大小
    if (brk(initial_brk) == -1) {
        perror("brk failed");
        return 1;
    }

    printf("Program break reset to: %p\n", sbrk(0));
    return 0;
}

mmap 系统调用接口

mmap 是用于映射文件或设备到内存的系统调用。以下是它的接口说明:

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

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • addr: 映射的起始地址,通常为 NULL 以让内核选择地址。
  • length: 映射的字节数。
  • prot: 内存保护标志,如 PROT_READPROT_WRITE 等。
  • flags: 映射选项,如 MAP_PRIVATEMAP_ANONYMOUS 等。
  • fd: 文件描述符,若使用匿名映射则为 -1。
  • offset: 文件映射的偏移量,通常为 0。
  • 返回值: 成功时返回映射区域的指针,失败时返回 MAP_FAILED 并设置 errno。

直接从 C 代码调用 mmap 的示例

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

int main() {
    size_t length = 4096; // 映射 4KB 内存
    void *addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED) {
        perror("mmap failed");
        return 1;
    }

    printf("Memory mapped at address %p\n", addr);

    // 使用映射的内存
    int *array = (int *)addr;
    for (int i = 0; i < length / sizeof(int); i++) {
        array[i] = i;
    }

    // 取消映射
    if (munmap(addr, length) == -1) {
        perror("munmap failed");
        return 1;
    }

    return 0;
}

综合,malloc内部的分叉逻辑:

  1. 小内存块(例如几 KB):通过 brk 调整堆的大小。
  2. 大内存块(通常超过一定阈值,如 128 KB):通过 mmap 分配匿名内存区域。

用户态到内核态流程的 Mermaid 图

malloc

small allocation

large allocation

System Call

System Call

User Application

glibc

brk/sbrk

mmap

Kernel: Adjust Heap

Kernel: Map Memory Pages

Allocate Physical Memory

内存分配的 Mermaid 图示

Expand by brk/sbrk

mmap

Page Allocation

Heap Start

Heap End

Anonymous Memory Region

Virtual Address Space

Kernel Memory Manager

Physical Memory

解释:

  1. Heap Start 和 Heap End 表示由 brk/sbrk 管理的堆区域。
  2. Anonymous Memory Region 是通过 mmap 分配的虚拟内存。
  3. Kernel Memory Manager 负责将虚拟地址映射到物理内存页。

最小程序加载后的内存分布示意图(演示brk/sbrk直接在Heap区域分配小内存)

brk/sbrk

Kernel Space

Stack

Unmapped Memory

Heap End

Heap Start

Uninitialized Data Segment

Initialized Data Segment

Program Static Code Segment

解释:

  1. Kernel Space:内核空间。
  2. Stack:存储函数调用的栈帧和局部变量。
  3. Unmapped Memory:未映射的内存区域。
  4. Heap Start 和 Heap End:由 brk/sbrk 管理的堆区域。
  5. Uninitialized Data Segment:存储未初始化的全局和静态变量(BSS段)。
  6. Initialized Data Segment:存储已初始化的全局和静态变量。
  7. Program Static Code Segment:存储程序的代码。

mmap 分配匿名内存区域的示意图

  1. Anonymous Memory Region 是通过 mmap 分配的虚拟内存。
  2. mmap 分配的内存区域不在 HEAP 区域内,因为 mmap 直接在虚拟地址空间中分配内存,而不依赖于堆的增长。堆的增长是通过 brk/sbrk 系统调用来管理的,而 mmap 则用于分配较大的内存块或特定用途的内存区域,如共享内存或文件映射。这样可以避免堆和 mmap 分配的内存区域相互干扰,提高内存管理的灵活性和效率。

Kernel Space

Stack End

Stack Start

Unmapped Memory

Anonymous Memory Region

Unmapped Memory

Heap End

Heap Start

Uninitialized Data Segment

Program Static Code Segment

解释:

  1. Stack Start 和 Stack End:表示栈的起始和结束位置。
  2. Anonymous Memory Region 是通过 mmap 分配的虚拟内存,位于栈和堆之间的未映射内存区域中。
  3. Heap Start 和 Heap End:由 brk/sbrk 管理的堆区域。
  4. Uninitialized Data Segment:存储未初始化的全局和静态变量(BSS段)。
  5. Initialized Data Segment:存储已初始化的全局和静态变量。
  6. Program Static Code Segment:存储程序的代码。
posted @   ffl  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示