目录linux
[性能优化]-linux内存体系结构
second60 20180801
1.说说linux内存分类
在linux中,内存分两种:物理内存和swap交换内存。
物理内存:真正的内存,内存条里的内存,一个内存条4G,那么物理内存大小就 是4G。
swap交换内存:把必定的磁盘作为交换内存,为了物理内存不够用时,把内存中的 数据page out或swap out到交换内存中。
2.内存是怎么管理的?
进程是怎么使用内存的呢?是直接操做的吗?
答案固然不是。
linux能够同时运行成千上万的进程。每一个进程都会占用必定的内存,若是进程直接操做内存,那么内存是根本不够用的。同时也不方便管理物理内存。若是一启动就把进程的全部内存分配好,那么会形成内存的浪费,由于可能运行过程根本不会运行到那。
根据上面缘由,内核对内存管理作了处理:
内存采用分页技术,提升性能
内存和用户之间,加了一层虚拟内存
采用动态分配的方法使用内存
使用swap内存扩展内存
进程采用虚拟地址空间
3. 物理内存的分页技术
若是物理内存是按字节来存取,那么效率是比较差的。为了高效地管理内存和提高效率,采用了内存分页技术。
3.1 什么是分页技术
把内存分红固定大小的chunk组成,称为分页page。分页大小取决于处理器体系结构。i386和x86_64中的分页大小是4KB。内核以页大小处理内存。
系统上的物理内存被分红页帧(page frame),一个页帧包含数据的一个分页。
3.1.1 页帧的分配
一个分页page是物理内存(page frame)或虚拟内存中一组连续性地址。一般4KB。
当一个进程请求必定数量的内存页时,若是有有效内存页,内刻当即分配给进程。
不然,须要从一些其余进程或分页缓存中获得。
3.1.2 进程分配内存
进程是不能直接对物理内存寻址的。每一个进程有一个虚拟地址空间(32位为4GB)。当为一个进程分配内存时,页帧的物理地址被映射到进程的虚拟地址。
一个进程的虚拟地址空间大小取决于处理器构构。32位系统,一个进程虚拟地址空间大小2的32次方(4GB);64位系统上,大小为2的64次方(16EB)。然而,一个进程不会使用到那么多的地址空间,大部份是未分配的,并无映射到任何物理内存。
以32位系统为例:
上图为一个进程在32位系统的虚拟地址空间图。
3.1.3 linux物理地址空间布局
以32位系统为例,内核地址空间划分为0-3G为用户空间,3-4G为内核空间。64位划分不一样。
物理地址空间的顶部如下一段空间,被PCI设备IO内存映射占据,它们的大小和布局由PCI规范所决定。640K-1M这段地址被BIOS和VGA适配器所占据。
linux系统在初始化时,会根据实际的物理内存大小,为每一个物理页面建立一个page对象,全部page对象构成一个mem_map数组。
内核将全部物理页面划分到3类内存管理区中
ZONE_DMA(0-16m):,该区域的物理页面专门供IO设备的DMA使用。之因此须要单独管理 DMA的物理页面,是由于DMA使用物理地址访问内存,不通过MMU,而且须要连续 的缓冲区,因此为了可以提供物理上连续的缓冲区,必须从物理地址空间专门划分一段 区域用于DMA。
ZONE_NORMAL(16M~896M):该区域的物理页面是内核可以直接使用的
ZONE_HIGHMEM(896M~end):该区域即为高端内存,内核不能直接使用。
总结:内核使用0-1G的内存,其中896-1G中的128M给映射高端内存。1G以上的内存内核经过映射来操做。
3.1.4 linux虚拟地址内核空间分布
在kernel image下面有16M的内核空间用于DMA操做。位于内核空间高端的128M地址主要由3部分组成,分别为vmalloc area,持久化内核映射区,临时内核映射区。
因为ZONE_NORMAL和内核线性空间存在直接映射关系,因此内核会将频繁使用的数据如kernel代码、GDT、IDT、PGD、mem_map数组等放在ZONE_NORMAL里。而将用户数据、页表(PT)等不经常使用数据放在ZONE_ HIGHMEM里,只在要访问这些数据时才创建映射关系(kmap())。好比,当内核要访问I/O设备存储空间时,就使用ioremap()将位于物理地址高端的mmio区内存映射到内核空间的vmalloc area中,在使用完以后便断开映射关系。
3.1.5 linux虚拟地址用户空间分布
用户进程的代码区通常从虚拟地址空间的0x08048000开始,这是为了便于检查空指针。代码区之上即是数据区,未初始化数据区,堆区,栈区,以及参数、全局环境变量。
这个你们都很熟悉了,就不详细说明了,请看另外一篇博客。
3.1.6 linux虚拟地址与物理地址映射关系
Linux将4G的线性地址空间分为2部分,0~3G为user space,3G~4G为kernel space。
因为开启了分页机制,内核想要访问物理地址空间的话,必须先创建映射关系,而后经过虚拟地址来访问。为了可以访问全部的物理地址空间,就要将所有物理地址空间映射到1G的内核线性空间中,这显然不可能。因而,内核将0~896M的物理地址空间一对一映射到本身的线性地址空间中,这样它即可以随时访问ZONE_DMA和ZONE_NORMAL里的物理页面;此时内核剩下的128M线性地址空间不足以彻底映射全部的ZONE_HIGHMEM,Linux采起了动态映射的方法,即按需的将ZONE_HIGHMEM里的物理页面映射到kernel space的最后128M线性地址空间里,使用完以后释放映射关系,以供其它物理页面映射。虽然这样存在效率的问题,可是内核毕竟能够正常的访问全部的物理地址空间了。
问题:分配了1GB给虚拟地址空间给内核,够用吗?
答:在内核896-1024M,能够映射到其余的物理内存。因此不存在不够的问题。除非内存不够了。
3.2 虚拟内存管理
操做系统的物理内存对于应用程序和用户来讲一般是隐藏的,由于操做系统能够将任何物理内存映射到虚似内存。
系统不会给应用程序分配物理内存,可是它会向linux内核请求必定大小的虚拟内存,并在虚拟内存中交换获得映射。
虚拟内存没必要映射到物理内存。若是你的应用程序被分了大量内存,则有一部分可能被映射到磁盘的swap文件。
应用程序一般不直接向磁盘子系统写入,而是向高速缓存或缓冲区写入。当时间片到达,或者一个文件的大小超出缓冲缓存时,内核线程pdflush/Per-BDI flush会将缓存/缓冲中的数据刷新到磁盘中。
管理虚拟内存的默认配置是:分配全部有效的空闲内存空间做为磁盘的缓存。
一样,linux也能够很是有效地处理swap空间。
3.2.1伙伴系统( buddy system)
内核使用一种被称为伙伴系统的机制来维护它的空闲分页。伙伴系统维护空闲分页,并尝试给分页分配请求分页。它试图保持内存区域是链接的。
当分配分页失败时,会进行分页回收。
经过/proc/buddyinfo找到伙伴系统的信息。
3.2.2分页回收
当一个进程请求映射必定数量分页的时候,若是没有有效的分页,linux内核将尝试释放必定数量的分页,而后将这些分页分配给新的请求内存的进程。这个过程称为分页回收(pace reclaiming)。内核线程kswapd和内核函数try_to_free_page()负责分页回收。
分页的使用主要有两个目的:分页缓存和进程地址空间。分页缓存是分页被映射到磁盘的一个文件。分页属于一个进程地址空间,它被用于堆和栈。当kswapd回收分页时,它宁肯缩小分页缓存也不肯分页移出进程拥有的分页。
分页缓存的分页回收和进程地址空间的回收在很大程序上依赖于使用场景,这将会影响性能。经过/proc/sys/vm/swappiness来控制这些行为.
3.2.3 swap
当发生分页回收时,在非活路列表中属于进程进址空间的候选分页能够被分页移出。目的保证主内存的分配和更加有效的利用空间。
虚似内存由物理内存和磁盘子系统或swap分区组成。若是linux中虚拟内存管理器发现内存分页已经被分配,但大量埋单尚未使用完,它会将这个内存分页移动到swap空间。
4 总结
内存是性能优化的最要一环。了解linux内存分配的原理,能够在写应用的内存使用时,能够更优的写出更优的代码。
4.1 内存代码层优化
1. 结构体对齐:可使内存更小。能够学习C语言中的__attribute__选项
2. 内存预分配:在能预测到使用内存最大数量时,或限制最大量数时,预分配内存。
a) 减小内存碎片的产生,存减小建立消毁的开销
b) 缺点:预知估测好大小,占用必定的内存
3. 能够设置TCP接收和发送缓冲区大小和分页大小的N倍,如分页4K,缓冲设为8K.
4. 编写适合项目应用的内存分配函数。开源库基本都会提供一套自已的内存分配函数,为了更适合功能场景的内存分配。
4.2 内存性能优化
这个留在后面写。