图解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 图

graph TD A[User Application] -->|malloc| B[glibc] B -->|small allocation| C[brk/sbrk] B -->|large allocation| D[mmap] C -->|System Call| E[Kernel: Adjust Heap] D -->|System Call| F[Kernel: Map Memory Pages] E --> G[Allocate Physical Memory] F --> G[Allocate Physical Memory]

内存分配的 Mermaid 图示

graph TD A[Heap Start] -->|Expand by brk/sbrk| B[Heap End] C[Anonymous Memory Region] -->|mmap| D[Virtual Address Space] E[Kernel Memory Manager] -->|Page Allocation| F[Physical Memory]

解释:

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

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

graph TD H[Kernel Space] --> G[Stack] G --> F[Unmapped Memory] F --> E[Heap End] E -->|brk/sbrk| D[Heap Start] D --> C[Uninitialized Data Segment] C --> B[Initialized Data Segment] B --> A[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 分配的内存区域相互干扰,提高内存管理的灵活性和效率。
graph TD H[Kernel Space] --> G[Stack End] G[Stack End] --> F[Stack Start] F[Stack Start] --> E[Unmapped Memory] E --> I[Anonymous Memory Region] I --> J[Unmapped Memory] J --> D[Heap End] D --> C[Heap Start] C --> B[Uninitialized Data Segment] B --> A[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 @ 2024-12-25 14:41  ffl  阅读(102)  评论(0)    收藏  举报