Linux系统内存管理系列之五,linux内存管理之五


前面几部分主要从原理上讲解了Linux内核如何管理内核态内存与用户态内存,这部分我们重点探究内存管理中一些实际工程上的性能问题。

1 为何需要内存对齐?

内存对齐是一种提供内存访问速度的策略,cpu在访问未对齐的内存需要经过两次内存访问,而经过内存对齐一次就可以。这是根据CPU位数决定的,32位CPU一次的取指能力为4个字节,如果不做内存对齐,很可能一个4字节的变量会处于两个4字节的内存块中,这样就需要CPU访问内存两次。可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

2 进程地址空间各段的分配情况?

测试代码:

#include

static int d=100;

void main(){

int a,b=10;

printf("%p %p\n",&a,&b);

int c[2];

printf("%p %p\n",&c[0],&c[1]);

printf("%p\n",&d);

}

运行结果:

0x7fff31b0f47c 0x7fff31b0f478

0x7fff31b0f470 0x7fff31b0f474

0x6008c4

我们看到用户空间栈是逆向增长的a,b都是分配在栈上,其中a分配在bss段上,堆是正向增长的c[0],c[1]分配在堆上,d是全局静态常量区,它分布在全局静态常量存储区,它的地址与其它变量地址相差较大。而且全局静态常量区地址一旦分定,二次运行不会发生变化。

3 oom_killer机制

所谓oom_killer就是当系统的物理内存与交换分区都被用尽时,再有新的进程要申请内存时,内核将触发oom_killer。在centos系统中该机制按照如下准则运行:

/proc/sys/vm/panic_on_oom为0,证明不会触发panic

/proc/sys/vm/oom_kill_allocating_task为0内核将检查每个进程的分数,分数最高的进程将被kill掉。/proc/sys/vm/oom_dump_tasks为1,且系统的rlimit中设置了core文件大小,将会由/proc/sys/kernel/core_pattern里面指定的程序生成core dump文件,这个文件里将包含pid, uid, tgid, vm size, rss, nr_ptes, nr_pmds, swapents, oom_score_adj,score, name等内容,拿到这个core文件之后,可以做一些分析,看为什么这个进程被选中kill掉。

测试代码:

#include

#define max 10000000

void main(){

int *p[max];

int i=0;

for(;i p[i]=malloc(max);

usleep(100);

}

}

在后台运行该进程,pid号为6048。执行cat /proc/6048/status会发现该进程的变化如下:

??

\
\

 

该进程地址空间不断增长,然后 free-m:

?

\

 

物理内存不断被消耗,交换分区的内存也不断被消耗。

执行cat /proc/6048/oom_score观察如下:

?

\

 

进程的oom_score不断增长,最后交换分区与物理内存都被用尽,系统检查分数最高的进程并kill掉。

4 缺页异常处理机制

当MMU中确实没有创建虚拟页物理页映射关系,并且在该虚拟地址之后再没有当前进程的线性区vma的时候,可以肯定这是一个编码错误,这将杀掉该进程;

当MMU中确实没有创建虚拟页物理页映射关系,并且在该虚拟地址之后存在当前进程的线性区vma的时候,这很可能是缺页异常,并且可能是栈溢出导致的缺页异常;

当使用malloc/mmap等希望访问物理空间的库函数/系统调用后,由于linux并未真正给新创建的vma映射物理页,此时若先进行写操作,将如上面的2的情况产生缺页异常,若先进行读操作虽也会产生缺页异常,将被映射给默认的零页(zero_pfn),等再进行写操作时,仍会产生缺页异常,这次必须分配物理页了,进入写时复制的流程;

当使用fork等系统调用创建子进程时,子进程不论有无自己的vma,“它的”vma都有对于物理页的映射,但它们共同映射的这些物理页属性为只读,即linux并未给子进程真正分配物理页,当父子进程任何一方要写相应物理页时,导致缺页异常的写时复制;

 

\

相关内容