Linux 用户进程内存空间详解
|
编码人员在编写程序之际,时常要处理变化数据,无法预料要处理的数据集变化是否大(phper可能难以理解),所以除了变量之外,还需要动态分配内存。GNU libc库提供了二个内存分配函数,分别是malloc()和calloc()。调用malloc(size_t size)函数分配内存成功,总会分配size字节VM(再次强调不是RAM),并返回一个指向刚才所分配内存区域的开端地址。分配的内存会为进程一直保留着,直到你显示地调用free()释放它(当然,整个进程结束,静态和动态分配的内存都会被系统回收)。开发人员有责任尽早将动态分配的内存释放回系统。记住一句话:尽早free()! 我们来看看,malloc()小示例。
/* @filename:example_2_4.c */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *p_4kb, *p_128kb, *p_300kb;
if ((p_4kb = malloc(4*1024)) != NULL)
{
free(p_4kb);
}
if ((p_128kb = malloc(128*1024)) != NULL)
{
free(p_128kb);
}
if ((p_300kb = malloc(300*1024)) != NULL)
{
free(p_300kb);
}
return 0;
}
#gcc example_2_4.c –o example_2_4 #strace –t ./example_2_4 … 00:02:53 brk(0) = 0x8f58000 00:02:53 brk(0x8f7a000) = 0x8f7a000 00:02:53 brk(0x8f79000) = 0x8f79000 00:02:53 mmap2(NULL, 311296, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb772d000 00:02:53 munmap(0xb772d000, 311296) = 0 … PS:系统调用brk(0)取得当前堆的地址,也称为断点。 通过跟踪系统内核调用,可见glibc函数malloc()总是通过brk()或mmap()系统调用来满足内存分配需求。函数malloc(),根据不同大小内存要求来选择brk(),还是mmap(), 128Kbytes是临界值。小块内存(<=128kbytes),会调用brk(),它将数据段的最高地址往更高处推(堆从底部向上增长)。大块内存,则使用mmap()进行匿名映射(设置标志MAP_ANONYMOUS)来分配内存,与堆无关,在堆之外。这样做是有道理的,试想:如果大块内存,也调用brk(),则容易被小块内存钉住,必竟用大块内存不是很频繁;反过来,小块内存分配更为频繁得多,如果也使用mmap(),频繁的创建内存映射会导致更多的开销,还有一点就是,内存映射的大小要求必须是“页”(单位,内存页面大小,默认4Kbytes或8Kbytes)的倍数,如果只是为了”hello world”这样小数据就映射一“页”内存,那实在是太浪费了。 跟malloc()一样,释放内存函数free(),也会根据内存大小,选择使用brk()将断点往低处回推,或者选择调用munmap()解除映射。有一点需要注意:并不是每次调用free()小块内存,都会马上调用brk(),即堆并不会在每次内存被释放后就被缩减,而是会被glibc保留给下次malloc()使用(必竟小块内存分配较为频繁),直到glibc发现堆空闲大小显著大于内存分配所需数量时,则会调用brk()。但每次free()大块内存,都会调用munmap()解除映射。下面是二张malloc()小块内存和大块内存的示例图。 示意图:函数malloc(100000),小于128kbytes,往高处推(heap->)。留意紫圈标注 示意图:函数malloc(1024*1024),大于128kbytes,在heap与stack之间。留意紫圈。PS:图中的Data Segment泛指BSS, Data, Heap。有些文档有说明:数据段有三个子区域,分别是BSS, Data, Heap。 缺页异常(Fault Page) 每次调用malloc(),系统都只是给进程分配线性地址(VM),并没有随即分配页框(RAM)。系统尽量将分配页框的工作推迟到最后一刻—用到时缺页异常处理。这种页框按需延迟分配策略最大好处之一:充分有效地善用系统稀缺资源RAM。 当指针引用的内存页没有驻留在RAM中,即在RAM找不到与之对应的页框,则会发生缺页异常(对进程来说是透明的),内核便陷入缺页异常处理。发生缺页异常有几种情况:1.只分配了线性地址,并没有分配页框,常发生在第一次访问某内存页。2.已经分配了页框,但页框被回收,换出至磁盘(交换区)。3.引用的内存页,在进程空间之外,不属于该进程,可能已被free()。我们使用一段伪代码来大致了解缺页异常。
/* @filename: example_2_5.c */
…
demo()
{
char *p;
//分配了100Kbytes线性地址
if ((p = malloc(1024*100)) != NULL) // L0
{
*p = ‘t’; // L1
… //过去了很长一段时间,不管系统忙否,长久不用的页框都有可能被回收
*p = ‘m’; // L2
p[4096] = ‘p’; // L3
…
free(p); //L4
if (p == NULL)
{
*p = ‘l’; // L5
}
}
}
…
本文URL地址:http://www.bianceng.cn/OS/Linux/201410/45418.htm (编辑:佛山站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

