Linux性能优化读书笔记(5):内存机制

一、内存映射
 

我们买电脑的参数8GB,是指物理内存,而只有内核才能直接访问物理内存,那么进程访问内存要如果做?

虚拟存储器(内存)的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。 为了更好的管理内存,操作系统将内存抽象成地址空间。每个进程拥有自己的地址空间,这个地址空间是连续的,这个地址空间被分割成多个块,每一块称为一页。也就是说,进程可以很方便访问内存,更确切是访问虚拟内存。

每个进程都有这么大的地址空间,那么所有进程的虚拟内存加起来,要比实际内存大很多。但是,并不是所有的虚拟内存都会分配物理内存,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存。内存映射,其实就是讲虚拟内存地址映射到物理内存地址,内核为每个进程维护一张页表,记录虚拟地址和物理地址的映射关系

Linux性能优化读书笔记(5):内存机制_第1张图片

页表实际存储在CPU的内存管理单元MMU中,这样处理器就可以直接通过硬件,找出要访问的内存

当进程访问的虚拟地址在页表中查不到,产生却缺页异常,进入内核空间分配物理内存,更新进程页表,最后返回用户空间,恢复进程运行

不过要注意,MMU并不以字节为单位来管理内存,而是规定内存映射最小单位页的大小通常为4KB,会导致页表项过多。比如32位系统需要100多万个页表项(4G/4k)才能实现整个地址空间映射

所以使用多级页表和大页(hugepage)解决页表项过多

Linux使用使用四级页表来管理内存页。虚拟地址分为五个部分,前四个表项用于选择页,最后一个索引表示页内偏移

Linux性能优化读书笔记(5):内存机制_第2张图片

每个进程都拥有一个自己的页表,在linux中,有一个页目录数组,这是分页机制的最高层,每个进程的页表对应其中的一个页目录项

32位系统的两极分页:两级表的第一级表称为页目录,存储在一个4K字节的页中,页目录表共有1K个表项,每个表项为4个字节,线性地址最高的10位(22-31)用来产生第一级表索引,由该索引得到的表项中的内容定位了二级表中的一个表的地址,即下级页表所在的内存块号。 
第二级表称为页表,存储在一个4K字节页中,它包含了1K字节的表项,每个表项包含了一个页的物理地址。二级页表由线性地址的中间10位(12-21)位进行索引,定位页表表项,获得页的物理地址。页物理地址的高20位与线性地址的低12位(偏移offset)形成最后的物理地址。 

Linux性能优化读书笔记(5):内存机制_第3张图片

 

二、内存分配与回收

用户空间内存,也分成了多个不同的段

Linux性能优化读书笔记(5):内存机制_第4张图片

  • 只读段包括代码和常量
  • 数据段包括全局变量
  • 堆包括动态分配的内存
  • 文件映射段包括动态库。共享内存等
  • 栈包括局部变量和函数调用上下文,一般大小固定为8MB

其中,堆和文件映射段的内存是动态分配的,比如使用C标准库的malloc()或者mmap()。

  • 对于小块内存(小于128k),C标准库使用brk()来分配,移动堆顶的位置来分配内存,这些内存释放后不会立刻归还给系统,而是被缓存起来,可以重复利用。优点是减少缺页异常的发生,缺点是内存没有归还系统,频繁的内存分配和释放会造成内存碎片
  • 大块内存(大于128k)使用内存映射mmap(),就是在文件映射段找一块空闲内存分配出去,会在释放时直接归还系统,每次mmap都会发生缺页异常。

然而,这两种调用发生是,并没有真正分配内存,而是在首次访问时,才通过缺页异常陷入内核中分配内存。

在内核空间,通过slab分配器来管理小内存

发现内存紧张时,系统会通过一系列机制来回收内存,比如如下三种机制:

  • 回收缓存,比如使用LRU算法,回收最近使用最少的内存页面
  • 回收不常访问的内存,把不常用的内存通过交换分区(swap)写到磁盘中
  • 杀死进程,通过OOM(out of memory) 直接杀掉占用大量内存进程

其中第三种方式OOM,是一种内核的保护机制,使用oom_score为每个进程的内存使用情况进行评分。管理员可以通过/proc文件系统,手动设置进程oom_adj,从而调整进程的oom_score。

  • 一个进程消耗内存越大,oom_score就越大
  • 一个进程运行占用的CPU越多,oom_score越小

三、Buffer和Cache

free命令的最终来源是/proc/meminfo。

A buffer is something that has yet to be “written” to disk.

A cache is something that has been “read” from the disk and stored for later use.

比如你每秒要写100次硬盘,对系统冲击很大,浪费了大量时间在忙着处理开始写和结束写这两件事嘛。用个buffer暂存起来,变成每10秒写一次硬盘,对系统的冲击就很小。

Cache的核心作用是加快取用的速度。比如你一个很复杂的计算做完了,下次还要用结果,就把结果放手边一个好拿的地方存着,下次不用再算了。加快了数据取用的速度。

Cache是内核页缓存和Slab用到的内存,对应的是/proc/meminfo中的cached和SReclaimable。Buffer是对磁盘(块设备文件)数据的缓存,Cache是文件(普通文件)数据的缓存,既会用在读请求,也会用在写请求中。读写普通文件,会经过文件系统,由文件系统负责与磁盘交互。而读写磁盘或者分区,跳过文件系统直接操作disk,“裸IO”(Direct IO),他们使用的缓存是不同的。

  • Buffers是对原始磁盘块的临时存储,用来缓存磁盘的数据,通常不会特别大。换句话说 是块设备的读写缓冲区用于内存和硬盘(或其他 I/O设备)之间的数据交换的速度而设计的。
  • cached是从磁盘读取文件或写文件的页缓存。Cache缓存区,是高速缓存,是位于CPU和主内存之间的容量较小但速度很快的存储器,如果多个进程要访问某个文件,可以把此文件读入Cache中,这样下一个进程获取CPU控制权并访问此文件直接从Cache读取,提高系统性能。
  • SReclaimable是Slab的一部分,且是可回收的部分。

实例:

1.通过读取随机设备,生成一个500MB大小文件

dd if=/dev/urandom of=/tmp/file bs=1M count=500

dd可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。

  • if=文件名:输入文件名,缺省为标准输入。即指定源文件。
  • of=文件名:输出文件名,缺省为标准输出。即指定目的文件。

 /dev/random和/dev/urandom是Linux系统中提供的随机伪设备,这两个设备的任务,是提供永不为空的随机字节数据流。

通过vmstat观察输出,可以看到Cache不断增长,而Buffer基本不变。说明写文件时会用到Cache缓存数据

2.向磁盘写入随机数据

dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048

这里Buffer增长很明显

3。从文件读取数据写入空设备

dd if=/tmp/file of=/dev/null

通过vmstat,发现读取文件(bi大于0)Buffer基本不变,而Cache在不停的增长

4.从块设备磁盘中读取数据

dd if=/dev/sda1 of=/dev/null

这里Buffer增长很明显,说明读磁盘时,数据缓存到了Buffer中,解决了存储速度不同步的设备问题,

 

 

 

 

你可能感兴趣的:(Linux性能优化读书笔记(5):内存机制)