版本声明:转载请注明出处,未经允许,禁止商业用途。

进程的地址空间

进程的地址空间是相互独立的,互相之间不会干扰,这种保护机制是硬件提供、操作系统使用的。

进程的地址空间由text segment、data segment、stack segment、heap segment四部分组成。

text segment存储程序代码对应的机器指令,内容只能读不能修改。当两个用户运行同一个程序时,可以共享同一个代码段。类似的,加载到内存中的共享库,可以同时被多个进程使用。这种原理还可以用来实现共享内存,做高效的进程间通信机制,比如C语言中使用的mmap()。

data segment存储存储类型为静态的变量。

可执行文件中存储的就是text segment和data segment中的内容。

stack segment存储program stack,包括正在被执行的函数的返回值、参数和local变量。通常从进程虚拟地址空间的顶部开始向下延伸。

heap segment是进程动态分配的内存区域。注意:这里的heap(堆)和数据结构中的堆不是一个意思,这里的堆是一个先进先出的队列。

stack segment溢出的原因可能是函数递归太深、定义了占用太大空间的局部变量。data segment溢出的原因是定义了占用太大空间的静态变量。为了防止stack segment和date segment溢出,在使用大块内存时尽量使用heap segment。

ulimit -a可以看到Centos6.2(linux 2.6.3.2)stack size缺省是10240k字节(ulimit -s 8192就可以修改为8M),data segment size缺省没有限制大小。

Windows栈空间缺省是1M字节,可以修改。

因为进程虚拟地址空间在物理空间上实际上不连续,进程使用的每块内存都要进行记录,然后将这些记录通过链表保存,链表长度超过32时,使用红黑树保存。

物理内存管理

物理内存的每个页(page)都有一个struct page结构体与其对应进行管理。里面有个指针,该页面被使用时,该指针指向它所属的地址空间(struct address_space)。里面有引用计数,如果引用计数为0,则表示页空闲。

Linux物理内存区域(zone)

因为硬件的限制,内存分为三种:ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM

因为有一些硬件只能使用特定的内存地址来执行DMA(例如ISA设备就只能访问物理内存前16M进行DMA),所以就有了ZONE_DMA。

因为一些体系结构其物理内存的寻址范围比内核虚拟地址(32位linux内核的寻址空间为1G,另外3G为用户空间)范围大。所以有一些内存不能永久映射到内核空间上,这些内存就是ZONE_HIGHMEM。

X86体系结构:

ZONE_DMA:可以用于DMA。内核可以直接映射。通常在0-16MB范围内。

ZONE_NORMAL:内核可以直接映射。通常在16MB-896MB范围内。

ZONE_HIGHMEM:内核不能直接映射。通常为896MB以上。

幸运的是,其它体系结构中通常所有的物理内存都是ZONE_NORMAL。实际上在使用32位linux XLP(MIPS体系)设备上,也存在low memory和high memory。如果使用64位linux,就没有high memory,因为内核虚拟地址空间非常大,能够映射全部物理内存。

ZONE_HIGHMEM对应的内存叫做high memory,其余内存叫做low memory。

Linux里/proc/meminfo中可以看到各内存区域大小。

内存分配

页面分配器(page allocator)

页面分配器用于分配物理内存中的页。它使用伙伴算法(buddy algorithm)。将大小相同且物理上连续内存块称为伙伴。分配给请求者的内存是2的若干次方个页。一个内存块被释放时,如果伙伴是否也空闲,和合并这两个内存块。例如将两个8个页面大的伙伴内存块合并成一个16个页面大的内存块。

slab分配器(slab allocator)

伙伴算法导致大量的内存碎片。Slab分配器可以解决这个问题。

Slab分配器虽然也是使用伙伴算法获得内存块,但是之后切出slab(更小的单元)分别进行管理。

Slab层把不同的对象划分为所谓的高速缓存(cache)组,每个cache都存放不同类型的对象。这些高速缓存又被划分为slab。Slab由一个或者多个物理上连续的页组成,通常一个Slab就是仅仅一页。每个slab都包含一些对象成员,对象是指被缓存的数据结构。Slab有三种状态:满、部分满、空。需要一个新对象时,从部分满的slab中分配,如果没有这样的slab可用,就从空的slab中进行分配,如果没有空的slab,就要创建一个slab。这种策略可以减少碎片。而且很明显很多时候内存的分配和释放不是真的分配和释放,因为cache中的节点是已经申请好了的内存,进程的内存分配操作常常实际上只是操作系统通知进程cache中的一个空闲节点地址,并且将这个节点的状态置为已用,内存释放操作只是将节点的状态置为未使用,从而大大提高了内存频繁的分配和回收时的效率。

OOM

Linux会在内存耗尽(out of memory)时杀掉一些进程以释放内存(实际上是low memory耗尽就开始杀进程)。这相当于为了保存性命而进行的外科手术。

Linux缺省打开OOM kill功能(/proc/sys/vm/panic_on_oom的内容是0),echo 1 > /proc/sys/vm/panic_on_oom关闭该功能。

Core dump

在一个程序崩溃时,它会在指定目录下生成一个core文件(可以通过ulimit命令设置开启还是关闭该功能)。core文件是这个程序的内存映象(同时加上调试信息),主要是用来调试的。用gdb打开core文件,可以看到调用栈。

类似的windows中程序崩溃时会产生dump文件(使用windbg加载)。

Logo

更多推荐