linux性能四大相关子系统,cpu, 内存,磁盘io,网络。内存的使用贯彻系统,任何活的东西都是活在内存中。
内存的管理:段式管理+页式管理。
段式管理,Linux中将段基址设为0,即兼容了intel的段的要求,又可以让不支持分段的架构能够绕过分段。这使得逻辑地址=线性地址。
页式管理,是将物理内存分成固定大小的frame,然后通过mmu,在虚拟空间中页设成为同样大小page,一般是4K。随着物理内存越来越大,64位架构的普及,可支持的内存以T计算,在某些应用中,内核使用大页,增加缓存命中,减少分配次数,8K,1M,2M,4M。虚拟空间的page通过mmu映射到物理的frame,Linux利用(时间,空间)局部性原理,只在必要时才做page到frame的映使射,而不必系统一启动就将所有物理内存映射到虚拟空间。使用分级的页表的方式,通过逐级检索获得最终的映射的物理页。32位中一般使用2机页表,64位系统可使用3级、4级、5级页表。
当代的内存管理体系中,最顶层称做node,这对应numa体系中(uma中就是常值),节点内部内存空间。node内部被划分成zone,一般分DMA zone, normal zone和high memory,一般64位系统不再设Highmem。
物理内存分布:开始部分保留,640K~1M,供bios、VGA的ROM使用,DMA zone一般供设备启动DMA功能使用,通常1M~16M,dma需要连续的物理内存。normal zone,16M~896M,线性映射区(kmalloc不经过页表,但线性直接映射确实在内核映射表中),虚拟地址=物理地址+固定偏移,内核将自己代码段和常用的数据结构结构都放在normal zone,以减小虚拟地址到物理地址映射的开销;high memory 区域由vmalloc区,永久映射区(pkmap)和固定映射区构成,设备的IO空间通过ioremap映射在vmalloc区间,用户数据、页表在pkmap区。固定映射区可以放一些基本不变化的映射,tlb不会将其换出,总是能命中。而64位系统,使用48位地址线,虚拟地址空间划分中,将内核空间放在高128T,用户空间放在低128T。
系统为了方便管理,减少内存的碎片,使用budysystem和slab来处理内存的分配和回收。buddysystem是将内存分成大小不等的块,一块是连续的页. 块大小相同、地址相邻、属性一致的认定为伙伴,可以合并成一个更大的块。处理分配内存请求时,从最接近的order的free list开始,如果没有空闲块,往上在order+1的freelist搜索,直至找到可用的空闲块。如果有剩余,则以尽可能大的order放入freelist。如果找不到可用的空闲块,会触发内存的回收机制,像脏页刷新,页的换出,页面迁移等,然后回收;低内存情况下还会触发oom killer。buddy system能够尽量减少大于1个页的内存孔洞,外部碎片。在申请小块内存,尤其是内核在normal zone上分配时,使用slab来处理,减少内部碎片。slab和buddy的思想是一致的,将内存以大小固定块把内存组织起来,以最接近请求的块大小分配。slab分为通用slab和专有slab。内核把常用的结构以slab cache的方式管理,结构释放后并不释放内存,将其放在对应的结构的slab cache中。内核中的kmalloc,申请内存,如果大于64K会进入buddysystem分配。如果较小,就在slab的通用分配器上。slab开始是空的,通过grow的方式,从buddysystem申请分配内存。
用户空间的内存使用,是在虚拟地址空间上,以MMU将物理内存映射到用户空间。程序分了代码段,数据段,bss,stack,mmap区间(以vma描述)。文件在load时候,通过解析解析elf文件,将各段通过do_mmap映射到到进程地址空间,这些数据保存再mm_struct的code_start.code_end这类似变量。在程序开始执行时,将虚拟空间映射到物理内存上,加载程序各个段到内存,开始执行。程序申请中动态内存申请时再进程的堆空间上完成的,通过brk系统调用来实现。brk系统调用建立vma(或扩展原vma的大小),返回虚拟地址,并没有建立页表建立映射。当第一做写操作时,才真正建立虚拟地址到物理内存的映射。