内存模型

代码区(只读代码和数据)
程序被操作系统加载到内存的时候,所有的可执行代码(程序代码指令、常量字符串等)都加载到代码区,这块内存在程序运行期间是不变的。代码区是平行的,里面装的就是一堆指令,在程序运行期间是不能改变的。函数也是代码的一部分,故函数都被放在代码区,包括main函数。
静态区(读写数据)
堆区
堆(heap)和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。更重要的是堆是一个大容器,它的容量要远远大于栈,可以解决栈内存溢出问题。堆上的内存必须手动释放,在C中有以下几种方式申请与释放内存:
- malloc
- free
- realloc
-
栈区
栈(stack)是一种先进后出的内存结构,所有的自动变量、函数形参都存储在栈中,这个动作由编译器自动完成,栈区有以下特点:
每个线程都有自己专属的栈
- 栈的最大尺寸固定,超出则引起栈溢出
- 变量离开作用域后栈上的内存会自动释放
```
include
// 函数的返回值是一个指针,尽管这样可以运行程序,但这样做是不合法的,因为 // 在x的地址是getddr函数栈中的地址,getaddr调用结束后此地址空间会被释放 int *getaddr() { int x = 10; return &x; }
int main() { int p = getaddr(); p = 20; printf(“%d”, *p); }
<a name="ZwDJv"></a>
### 共享库
共享代码与数据
<a name="8ZMxp"></a>
### 内核虚拟内存
内核定义的代码与函数,应用程序不能直接读写这个区域的内容或直接调用内核代码定义的函数
<a name="0YoGW"></a>
###
<a name="KjPCs"></a>
### 内存布局演示
```c
#include <stdio.h>
int g = 0;
void test(int a, int b)
{
printf("Param a:%p\nParam b:%p\n", &a, &b);
}
int main()
{
static int s = 1;
int a = 3;
int b = 4;
printf("Auto a:%p\nAuto b:%p\n", &a, &b);
printf("Global g:%p\nStatic s:%p\n", &g, &s);
test(a, b);
char *c = (char *)malloc(20 * sizeof(char));
printf("malloc c:%p\n", c);
printf("function test:%p\n", &test);
}
______________________
STACK
Param b:0x7ffd88160028
Param a:0x7ffd8816002c
Auto b:0x7ffd88160048
Auto a:0x7ffd8816004c
____________________
HEAP
malloc c:0x1abe010
____________________
STATIC
Global g:0x60103c
Static s:0x601034
_____________________
CODE
function test:0x40052d
malloc工作流程
malloc采用两种系统调用进行内存分配,如果分配的内存大小大于M_MMAP_THRESHOLD选项,那么malloc会直接使用mmap来分配内存:
- sbrk(), 移动program break 指针来改变堆数据区域的大小
- mmap(),是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存
使用mmap产生的内存可以直接释放,所以下面直接说明sbrk()申请与释放内存的过程:
struct header {
size_t size;
unsigned is_free;
struct header_t *next;
}
malloc()
- 检查请求参数大小,如果是0;直接返回NULL
- 获取一个全局锁以保证线程安全
- malloc 内部维护着一个单向链表维护着被分配的内存大小,紧接着是遍历链表,看看是否存在一个标记为空闲的内存块,并且可以容纳给定的大小。如果找到足够大的空闲块,我们将简单地将该块标记为非空闲,释放全局锁,然后返回指向该块的指针
- 如果我们没有找到足够大的空闲块,则通过调用sbrk()来扩展堆。堆必须扩展一个符合请求大小以及头标志的大小。总大小total_size = sizeof(struct header_t)+ size;最后调用sbrk(total_size)
free()
- 调用必须首先确定要释放的块是否在堆的末尾。如果是,我们可以将他提交操作系统回收。否则,只需将其标记为”free”,可以在以后重复使用。
参考:
内存对齐:https://www.cnblogs.com/zzdbullet/p/9635318.html
c内存:https://www.cnblogs.com/yif1991/p/5049638.html
malloc工作流程:https://blog.csdn.net/songchuwang1868/article/details/89928421
自己实现内存分配:https://baijiahao.baidu.com/s?id=1613274452767326335&wfr=spider&for=pc