内存五区到底是什么?

本文介绍了计算机内存的五个区域,包括栈、堆、自由存储区、全局/静态存储区和常量存储区,并通过实例解释了它们的工作原理。栈用于存储局部变量和函数参数,堆由new分配,需要手动释放,自由存储区使用malloc等分配,全局/静态存储区存放全局变量和静态变量。此外,还讨论了指针的概念,强调指针和它所指向的内存空间是不同的。文章旨在帮助开发者深入理解计算机内存管理,促进技能提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

电场、磁场都是实际存在的物质,这些物质我们看不见摸不着,当它们出现时,我们都认为理所当然,而不会对其背后的理论与原理进行深究,我们追求的是实用性和帮我们解决问题。计算机也是如此,当汇编语言和图形界面出现之前,操作和使用计算机是少部分专业人的专利,与我们芸芸众生相隔甚远。而当计算机走入寻常生活时,哪怕我们对计算机一无所知,也不妨碍我们通过图形界面使用应用软件和操作计算机,我们只关心具体的术、而不在乎形而向上的道。

但是对于一个开发者来说,我们只有在充分理解计算机背后的”道“基础上,才能做到对具体术的游刃有余。而这个修道的过程,恰恰是我们成长的过程。

计算机中有很多概念,计算机的学习实际是对概念的学习。看书阅读是最基本的方式,但是最重要的还是需要自己动手练习和做项目,对于成年人来说,没有什么比任务式学习更有效。下面对内存五区进行简单说明,希望以后能够和大家互相讨论,共同进步,毕竟终生学习不是口号,而是已经走近现实。
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆非常相似,不过他们是通过free的方式释放内存。

全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

一个典型程序的执行过程如下:
在这里插入图片描述
上图来自网络

一个函数的执行过程如下:

<think>我们正在讨论C语言中的内存映射(mmap)原理。根据提供的引用信息,我将详细解释mmap的工作原理、函数使用步骤以及相关注意事项。 ### 1. mmap的基本原理 mmap(memory mapping)是一种将文件或其他对象映射到进程地址空间的方法。通过这种方式,应用程序可以直接通过内存指针访问文件数据,而无需使用read/write等系统调用。其核心优势在于: - **减少数据拷贝**:传统文件I/O需要在内核缓冲和用户空间之间拷贝数据,而mmap直接建立映射,避免了额外拷贝[^1]。 - **高效随机访问**:映射后可以像操作数组一样随机访问文件内容。 - **共享内存**:多个进程可映射同一文件,实现共享内存通信。 ### 2. mmap函数详解 #### 函数原型 ```c #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); ``` - **参数说明**: - `addr`:建议映射起始地址(通常设为`NULL`由系统自动选择)。 - `length`:映射域的长度(必须是页大小的整数倍)[^1]。 - `prot`:保护模式(如`PROT_READ | PROT_WRITE`)。 - `flags`:映射类型(常用`MAP_SHARED`或`MAP_PRIVATE`)。 - `fd`:文件描述符(需先打开文件)。 - `offset`:文件偏移量(通常为0,表示从文件头部开始映射)。 #### 关键步骤(结合引用[2]) 1. **打开文件**:使用`open()`获取文件描述符`fd`。 2. **获取/设置文件大小**:通过`ftruncate()`调整文件大小至至少`length`字节。 3. **执行映射**:调用`mmap()`建立映射关系,返回映射内存的起始地址`ptr`。 4. **访问数据**:直接通过`ptr`指针读写内存(如`memcpy(ptr, data, len)`)。 5. **同步数据**:使用`msync()`确保修改写回文件。 6. **解除映射**:通过`munmap(ptr, length)`释放资源。 7. **关闭文件**:`close(fd)`(可在映射后立即关闭,因为映射独立存在)。 ### 3. 内存映射的底层机制 - **页式管理**:Linux采用页式内存管理,映射以内存页(通常4KB)为单位[^3]。 - **按需加载**:初始映射时并不立即加载全部文件内容,当访问未加载的页面时触发缺页中断,由内核自动从磁盘读取。 - **有效访问范围**:进程能访问的有效地址范围取决于文件被映射部分的大小(实际可访问域是容纳该部分的最小页面集合)[^3]。 ### 4. 注意事项 - **映射大小限制**:`length`必须是系统页大小的整数倍(可通过`sysconf(_SC_PAGE_SIZE)`获取页大小)。 - **设备限制**:面向流的设备(如终端、打印机)无法使用mmap[^1]。 - **同步机制**:修改后的数据不会立即写回磁盘,需调用`msync()`或依赖内核定期回写(`MAP_SHARED`模式下)。 ### 5. 示例代码片段 ```c #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> int main() { int fd = open("test.txt", O_RDWR | O_CREAT, 0666); // 步骤1:打开文件 ftruncate(fd, 4096); // 步骤2:调整文件大小 // 步骤3:映射文件(可读可写,共享映射) char *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); // 映射后可关闭文件描述符 // 步骤4:操作内存 sprintf(ptr, "Hello, mmap!"); msync(ptr, 4096, MS_SYNC); // 步骤5:同步到文件 munmap(ptr, 4096); // 步骤6:解除映射 return 0; } ``` ### 6. 性能与应用场景 - **高性能场景**:适用于需要高频读写大文件的场景(如数据库系统、大规模数据处理)。 - **进程间通信(IPC)**:多个进程映射同一文件可实现高效数据共享。 - **敏感操作**:修改内存即修改文件,需谨慎处理同步问题。 §§相关问题§§ 1. mmap和常规文件I/O(如read/write)在性能上有哪些具体差异? 2. 使用mmap时如何处理不同进程间的同步问题? 3. mmap能否用于匿名映射(不关联文件)?如何实现?</think>### C语言内存映射(mmap)工作原理详解 #### 一、核心原理 内存映射(mmap)是一种**将文件或设备直接映射到进程地址空间**的技术,其工作原理基于以下关键机制: 1. **虚拟内存映射** - 当调用`mmap()`时,内核在进程的虚拟地址空间中创建映射域 - 该域与物理内存页建立关联,但初始时**不加载实际数据**(延迟加载) 2. **页表管理** - Linux采用页式管理机制(通常4KB页大小) - 映射域大小必须是**页面大小的整数倍**(如4KB/8KB等)[^1] 3. **按需加载** - 当进程首次访问映射域时,触发缺页中断(page fault) - 内核将文件对应内容加载到物理内存 4. **零拷贝优化** - 直接通过内存指针访问文件数据,**避免内核与用户空间的多次数据拷贝**[^1] #### 二、mmap函数详解 ```c #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); ``` **参数解析:** | 参数 | 说明 | |----------|----------------------------------------------------------------------| | `addr` | 建议映射地址(通常设为`NULL`由内核自动选择) | | `length` | 映射域长度(必须是页大小的整数倍)[^1] | | `prot` | 保护模式:`PROT_READ`/`PROT_WRITE`/`PROT_EXEC`的组合 | | `flags` | 映射类型:`MAP_SHARED`(修改同步到文件)或`MAP_PRIVATE`(写时复制) | | `fd` | 文件描述符(需先通过`open()`获取) | | `offset` | 文件偏移量(通常设为0) | **返回值:** 成功时返回映射域起始地址,失败返回`MAP_FAILED`((void*)-1) #### 三、工作流程(结合引用[2]) 1. **打开文件** ```c int fd = open("data.bin", O_RDWR); ``` 2. **调整文件大小**(确保足够容纳映射) ```c ftruncate(fd, 4096); // 扩展到4KB ``` 3. **执行内存映射** ```c char *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ``` 4. **访问映射内存** ```c memcpy(ptr, "Hello", 5); // 直接操作内存即修改文件 ``` 5. **数据同步**(可选) ```c msync(ptr, 4096, MS_SYNC); // 强制写入磁盘 ``` 6. **解除映射** ```c munmap(ptr, 4096); close(fd); ``` #### 四、关键特性与限制 1. **高效性** - 相比`read/write`减少**2次数据拷贝**(内核缓冲↔用户空间)[^1] 2. **访问范围限制** - 进程能访问的有效数据范围 = `min(文件大小, 映射长度)`[^3] - 超出文件实际大小的访问会触发`SIGBUS`信号 3. **设备限制** - 面向流的设备(终端/打印机)**不支持**mmap[^1] 4. **同步机制** - `MAP_SHARED`模式下,修改会**异步回写**到文件 - 重要数据需用`msync()`强制同步 #### 、典型应用场景 1. **大文件处理** - 无需加载整个文件即可随机访问(如数据库系统) 2. **进程间通信(IPC)** - 多个进程映射同一文件实现共享内存 3. **零拷贝网络传输** - 配合`sendfile()`实现高效文件传输 4. **内存分配器** - `malloc()`底层可能使用mmap分配大内存块 > **注意**:映射后关闭文件描述符不影响已建立的映射(但需确保文件在解除映射前不被删除)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值