Linux分级存储, since 2020-11-20

(2020.11.20 Fri)
CPU缓存、内存和SD卡都有存储功能,他们速度和容量各不相同,且速度和容量是个矛盾。为兼顾性能和成本,计算机大多采取分级存储的形式,使不同速度的存储原件协同工作。

CPU缓存

CPU配上了两级的高速缓存,让CPU更快的提取到数据。由于缓存造假高昂,容量并不大。

当CPU需要读写某个内存地址,它会先检查该内存地址的数据是否已经存在于某条缓存记录(cache entry)中。如果缓存记录中的内存地址信息和CPU寻址信息相符,就说明数据已经缓存了,这种情况叫缓存命中(cache hit)。CPU会直接读写缓存中的目标记录,速度会比读写内存快很多。如果CPU想要读写的数据不再缓存中,称为缓存缺失(cache miss),那么缓存会增加一条新的缓存记录,把内存地址的数据加载到该缓存记录中。CPU随后从缓存中读写数据。

缓存空间优选,当数据过多,需要选择替换缓存中的一个记录。这条已经存在的缓存记录称为牺牲者(victim)。新的缓存记录会被放在牺牲者所在的位置。选择牺牲者的方法有下面4种

  • 最少使用(LFU, least frequently used)数据
  • 最久没有使用(LRU, least recently used)数据
  • 最早被缓存(FIFO, first-in first-out)数据
  • 随机替换(random replacement)数据

以LRU为例,在该策略下,CPU为每个缓存记录增加一个计数。当CPU读缓存时,LRU会把命中记录的计数清零,而其他记录的计数加1.如果一条记录长期没有被读取,那么它的计数就会越来越大。在选择牺牲者时,CPU缓存会选择计数最大的记录作为牺牲者。该流程基于一层缓存。

虚拟内存如何利用缓存提高效率

除了数据,CPU还会缓存来自内存的指令及分页记录。很多技术的实现都离不开缓存带来的效率,以虚拟内存为例,虚拟内存技术可以实现很多功能比如构建进程空间和实现内存共享,但虚拟内存并不免费。内核必须记录虚拟内存页和物理内存页的对应关系,并花费额外的CPU时间进行地址转换。利用缓存技术把分页记录放在CPU内部的高速元件上,可以有效解决寻址的效率问题。

页交换

通过缓存技术,弥补了CPU和内存之间的速度差。而虚拟内存技术,可以把外部存储器空间当做内存用。虚拟内存可以把一部分的外部存储器空间换成内存空间,使应用程序可以虚拟的增加内存大小。这个技术的关键在于页交换(page swap),也就是进程空间和外部存储空间以页为单位交换数据。虚拟内存是管理数据和数据地址的方法,也可用于外部存储空间的管理。操作系统把一部分外部存储空间划分成页,称作交换空间(swap space)。操作系统管理内存的方式来管理交换空间。物理内存加交换空间大大超越了实际存储容量。

为了保证读写效率,程序只用在物理内存中的虚拟内存。当程序访问的数据恰好在交换空间时,内核就会启动页交换,把交换空间的页转移到物理内存中,随后内核把分页对应到该物理内存的位置,通知程序继续进行数据操作。这样,程序访问的虚拟内存地址就指向了物理内存中的数据位置。而应用内存只是根据虚拟内存地址进行操作,不需要知道内核的幕后操作。

具体讲,内核记录着虚拟内存的对应关系。当程序访问虚拟内存页时,内核会根据对应关系,知道物理页存在内存还是外部存储器中。如果该页存在外部存储器,内核则会让进城短暂休息,然后将外部存储器这一页的内容放入物理内存中。如果内存空间已满,那么虚拟内存要选择把内存中的一页移出交换空间,从而为要进入内存的页准备好空间。在这个过程中,内存和外部存储器交换了一页。移出内存的页充当了牺牲者。Linux系统使用了一种类似于LRU的策略,选择最久没有使用的分页作为牺牲者。

交换空间

一些具体的实施方法。交换空间有交换分区和交换文件两种形式。分区就是用一个独立的存储器分区作为交换空间,和一般的磁盘分区不同,它没有文件系统,完全以页的方式进行管理。交换文件是文件系统中的一个特殊文件,它占据的空间以页的方式进行管理,作为交换空间。

$sudo swapon -s #查看交换空间

返回: filename type size used priority
返回的每一行是系统正在使用的交换空间。type字段表明该交换空间是一个分区而不是文件,通过filename可以知道交换分区是磁盘xxx。size字段表明磁盘大小,单位KB。used字段表示有多少交换空间被使用。priority字段表示linux系统的交换空间使用优先级。如果在Linux系统中挂载两个或更多具有相同优先级的交换空间,那么Linux会替换使用。如果两个交换空间正好位于两种设备上,那么交替使用的方式可以提升交换性能。

$sudo mkswap /dev/hdb1 #mkswap把一个分区变成交换分区
$sudo swapon /dev/hdb1 # swapon激活交换分区
$sudo swapon -s #确认/dev/hdb1已经加入交换空间
$sudo dd if=/dev/zero of=/var/swapfile bs=1024 count=104875 #创建一个1GB的文件
$ #/var/swapfile是交换文件,选项count说明了文件大小,104875KB
$mkswap /var/swapfile #用mkswap调用交换文件
$swapon /var/swapfile #激活交换文件,让交换文件称为可以使用的交换空间

外存的缓存和缓冲

内存和外存速度差距极大,读写文件时,要想办法弥补这个差距。CPU缓存技术就可以弥补该差距。

如果内存中的部分空间可以用来缓存常用的文件系统数据,就可以大大减少外存的访问量。这种技术就是页缓存(page cache),页缓存和CPU缓存非常类似。在读取外存中的文件时,文件的数据会先存在内存中未使用的页上。此后,如果需要读取相同的数据,那么CPU可以直接从内存中提取。当内存中可用于页缓存的空间填满时,计算机使用类似于LRU的策略选择牺牲者,用新的文件数据来替换掉缓存页。free命令可以产卡内存中页缓存空间的大小

$free

返回结果中的cached那一列说明了页缓存空间的大小。除了页缓存,内存还会在其他场景下使用缓存思想。比如,内存中会缓存文件系统的inode。读取文件的inode,是获取文件数据的第一步。对于频繁读写的文件,如果能在内存中缓存inode,文件的读写效率也会大大提高。

另一种思想:缓冲读写。例子:进程往一个文件中写入15个字符,进程可以每次从内存中拿一个字符写入外存。由于外存写入速度慢,那么在外存完成这个字符写入的过程中,进程都是闲置的。当然,进程可以进入阻塞状态,把CPU让给其他的进程。然而,进程状态切换需要付出代价。此外,外存写入字符前需要进行一些准备动作,比如找到写入快位置。分开写入15个字符,外存要重复15次准备动作。

缓冲的目的是在内存中收集多个待写入的字符,再一次性写入外存。一方面减少了进程切换的次数,另一方面,外存可以共用一套准备动作,从而减少开销。内存为进程打开的文件保留缓冲区(buffer),用于收集待写入文件的文本。缓冲区采用FIFO的策略。在刷新(flush)缓冲区时,缓冲区中存储的文本会按照先后次序一次性写入外存。操作系统的内核提供了缓冲区。因此,很多时候用write()系统写一个字符到文件,字符并没有真正存入文件。

刷新缓冲区的条件如下

  • 缓冲区填满了数据
  • 文件关闭
  • 进程终结
  • 文本中出现换行符
  • 该文件出现数据读取

内核中内置了缓冲读写功能。不过鉴于缓冲读写是一种简单而有效的策略,应用程序也可以自己在进程空间中安排缓冲区,来把多次操作合并成一次操作。事实上C标准库中的标准IO函数,就负责在读写过程中管理进程空间的缓冲区。IPC和网络通信也经常用到相似的缓冲策略,以提高通信效率。

Reference

1 Vamei,周梓昕著,树莓派开始玩转Linux,中国工信出版集团,电子工业出版社

你可能感兴趣的:(Linux分级存储, since 2020-11-20)