Linux内核:虚拟地址到物理地址,是什么时候开始映射( 二 )


Linux内存分配的lazy行为Linux总是以最lazy的方式,给应用程序分配内存 。

Linux内核:虚拟地址到物理地址,是什么时候开始映射

文章插图
 
malloc100M内存成功时,其实并没有真实拿到 。只有当100M内存中的任何一页,被写一次的时候,才成功 。vss:虚拟地址空间 。rss:常驻内存空间malloc 100M内存成功时,Linux把100M内存全部以只读的形式,映射到一个全部清0的页面 。
当应用程序写100M中每一页任意字节时,会发出page fault 。linux 内核收到缺页中断后,从硬件寄存器中读取到,包括缺页中断发生的原因和虚拟地址 。Linux从内存条申请一页内存,执行cow,把页面重新拷贝到新申请的页表,再把进程页表中的虚拟地址,指向一个新的物理地址,权限也被改成R+W 。
调用brk 把8k变成 16k 。
针对应用程序的堆、代码、栈、等,会使用lazy分配机制,只有当写内存页时,才会真实请求内存分配页表 。但,当内核使用kmalloc申请内存时,就真实地分配相应的内存,不使用lazy机制 。
内存OOM当真实去写内存时,应用程序并不能拿到真实的内存时 。Linux启动OOM,linux在运行时,会对每一个进程进行out-of-memory打分 。大部分主要基于,耗费的内存 。耗费的内存越多,打分越高 。
cat /proc/<pid>/oom_score#include <stdlib.h>#include <stdio.h>#include <string.h>int main(int argc, char **argv){int max = -1;int mb = 0;char *buffer;int i;#define SIZE 2000unsigned int *p = malloc(1024 * 1024 * SIZE);printf("malloc buffer: %pn", p);for (i = 0; i < 1024 * 1024 * (SIZE/sizeof(int)); i++) {p[i] = 123;if ((i & 0xFFFFF) == 0) {printf("%dMB writtenn", i >> 18);usleep(100000);}}pause();return 0;}定条件:
总内存1G
1、swapoff -a 关掉swap交换
2、echo 1 >
/proc/sys/vm/overcommit_memory
3、内核不去评估系统还有多少空闲内存
Linux进行OOM打分,主要是看耗费内存情况,此外还会参考用户权限,比如root权限,打分会减少30分 。
还有OOM打分因子:/proc/pid/oom_score_adj (加减)和 /proc/pid/oom_adj (乘除) 。
总结:1、slab的作用,针对在内核空间小内存分配,和常用数据结构的申请 。
2、同样的二次分配器,在用户空间是C库 。malloc和free的时候,内存不一定从buddy分配和还给buddy 。
3、kmalloc,vmalloc 和malloc的区别
  • kmalloc:申请内存,一般在低端内存区 。申请到时,内存已经映射过了,不需要再去改进程的页表 。所以,申请到的物理页是连续的 。
  • vmalloc:申请内存,申请到就拿到内存,并且已经修改了进程页表的虚拟地址到物理地址的映射 。vmalloc()申请的内存并不保证物理地址的连续 。
  • 用户空间的malloc:申请内存,申请到并没有拿到,写的时候才去拿到 。拿到之后,才去改页表 。申请成功,页表只读,只有到写时,发生page fault,才去buddy拿内存 。
  • kmalloc和vmalloc针对内核空间,malloc针对用户空间 。这些内存,可以来自任何一个Zone 。
  • 无论是kmalloc,vmalloc还是用户空间的malloc,都可以使用内存条的不同Zone,无论是highmem zone、lowmem zone 和 DMA zone 。
4、如果在从buddy拿不到内存时,会触发Linux对所有进程进行OOM打分 。当Linux出现内存耗尽,就kill一个oom score 最高的是那个进程 。oom_score,可以根据 oom_adj (-17~25) 。
Android/ target=_blank class=infotextkey>安卓的程序,不停地调整前台和后台进程oom_score,当被切换到后台时,oom_score会被调整得比较大 。以保证前台的进程不容易因为oom而kill掉 。




推荐阅读