上一篇文章大概介绍了I/O的一些基本原理和技术,这篇我们主要介绍基于Linux系统的I/O的一些运行原理、监控方式。
虚拟内存
为了优化读取磁盘数据的效率,操作系统在内存和磁盘之间增加了一层缓存。从内存角度来看,各个进程的操作都需要内存空间,而当进程过多的时候,需要一种机制来管理内存,以区分各个进程间的内存隔离、异常处理等,这就引出了操作系统的虚拟内存,使用虚拟内存机制管理内存,使内存、磁盘、进程、内核软件、硬件异常之间完美融合,给进程提供一致的地址空间,从而简化内存管理。可以将虚拟内存看成是磁盘数据的一种缓存机制。另外,应用程序不需要关心虚拟内存的底层工作原理,虚拟内存有一套自己的逻辑来管理整个内存,应用程序的感知就像物理内存变大了一样。
缺页中断和缓存淘汰算法
虚拟内存是以页为单位进行缓存的,下图是查看系统默认页大小:
CPU和主存之间的L1、L2、L3的高速缓存叫做SRAM,虚拟内存的的缓存叫做DRAM,DRAM比SRAM慢大约10倍,磁盘比DRAM慢大约100,000多倍,所以DRAM不命中要比SRAM不命中效率低很多,因为DRAM不命中,要由磁盘来服务,而且磁盘的随机读取要比顺序读取又要慢很多。所以,DRAM缓存的算法对于整个系统效率提升非常关键。
下面我们大概说一下虚拟内存的工作原理:
Linux系统通过内存管理单元(MMU)和页表来处理DRAM功能,页表将虚拟页映射到物理页,每次映射时都要由页表读取,将虚拟地址转换成物理地址。
当程序在进行一些计算时,CPU会请求内存中存储的数据,若数据不存在内存中,就会报告一个缺页错误(Page Fault),用户进程就中断了,进程会从用户态切换到系统态,交由操作系统内核处理缺页错误,处理完缺页错误之后,最后交给用户态代码继续执行。
缺页中断分为主缺页中断(Major Page Fault)和次缺页中断(Minor Page Fault),请求数据时,若内存中已经有此时CPU正在请求的页帧,只不过还没有和进程建立映射关系,那么这就是一个次缺页错误。这时,让MMU把这个页帧分配给当前进程使用;而如果,此时内存中没有对应的页帧,则产生一个主缺页中断,这就需要CPU从已经打开的磁盘文件中读取相应的内容到物理内存,而后交由 MMU 建立这份页帧到页的映射关系。
一般来说,虚拟内存需要尽量避免主缺页中断,因为磁盘I/O将会影响虚拟内存的性能,而使用次缺页中断虚拟内存,只是内核级别的操作,效率较高。虚拟内存使用几种页面置换算法来淘汰页面缓存:
最优—OPT(Optimal):置换以后不再访问、或者将来最迟被访问的页面,缺页中断最低。无法实现此算法,作为衡量算法优劣的标准。
先进先出算法—FIFO(First In First out):按照进入内存的先后次序排列成队列,从队尾进入,从队首删除。效率不高,很少使用。
最近最少使用算法—LRU(Least recently used):根据局部性原理,最近被访问的,可能马上会被访问,较长时间未访问的将被淘汰。
最近最少使用算法—LFU(Least frequently used):根据最近一段时间使用次数统计,最少使用次数的,将来也会很少使用;与LRU不同的是,LFU是基于访问次数,LRU是基于访问时间的。
时钟算法—Clock(又叫最近未用算法-Not Recently Used, NRU):LRU算法的性能接近于OPT,但是实现起来比较困难,且开销大;FIFO算法实现简单,但性能差。所以操作系统的设计者尝试了很多算法,试图用比较小的开销接近LRU的性能,这类算法都是CLOCK算法的变体。
磁盘I/O
操作系统每一层都存在I/O,CPU和内存都存在I/O,磁盘也有I/O,网络传输也有I/O,内存和CPU的I/O处理可能会产生磁盘I/O,上一篇我们已经分析磁盘进行I/O处理时的总体响应时间,包括寻道时间、旋转延迟、数据传输。而体现磁盘性能的最主要的指标是IOPS(IO's Per Second),即每秒的输入输出量(或读写次数),IOPS通常是指单位时间内系统能处理的I/O请求数量,I/O请求通常为读或写数据操作请求。另外一个重要指标是吞吐量(KB per IO),指单位时间内可以成功传输的数据数量。
简而言之:
磁盘的 IOPS,也就是在一秒内,磁盘进行多少次 I/O 读写。
磁盘的吞吐量,也就是每秒磁盘 I/O 的流量,即磁盘写入加上读出的数据的大小。
上一篇也提到,很多开源框架,利用将随机I/O转化成连续I/O,以此提升性能。那么连续I/O比随机I/O效率高的原因是:在做连续 I/O 的时候,磁头几乎不用换道,或者换道的时间很短;而对于随机I/O,如果I/O请求很多的话,会导致磁头不停地换道,而这部分机械操作无法优化,将造成效率的极大降低。从性能上看,随机I/O更加关注IOPS,连续I/O更加关注吞吐量。
操作系统访问磁盘I/O有几种方式,上面所说的属于缓存I/O,也叫标准I/O,还包括直接I/O、内存映射。
在Linux的缓存I/O机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓冲区复制到应用程序的地址空间。在一定程度上分离了内核和用户空间, 保护了系统的运行安全; 可以减少读盘的次数, 从而提高性能。不过,另一方面,数据在传输过程中需要在应用程序地址空间和缓存之间进行多次数据拷贝操作,这些数据拷贝操作所带来的CPU以及内存开销是非常大的。
直接IO就是应用程序直接访问磁盘数据,而不经过内核缓冲区,这种一般使用在数据库中,这样做的目的是减少一次从内核缓冲区到用户程序缓存的数据复制,另外,不使用系统的缓存,而使用数据库自己的缓存,因为数据库可以根据数据的特点进行缓存。
内存映射是指将硬盘上文件的位置与进程逻辑地址空间中一块大小相同的区域一一对应,当要访问内存中一段数据时,转换为访问文件的某一段数据。这种方式的目的同样是减少数据在用户空间和内核空间之间的拷贝操作。当大量数据需要传输的时候,采用内存映射方式去访问文件会获得比较好的效率。
Linux性能分析
Linux系统使用ps -o来查看某一个进程号为24150的java进程的缺页错误
ps -o min_flt,maj_flt,cmd,args,uid,gid 24150
还可以使用watch和sort命令来观察产生缺页错误最多的几个进程
watch -n 1 "ps -eo min_flt,maj_flt,cmd,args,uid,gid,pid | sort -nrk1 | head -n 8"
通过iostat命令可以查看CPU利用率和磁盘tps、读、写等。
iostat -d -x -k 1
吞吐量计算公式:
I/O吞吐量 = IOPS * 平均I/O数据大小(size)
根据iostat看出,吞吐量等于每秒的读写数据量之和,上面是一台闲置的服务器,可以看到,tps也就是IOPS几乎没有I/O操作,每秒也没有读写数据操作,cpu为空闲状态,空闲率98.99%,设备使用率为0。
tps 每秒I/O数(即IOPS。磁盘连续读和连续写之和)
Blk_read/s 每秒从设备读取的数据大小,单位是block/s(块每秒)Blk_wrtn/s 每秒写入设备的数量,单位是block/sBlk_read 从磁盘读出的块的总数Blk_wrtn 写入磁盘的块的总数
kB_read/s 每秒从磁盘读取数据大小,单位KB/skB_wrtn/s 每秒写入磁盘的数据的大小,单位KB/skB_read 从磁盘读出的数据总数,单位KBkB_wrtn 写入磁盘的的数据总数,单位KB
rrqm/s 每秒合并到设备的读取请求数wrqm/s 每秒合并到设备的写请求数
r/s 每秒向磁盘发起的读操作数。w/s 每秒向磁盘发起的写操作数。rsec/s 每秒从设备读取的扇区数量。wsec/s 每秒向设备写入的扇区数量。
avgrq-sz I/O 请求的平均大小,以扇区为单位avgqu-sz 向设备发起的I/O 请求队列的的平均队列长度
await I/O 请求的平均等待时间,单位为毫秒。这个时间包括请求队列(这个概念很重要)消耗的时间和为每个请求服务的时间svctm I/O 请求的平均服务时间,单位为毫秒(这个数据不可信!)%util 处理 I/O 请求所占用的时间的百分比,即设备利用率。I/O请求期间CPU时间的百分比(即设备的带宽利用率)。当这个值接近100%时,表示磁盘I/O已经饱和
sar(System ActivityReporter系统活动情况报告)命令是比较全面的分析系统性能的工具。
内存分析:cat /proc/meminfo
cat /proc/meminfo
vmstat命令——虚拟内存统计(Virtual Meomory Statistics),提供了一种低开销的系统性能观察方式,可对操作系统的虚拟内存、进程、CPU活动进行监控。
free命令可以显示Linux系统中空闲的、已用的物理内存及swap内存,及被内核使用的buffer。