对一个进程来说,内存是最基本也是最重要的资源。
存储器分配(allocation),内存操控(manipulation)和最后的内存释放(release)。
进程地址空间
Linux将它的物理内存虚拟化。进程不能直接在物理内存上寻址而是由Linux内核为每个进程维护一个特殊
的虚拟地址空间。这个地址空间是线性的,从0开始。
页和页面调度
虚拟空间由许多页组成。系统的体系结构决定了页的大小,典型的页大小包括4K(32位)和8K(64位)。每个页只有无效和有效两种状态。
有效页面和一个物理页或者一些二姐存储介质关联,例如交换分区。
无效页面没有关联,代表它没有被分配使用。对无效页面的访问会出现段错误。
进程不能访问处在二级存储中的页,除非这个页和物理内存中的页相关联,否则存储器管理单元(MMU)会产生一个页错误(page fault)。然后从二级存储换入需要的页面。
共享和写时复制
虚存中的多个页面,甚至是属于不同进程的虚拟地址空间,也有可能被映射到同一个物理页面。这样运行不同的虚拟地址空间共享物理内存上的数据。共享的数据可能是可读的,或者是可读可写的。
当进程试图写某个共享的可写页时,最简单的情况下是内核运行写操作。通常会允许大量的进程对同一页面的读写需要某种程度上的合作和同步。
另一种是MMY截取写操作的异常。内核会透明的创造一份页的拷贝供该进程写。这就是写时拷贝。写时拷贝以页为单位,仿佛每个进程都有自己的私有拷贝。
存储区域
内核将某些相同特征的页组织成块。这些块叫做存储区域,段,或者映射。
文本段:包含一个进程的代码,字符串,常量和一些只读的数据。
堆栈段:包括一个进程的执行栈,栈中包括程序的局部变量和函数的返回值。
数据段:又叫堆,包含进程的动态存储空间。
BSS段:包含没有被初始化的全局变量
内存动态分配
#include <stdlib.h>
void *malloc(size_t size);
成功调用会得到一个size大小的内存区域,并返回一个指向这块区域内存的首地址指针。失败返回NULL。
数组分配
数组元素的大小以及确定,但是元素的个数可变时
#include <stdlib.h>
void *calloc(size_t nr, size_t size);
成功会返回一个指针,指向一快可以存储下整个数组的内存。nr个元素,每个为size字节。
int *x, *y;
x = malloc(50 * sizeof(int));
y = calloc(50 ,sizeof(int));
上面两个函数分配的内存是一样的,但是calloc会初始化为0,
调整已分配内存大小
void* realloc(void *ptr, size_t size);
成功调用将ptr指向的内存区域大小变为size字节。返回一个新的空间指针。
动态内存释放
void free(void *ptr);
自动分配内存,当栈不在使用,空间被自动释放。与之不同的是,动态内存将永久占有一个进程地址空间的一部分,知道它被显式释放。
对齐
数据的对齐是指数据地址和由硬件确定的内存块之间的关系。一个变量的地址是它大小的倍数,叫做自然对齐。
数据段的管理
一般都不会使用这些接口。