性能之巅节选

http://www.51pptmoban.com/sucai/3689.html
http://www.51pptmoban.com/sucai/3220.html
http://www.51pptmoban.com/sucai/2047.html

!/bin/bash

/usr/bin/cd /wls/apache/appsystems/tm_sbtps-jzyy-biz/apps

cpu

uptime
vmstat
procs:procs 中有 r 和 b 列,它报告进程统计信息。在上面的输出中,在运行队列(r)中有两个进程在等待 CPU 并有零个休眠进程(b)。通常,它不应该超过处理器(或核心)的数量,如果你发现异常,最好使用 top 命令进一步地排除故障。
r:等待运行的进程数。
b:休眠状态下的进程数。
memory: memory 下有报告内存统计的 swpd、free、buff 和 cache 列。你可以用 free -m 命令看到同样的信息。在上面的内存统计中,统计数据以千字节表示,这有点难以理解,最好添加 M 参数来看到以兆字节为单位的统计数据。
swpd:使用的虚拟内存量。
free:空闲内存量。
buff:用作缓冲区的内存量。
cache:用作高速缓存的内存量。
inact:非活动内存的数量。
active:活动内存量。
swap:swap 有 si 和 so 列,用于报告交换内存统计信息。你可以用 free -m 命令看到相同的信息。
si:从磁盘交换的内存量(换入,从 swap 移到实际内存的内存)。
so:交换到磁盘的内存量(换出,从实际内存移动到 swap 的内存)。
I/O:I/O 有 bi 和 bo 列,它以“块读取”和“块写入”的单位来报告每秒磁盘读取和写入的块的统计信息。如果你发现有巨大的 I/O 读写,最好使用 iotop 和 iostat 命令来查看。
bi:从块设备接收的块数。
bo:发送到块设备的块数。
system:system 有 in 和 cs 列,它报告每秒的系统操作。
in:每秒的系统中断数,包括时钟中断。
cs:系统为了处理所以任务而上下文切换的数量。
CPU:CPU 有 us、sy、id 和 wa 列,报告(所用的) CPU 资源占总 CPU 时间的百分比。如果你发现异常,最好使用 top 和 free 命令。
us:处理器在非内核程序消耗的时间。
sy:处理器在内核相关任务上消耗的时间。
id:处理器的空闲时间。
wa:处理器在等待IO操作完成以继续处理任务上的时间。
mpstat

参数  释义  从/proc/stat获得数据
CPU 处理器ID   
%usr    在internal时间段里,用户态的CPU时间(%),不包含 nice值为负进程    usr/total*100
%nice   在internal时间段里,nice值为负进程的CPU时间(%)    nice/total*100
%sys    在internal时间段里,核心时间(%)   system/total*100
%iowait 在internal时间段里,硬盘IO等待时间(%)   iowait/total*100
%irq    在internal时间段里,硬中断时间(%)  irq/total*100
%soft   在internal时间段里,软中断时间(%)  softirq/total*100
%steal  显示虚拟机管理器在服务另一个虚拟处理器时虚拟CPU处在非自愿等待下花费时间的百分比   steal/total*100
%guest  显示运行虚拟处理器时CPU花费时间的百分比   guest/total*100
%gnice      gnice/total*100
%idle   在internal时间段里,CPU除去等待磁盘IO操作外的因为任何原因而空闲的时间闲置时间(%)    idle/total*100

sar
sar 的命令格式为:
sar [ -A ] [ -b ] [ -B ] [ -c ] [ -d ] [ -i interval ] [ -p ] [ -q ]
[ -r ] [ -R ] [ -t ] [ -u ] [ -v ] [ -V ] [ -w ] [ -W ] [ -y ]
[ -n { DEV | EDEV | NFS | NFSD | SOCK | ALL } ]
[ -x { pid | SELF | ALL } ] [ -X { pid | SELF | ALL } ]
[ -I { irq | SUM | ALL | XALL } ] [ -P { cpu | ALL } ]
[ -o [ filename ] | -f [ filename ] ]
[ -s [ hh:mm:ss ] ] [ -e [ hh:mm:ss ] ]
[ interval [ count ] ]
其中:
interval : 为取样时间间隔
count : 为输出次数,若省略此项,默认值为 1
常用选项:
选项 说明
-A 等价于 -bBcdqrRuvwWy -I SUM -I XALL -n ALL -P ALL
-b 显示I/O和传送速率的统计信息
-B 输出内存页面的统计信息
-c 输出进程统计信息,每秒创建的进程数
-d 输出每一个块设备的活动信息
-i interval 指定间隔时长,单位为秒
-p 显示友好设备名字,以方便查看,也可以和-d 和-n 参数结合使用,比如 -dp 或-np
-q 输出进程队列长度和平均负载状态统计信息
-r 输出内存和交换空间的统计信息
-R 输出内存页面的统计信息
-t 读取 /var/log/sa/saDD 的数据时显示其中记录的原始时间,如果没有这个参数使用用户的本地时间
-u 输出CPU使用情况的统计信息
-v 输出inode、文件和其他内核表的统计信息
-V 输出版本号信息
-w 输出系统交换活动信息
-W 输出系统交换的统计信息
-y 输出TTY设备的活动信息
-n {DEV|EDEV|NFS|NFSD|SOCK|ALL} 分析输出网络设备状态统计信息。
DEV 报告网络设备的统计信息
EDEV 报告网络设备的错误统计信息
NFS 报告 NFS 客户端的活动统计信息
NFSD 报告 NFS 服务器的活动统计信息
SOCK 报告网络套接字(sockets)的使用统计信息
ALL 报告所有类型的网络活动统计信息
-x {pid|SELF|ALL} 输出指定进程的统计信息。
pid 用 pid 指定特定的进程
SELF 表示 sar 自身
ALL 表示所有进程
-X {pid|SELF|ALL} 输出指定进程的子进程的统计信息
-I {irq|SUM|ALL|XALL} 输出指定中断的统计信息。
irq 指定中断号
SUM 指定输出每秒接收到的中断总数
ALL 指定输出前16个中断
XALL 指定输出全部的中断信息
-P {cpu|ALL} 输出指定 CPU 的统计信息
-o filename 将输出信息保存到文件 filename
-f filename 从文件 filename 读取数据信息。filename 是使用-o 选项时生成的文件。
-s hh:mm:ss 指定输出统计数据的起始时间
-e hh:mm:ss 指定输出统计数据的截至时间,默认为18:00:00
sar 使用举例

怀疑CPU存在瓶颈,可用 sar -u 和 sar -q 等来查看

怀疑内存存在瓶颈,可用 sar -B、sar -r 和 sar -W 等来查看

怀疑I/O存在瓶颈,可用 sar -b、sar -u 和 sar -d 等来查看

ps
ps命令常用用法(方便查看系统进程)

1)ps a 显示现行终端机下的所有程序,包括其他用户的程序。
2)ps -A 显示所有进程。
3)ps c 列出程序时,显示每个程序真正的指令名称,而不包含路径,参数或常驻服务的标示。
4)ps -e 此参数的效果和指定'A'参数相同。
5)ps e 列出程序时,显示每个程序所使用的环境变量。
6)ps f 用ASCII字符显示树状结构,表达程序间的相互关系。
7)ps -H 显示树状结构,表示程序间的相互关系。
8)ps -N 显示所有的程序,除了执行ps指令终端机下的程序之外。
9)ps s 采用程序信号的格式显示程序状况。
10)ps S 列出程序时,包括已中断的子程序资料。
11)ps -t<终端机编号>  指定终端机编号,并列出属于该终端机的程序的状况。
12)ps u  以用户为主的格式来显示程序状况。
13)ps x  显示所有程序,不以终端机来区分。

Head头

USER    用户名
UID    用户ID(User ID)
PID    进程ID(Process ID)
PPID    父进程的进程ID(Parent Process id)
SID    会话ID(Session id)
%CPU    进程的cpu占用率
%MEM    进程的内存占用率
VSZ    进程所使用的虚存的大小(Virtual Size)
RSS    进程使用的驻留集大小或者是实际内存的大小,Kbytes字节。
TTY    与进程关联的终端(tty)
STAT    进程的状态:进程状态使用字符表示的(STAT的状态码)
R 运行    Runnable (on run queue)            正在运行或在运行队列中等待。
S 睡眠    Sleeping                休眠中, 受阻, 在等待某个条件的形成或接受到信号。
I 空闲    Idle
Z 僵死    Zombie(a defunct process)        进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放。
D 不可中断    Uninterruptible sleep (ususally IO)    收到信号不唤醒和不可运行, 进程必须等待直到有中断发生。
T 终止    Terminate                进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行。
P 等待交换页
W 无驻留页    has no resident pages        没有足够的记忆体分页可分配。
X 死掉的进程
< 高优先级进程                    高优先序的进程
N 低优先    级进程                    低优先序的进程
L 内存锁页    Lock                有记忆体分页分配并缩在记忆体内
s 进程的领导者(在它之下有子进程);
l 多进程的(使用 CLONE_THREAD, 类似 NPTL pthreads)
+ 位于后台的进程组 
START    进程启动时间和日期
TIME    进程使用的总cpu时间
COMMAND    正在执行的命令行命令
NI    优先级(Nice)
PRI    进程优先级编号(Priority)
WCHAN    进程正在睡眠的内核函数名称;该函数的名称是从/root/system.map文件中获得的。
FLAGS    与进程相关的数字标识

############################################################

例子:
查看当前系统进程的uid,pid,stat,pri, 以uid号排序.
ps -eo pid,stat,pri,uid –sort uid

查看当前系统进程的user,pid,stat,rss,args, 以rss排序.
ps -eo user,pid,stat,rss,args –sort rss

top

top   //每隔5秒显式所有进程的资源占用情况
top -d 2  //每隔2秒显式所有进程的资源占用情况
top -c  //每隔5秒显式进程的资源占用情况,并显示进程的命令行参数(默认只有进程名)
top -p 12345 -p 6789//每隔5秒显示pid是12345和pid是6789的两个进程的资源占用情况
top -d 2 -c -p 123456 //每隔2秒显示pid是12345的进程的资源使用情况,并显式该进程启动的命令行参数
total 进程总数
running 正在运行的进程数
sleeping 睡眠的进程数
stopped 停止的进程数
zombie 僵尸进程数
Cpu(s): 
0.3% us 用户空间占用CPU百分比
1.0% sy 内核空间占用CPU百分比
0.0% ni 用户进程空间内改变过优先级的进程占用CPU百分比
98.7% id 空闲CPU百分比
0.0% wa 等待输入输出的CPU时间百分比
0.0%hi:硬件CPU中断占用百分比
0.0%si:软中断占用百分比
0.0%st:虚拟机占用百分比
atop


序号  列名    含义
a    PID     进程id
b    PPID    父进程id
c    RUSER   Real user name
d    UID     进程所有者的用户id
e    USER    进程所有者的用户名
f    GROUP   进程所有者的组名
g    TTY     启动进程的终端名。不是从终端启动的进程则显示为 ?
h    PR      优先级
i    NI      nice值。负值表示高优先级,正值表示低优先级
j    P       最后使用的CPU,仅在多CPU环境下有意义
k    %CPU    上次更新到现在的CPU时间占用百分比
l    TIME    进程使用的CPU时间总计,单位秒
m    TIME+   进程使用的CPU时间总计,单位1/100秒
n    %MEM    进程使用的物理内存百分比
o    VIRT    进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
p    SWAP    进程使用的虚拟内存中,被换出的大小,单位kb。
q    RES     进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
r    CODE    可执行代码占用的物理内存大小,单位kb
s    DATA    可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb
t    SHR     共享内存大小,单位kb
u    nFLT    页面错误次数
v    nDRT    最后一次写入到现在,被修改过的页面数。
w    S       进程状态(D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)
x    COMMAND 命令名/命令行
y    WCHAN   若该进程在睡眠,则显示睡眠中的系统函数名
z    Flags   任务标志,参考 sched.h

prstat

pidstat
linux:~ # pidstat -r -p 13084 1
Linux 2.6.32.12-0.7-default (linux) 06/18/12 x86_64

15:08:18          PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
15:08:19        13084 133835.00      0.00 15720284 15716896  96.26  mmmm
15:08:20        13084  35807.00      0.00 15863504 15849756  97.07  mmmm
15:08:21        13084  19273.87      0.00 15949040 15792944  96.72  mmmm

minflt/s: 每秒次缺页错误次数(minor page faults),次缺页错误次数意即虚拟内存地址映射成物理内存地址产生的page fault次数
majflt/s: 每秒主缺页错误次数(major page faults),当虚拟内存地址映射成物理内存地址时,相应的page在swap中,这样的page fault为major page fault,一般在内存使用紧张时产生
VSZ:      该进程使用的虚拟内存(以kB为单位)
RSS:      该进程使用的物理内存(以kB为单位)
%MEM:     该进程使用内存的百分比
Command:  拉起进程对应的命令


linux:~ # pidstat -d 1 2
Linux 2.6.32.12-0.7-default (linux)             06/18/12        _x86_64_

17:11:36          PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
17:11:37        14579 124988.24      0.00      0.00  dd

17:11:37          PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
17:11:38        14579 105441.58      0.00      0.00  dd
kB_rd/s: 每秒进程从磁盘读取的数据量(以kB为单位)
kB_wr/s: 每秒进程向磁盘写的数据量(以kB为单位)
Command: 拉起进程对应的命令
linux:~ # pidstat -r -p 1 1
Linux 2.6.32.12-0.7-default (linux)             06/18/12        _x86_64_

18:26:17          PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
18:26:18            1      0.00      0.00   10380    640   0.00  init
18:26:19            1      0.00      0.00   10380    640   0.00  init

pidstat -u 1

pidstat -r 1

pidstat -d 1

time
使用方式: time [options] COMMAND [arguments]
说明: time 指令的用途,在于量测特定指令执行时所需消耗的时间及系统资源等资讯。例如 CPU 时间、记忆体、输入输出等等。需要特别注意的是,部分资讯在 Linux 上显示不出来。这是因为在 Linux 上部分资源的分配函式与 time 指令所预设的方式并不相同,以致于 time 指令无法取得这些资料。
参数:
-o or –output=FILE
设定结果输出档。这个选项会将 time 的输出写入 所指定的档案中。如果档案已经存在,系统将覆写其内容。
-a or –append
配合 -o 使用,会将结果写到档案的末端,而不会覆盖掉原来的内容。
-f FORMAT or –format=FORMAT
以 FORMAT 字串设定显示方式。当这个选项没有被设定的时候,会用系统预设的格式。不过你可以用环境变数 time 来设定这个格式,如此一来就不必每次登入系统都要设定一次。
一般设定上,你可以用
’ ’
表示跳栏,或者是用


表示换行。每一项资料要用 % 做为前导。如果要在字串中使用百分比符号,就用 。(学过 C 语言的人大概会觉得很熟悉)
time 指令可以显示的资源有四大项,分别是:
Time resources
Memory resources
IO resources
Command info
详细的内容如下:
Time Resources
E 执行指令所花费的时间,格式是:[hour]:minute:second。请注意这个数字并不代表实际的 CPU 时间。
e 执行指令所花费的时间,单位是秒。请注意这个数字并不代表实际的 CPU 时间。
S 指令执行时在核心模式(kernel mode)所花费的时间,单位是秒。
U 指令执行时在使用者模式(user mode)所花费的时间,单位是秒。
P 执行指令时 CPU 的占用比例。其实这个数字就是核心模式加上使用者模式的 CPU 时间除以总时间。
Memory Resources
M 执行时所占用的实体记忆体的最大值。单位是 KB
t 执行时所占用的实体记忆体的平均值,单位是 KB
K 执行程序所占用的记忆体总量(stack+data+text)的平均大小,单位是 KB
D 执行程序的自有资料区(unshared data area)的平均大小,单位是 KB
p 执行程序的自有堆叠(unshared stack)的平均大小,单位是 KB
X 执行程序间共享内容(shared text)的平均值,单位是 KB
Z 系统记忆体页的大小,单位是 byte。对同一个系统来说这是个常数
IO Resources
F 此程序的主要记忆体页错误发生次数。所谓的主要记忆体页错误是指某一记忆体页已经置换到置换档(swap file)中,而且已经分配给其他程序。此时该页的内容必须从置换档里再读出来。
R 此程序的次要记忆体页错误发生次数。所谓的次要记忆体页错误是指某一记忆体页虽然已经置换到置换档中,但尚未分配给其他程序。此时该页的内容并未被破坏,不必从置换档里读出来
W 此程序被交换到置换档的次数
c 此程序被强迫中断(像是分配到的 CPU 时间耗尽)的次数
w 此程序自愿中断(像是在等待某一个 I/O 执行完毕,像是磁碟读取等等)的次数
I 此程序所输入的档案数
O 此程序所输出的档案数
r 此程序所收到的 Socket Message
s 此程序所送出的 Socket Message
k 此程序所收到的信号 ( Signal )数量
Command Info
C 执行时的参数以及指令名称
x 指令的结束代码 ( Exit Status )
-p or –portability
这个选项会自动把显示格式设定成为:
real %e
user %U
sys %S
这么做的目的是为了与 POSIX 规格相容。
-v or –verbose
这个选项会把所有程序中用到的资源通通列出来,不但如一般英文语句,还有说明。对不想花时间去熟习格式设定或是刚刚开始接触这个指令的人相当有用。

ptime
dtrace
systemtap
perf
cpustat

usr - 用户模式运行时间占 CPU 百分比的 min/avg/max 值。
sys - 系统模式运行时间占 CPU 百分比的 min/avg/max 值。
nice - 用户模式低优先级运行时间占 CPU 百分比的 min/avg/max 值。
idle - 用户模式空闲时间占 CPU 百分比的 min/avg/max 值。
iowait - 等待磁盘 IO 的 min/avg/max 延迟时间。
prun - 处于可运行状态的 min/avg/max 进程数量(同“平均负载”一样)。
pblock - 被磁盘 IO 阻塞的 min/avg/max 进程数量。
pstat - 在本次汇总间隔里启动的进程/线程数目。
同样还是上面的输出,对于一个进程,不同列的意思分别是:

name - 从 /proc/pid/stat 或 /proc/pid/cmdline 获取的进程名称。
pid - 进程 ID,也被用作 “tgid” (线程组 ID)。
min - 该 pid 的用户模式+系统模式时间的最小样本,取自 /proc/pid/stat。比率是 CPU 的百分比。
max - 该 pid 的用户模式+系统模式时间的最大样本,取自 /proc/pid/stat。
usr - 在汇总期间该 pid 的平均用户模式运行时间,取自 /proc/pid/stat。
sys - 在汇总期间该 pid 的平均系统模式运行时间,取自 /proc/pid/stat。
nice - 表示该进程的当前 “nice” 值,取自 /proc/pid/stat。值越高表示越好(nicer)。
runq - 进程和它所有线程可运行但等待运行的时间,通过 netlink 取自 taskstats。比率是 CPU 的百分比。
iow - 进程和它所有线程被磁盘 IO 阻塞的时间,通过 netlink 取自 taskstats。比率是 CPU 的百分比,对整个汇总间隔平均。
swap - 进程和它所有线程等待被换入(swap in)的时间,通过 netlink 取自 taskstats。Scale 是 CPU 的百分比,对整个汇总间隔平均。
vcx 和 icx - 在汇总间隔期间进程和它的所有线程自动上下文切换总的次数,通过 netlink 取自 taskstats。
rss - 从 /proc/pid/stat 获取的当前 RSS 值。它是指该进程正在使用的内存数量。
ctime - 在汇总间隔期间等待子进程退出的用户模式+系统模式 CPU 时间总和,取自 /proc/pid/stat。 注意长时间运行的子进程可能导致混淆这个值,因为只有在子进程退出后才会报告时间。但是,这对于计算高频 cron 任务以及 CPU 时间经常被多个子进程使用的健康检查非常有帮助。
thrd - 汇总间隔最后线程的数目,取自 /proc/pid/stat。
sam - 在这个汇总间隔期间该进程的样本数目。最近启动或退出的进程可能看起来比汇总间隔的样本数目少。

内存

vmstat
sar
slabtop
::kmstat
ps
top
prstat
pmap
dtrace
systemtap

文件

vfsstat
fsstat
strace truss
dtrace
sarystemtap
latencytop
free
top
vmstat
sar
slabtop
mdb::kmastat
fcachestat
/proc/meninfo
mdb::memstat
kstat

磁盘

iostat
sar
pidstat
dtrace
systemtap
perf
iotop
iosnoop
blktrace
megacli
smartctl

网络

netstat
sar
ifconfig
ip
nicstat
dladm
ping
traceroute
pathchar
tapdump
snoop
wireshark
dtrace
systemtap
perf

控制器是cpu的心脏,运行指令预录取、解码、管理执行以及存储结果
P-cache 预取缓存
W-cache 写缓存
时钟:CPU时钟信号生成器
时间戳计数器:为了高精度时间,由时钟递增
微代码ROM:快速把指令转化成电路信号
温度传感器:温度监控
网络接口:如果集成的芯片里

多种硬件缓存往往包含在(包括了片上、晶粒内置、嵌入或者集成)处理器内或者与处理器放在一起。这样通过更快类型的内存缓存了读并缓冲了写

数据缓存 DATA_MEM_REFS 从所有类型内存的读取。对所有类型内存的存储。每一部分被分别计数不包括IO访问或者其他非内存访问
取指令单元 DCU_MISS_OUSTANDING DCU未命中期间的周期权重数,在任意时间每次递增缓存未命中数。公考虑可缓存的读请求
取指令单 IFU_IFETCH 取指令数,可缓存和不可缓存的,包括UC
二级缓存 L2_IFETCH L2取指令数
浮点单元 FLOPS 引退的可计算浮点数操作数
外部总线逻辑 BUS_SNOOP_STALL 总线窥探停滞期间的时钟周期数
指令解码和引退 INST_RETIRED 引退的指令数
中断 HW_INT_RX 收到的硬中断数
分支 BR_MISS_PRED_RETIRED 预测错误分支引退数
停滞 RESOURCE_STALLS 当有与资源相关的停滞时,每个周期递增一次
时钟 CPU_CLK_UNHALTED 处理器未停止期间的周期数

在linux上,分时通过系统时钟中断调用scheduler_tick()实现。这个函数调用调试器类函数管理优先级和称为时间片的CPU时间单位的到期事件。当线程状态变成可运行后,就触发了抢占,调度类函数check_preempt_curr()被调用。线程的切换由__schedule()管理,后者通过pick_next_task()选择最主优先级的线程运行。负载均衡由load_blance()函数负责执行。
高度类管理了可运行线程的行为,特别是它们的优先级,还有CPU时间是否分片,以及这些时间片的长度又称为时间量子。通过高度策略还可以施加其他的控制,在一个高度器内进行选择,控制同一个优先级线程间的高度。

linux高度器类如下:
RT:为实时类负载提供固定的高优先级。内核支持用户和内核级别的抢占,允许RT任务以短延时颁发。
O(1)高度器在Linux2.6作为默认用户进程分时高度器引入。名字来源于算法复杂度,先前的调度器包含了一个遍历所有任务的函数,算法复杂度,这样扩展性就成了问题。相对于CPU消耗型线程,它方式器动态地提高IO消耗型线程的优行级,以降低交互和IO负载的延时
CFS:linux2.6.23引入了完成公平高度作为默认用户进程分时高度器。这个高度器使用红黑树取代了传统运行队列来管理任务。以任务的CPU时间作为键值。这样使得CPU的少量消费者相对于CPU消耗型负载更容易被找到,提高了交互和IO消耗型负载的性能

用户级进程可以通过调度sched_setscheduler()设置高度器策略以调整调度类的行业。RT类支持SCHED_RR 和SCHED_FIFO 策略,而CFS类支持SCHED_NORMAL 和SCHED_BATCH

RR:SCHED_RR是轮转调度。一量一个线程用完了它的时间片,它就被挪到自己优先级运行队列的尾部,这样同等优先级的其他线程可以运行。

FIFO:SCHED_FIFO是一个先进先出调度,一直运动队列头的络直到它自愿退出,或者一个更高优先级的线程抵达。线程会一直运行,即便在运动队列当中存在相同优先级的其他线程。

NORMAL:SCHED_NORMAL是一种分时调度,是用户进程的默认策略。高度器根据高度类动态调整优先级。对于,时间片长度根据静态优先级设置,即更高优先级的工作分配到更长的时间,对于CFS,时间片是动态的。

BATCH:SCHED_BATCH和SCHED_NORMAL类似,但期望线程是CPU消耗型的,这样就不会打断其他IO消耗型交互工作。

NUMA系统上的性能可以通过使用内核感知NUMA而得到 极大提高,因为这样它可以做出更好的高度和内存分配决定。它可以自动检测并创建酝化的CPU和内存资源组,并按照反馈NUMA架构的拓扑结构组织起来。这种结构可以预估任意的内存访问开销。

谁测量、为什么测量、测量什么、如何测量
剖析:剖析构建了研究目标的一幅图像。CPU用量可以通过定期取样CPU的状态进行剖析,按以下步骤进行:
1、选择需要采集的剖析数据以及频率
2、开始每隔一定时间进行取样
3、等待兴趣事件的发生
4、停止取样并收集取样数据
5、处理数据

周期分析:
通过使用CPU性能计数器CPC,我们能够周期级别理解CPU使用率。这可能会展示消耗在一级、二级或者三级缓存未命中,内存IO以及资料IO上的停滞周期,亦或是花浮点操作及其他活动上周期。命运这项信息后,可以通过调整编译器选项或者修改代码 以取得性能收益。

    使用率应该对每个CPU分别监控,以发现线程的扩展性问题。对于实现了CPU限制或者配额的环境,例如云计算环境,还要记录相对于这些限制的CPU用量。
监控CPU用量的一个挑战在于挑选合适的测量和归档时间间隔。

静态性能调优:
1、有多少CPU可用?是核吗?还是硬件线程?
2、CPU缓存的大小是多少?是共享的吗?
3、CPU的架构是单处理还是多处理器?
4、CPU的时钟频率是多少?是动态吗?这些动态特性在BIOS启用了吗?
5、BIOS里启用或禁用了其他什么CPU相关的特性?
6、这款型号的处理器有什么性能问题?出现在处理器勘误一上了吗?
7、这个BIOS固件版本有什么性能问题吗?
8、有软件的CPU使用限制吗?是什么?

CPU绑定:
另一个CPU性能调优的方法是把进程和线程绑定到单个 CPU或一组CPU上。这可以增加进程的CPU缓存温度,提高它的内存IO性能。对NUMA系统这可以提高 内存的本地性,同样也提高性能。
这个方法以下两个方式实现:
1、进程绑定:配置一个进程只跑在单个CPU上,或者预定义CPU组中的一个;
2、独占CPU组:分出一组CPU,让这些CPU只能指定的进程。这可以更大地提升CPU缓存效率,因为当前进程空闲时,其他进程不能使用CPU,保证了缓存的温度。

微型基准测试:
CPU指令:整数运算、浮点操作、内存加载和存储、分支和其他指令。
内存访问:调查不同CPU缓存的延时和主存吞吐量
高级语言:类似CPU指令测试,不过使用高级解释或者编译语言编写
操作系统操作:测试CPU消耗型系统和系统调用函数,例如getpid()和进程创建

uptime 平均负载
vmstat  包括系统范围的CPU平均负载
mpstat 单个CPU统计信息
sar  历史统计信息
ps 进程状态
top 监控每个进程线程CPU用量
pidstat  每个进程线程CPU用量分解
time 给一个命令计时,带CPU用量分析
Dtrace、perf  CPU剖析和跟踪
perf CPU性能计数器分析

ST: 偷取,CPU在虚拟化的环境下在其他租户上的开销
steal:耗费在服务其他租户的时间
guest:花在访客虚拟机的时间

top 顶部是系统范围的统计信息,而下面则是进程、任务的列表,默认按照CPU用量排序。
由于top(1)对/proc拍快照,它会错过一些寿命较短的进程,这些进程在拍快照之前退出。这在软件构建时经常出现,此时CPU被许多构建过程中命的工具牢牢占据。Liunx上有一个top1的变种atop1,使用进程核算技术以捕捉短寿命进程的存在,然后把这些进程加入显示
线程微状态核算统计可通过

pidstat
Linux上的pidstat1工具按进程或线程打印CPU用量,包括用户态和系统态时间的分解。

DTrace
DTrace可以用来剖析用户级和内核级代码的CPU用量,也能够跟踪函数执行、CPU交叉调用、中断和内核调度器。这些功能支持了负载特征分析、剖析、下钻分析和延时分析等活动

单行程序
以997Hz频率取样内核栈:
dtrace -n ‘profile-997 /arg0/ {@[stack()] = count();}’

以997Hz频率取样内核栈,仅输出最频繁的10:
dtrace -n 'profile-997 /arg0/  {@[stack()] = count();} END {trunc(@,10); }'

以997Hz频率取样内核栈,每个栈只输出5帧:
dtrace -n 'profile-997 /arg0/  {@[stack(5)] = count();} }'

以997Hz频率取样在CPU上运行的函数:
dtrace -n 'profile-997 /arg0/  {@[func(arg0)] = count();} }'

以997Hz频率取样在CPU上运行的模块:
dtrace -n 'profile-997 /arg0/  {@[mod(arg0)] = count();} }'

perf已经演化成为一套剖析和跟踪工具,现名为linux性能事件的。每个工具分析作为一个子命令,例如,perf stat执行stat命令,提供基于CPC的统计信息。这些命令的列表可能在USAGE消息中找到

annotate 读取perf.data并显示注释过后代码
diff 赢取两个data文件并显示两份剖析信息之间的差异
evlist 列出一个data文件里的事件名称
inject 过滤以加强事件流,在其中加入额外的信息
kmem 跟踪、测量内核内存slab属性的工具
kvn 跟踪、测量kvm客户机操作系统的工具
list 列出所有的符号事件类型
lock 分析锁事件
probe 定义新的动态跟踪点
record 运行一个命令,并报剖析信息记录在data中
report 读取data并显示剖析信息
sched 跟踪、测量高度器属性的工具
script 读取data并显示跟踪输出
stat 运行一个命令并收集性能计数器统计信息
timechart 可视化某一个负载期间系统总体性能的工具
top 系统剖析工具

系统剖析
perf可以用来剖析CPU调用路径,对CPU时间如何消耗在内核和用户空间进行概括总结。这项工作由record命令完成,该命令以一写间隔进行取样,并导出一一个data文件,然后使用report命令查看文件

在下面的例子里,所有的CPU -a 以997Hz的频率 -F 997 对调用栈 -g 取样10s sleep 10 。选项--stdio 用来打印所有的输出,而非采用默认的交互模式操作

pref record -a -g - F 997 sheep 10

这些取样计数以百分数输出,展示了CPU时间去处。
这些内核和进程符号只在它们的调试信息文件存在的情况下可用,否则只显示十六进制地址

进程剖析
除了剖析系统里的所有CPU,我们也可以对单个进程进行剖析。下面的命令执行了command并创建文件perf.data
perf record -gcommand
和之前一样, perf需要调试信息文件,这样在查看报告时可以进行符号转译

调度器延时器
sched命令记录并报告调度器统计信息
perf sched record sheep 10
上面显示了跟踪时期平均和最大的高度器延时
调度器事件较为频繁,因此此类跟踪会导致 CPU和存储的开销。本例中data文件即为跟踪10s的产物,大小达到了1.7GB。输出中的INFO行表示有些事件被丢弃了。这突显Dtrace在内核过滤和聚合模型的优势:它可以在跟踪时汇总数据并只报汇总结果传递给用户空间,把开销最小化。
stat
stat 命令基于CPC为CPU周期行为提供了一个概要总结。下面的例子里它启动了gzip命令
perf stat gzip file1

进程绑定:
一个进程可以绑定在一个或者多个CPU上,这样可以通过提高缓存温度和内存本地性来提高性能
在Linux上,是通过taskset命令来实现的,这个方法可以使用CPU掩码或者范围设置CPU关联性
task -pc 7-10 10790
上面的设置限定了PID为10790的进程只能跑在CPU7到CPU10之间。

独占CPU组
Linux提供了CPU组,允许编组CPU并为其分配进程。这和进程绑定类似,可以提高性能,但还可以通过使得CPU组独占–不允许其他进程使用–而进一步提高性能。这种权衡另一方面减少了系统其他部分的可用CPU数量。

资源控制
除了把进程和整个CPU关联以外,现代操作系统还对CPU用量分配提供了细粒度资源控制。
Linux上的控制组cgroups,通过进程或者进程组控制了资源用量。CPU用量可以使用份额进行控制,而CFS调度器允许对每段时间内分配的CPU微秒数周期,设置固定上限(CPU带宽)

处理器选项
处理器通常提供一些设置,以启动、禁用和调优处理器级别的特性。这些选项通常在启动时通过BIOS设置菜单管理。

内存:
系统主存存储应运程序和内核指令,包括它们的工作数据,以及文件系统缓存。许多系统中,存放这些数据的二级存储是主要的存储设备磁盘它们的处理速度 比内存低几个数量级。

需要考察的影响系统性能的因素包括分配和释放内存、复制内存、以及管理内存地址空间映射的CPU开销。对于多路处理器架构的系统,由于连接到本地CPU的内存相对于远程CPU访问时更低,内存本地性也是一个影响因素

主存:也称为物理内存,描述了计算的调整数据存储区域,通常是动态随机访问内存DRAM
虚拟内存:一个抽象的请在概念,它(几乎是)无限的和竞争性的。虚拟内存不是真实的内存。
常驻内存:当前处于主存中的内存。
匿名内存:无文件系统位置或者路径名的内存。它包括进程地址空间的工作数据,称作堆。
地址空间:内存上下文。每个进程和内核都有对应的虚拟地址空间。
段:标记为特殊用途的一块内存区域,例如用来存储可执行或者可写的页。
OOM:内存耗尽,内核检测 到的可用内存低。
页: 操作系统和CPU使用的内存单位。它一直以来是4KB或者8KB。现代的处理器允许多种页大小以支持更大的页面尺寸。
缺页:无效的内存访问。使用按需虚拟内存的时,这是正常事件。
换页:在请在与存储设置间交换页。

交换:源自UNIX,指将整个进程从主存转移到交换设备。它可以是存储设备的一块空间,也称为物理交换设备,或者是文件系统文件,称作交换文件。部分工具用交换这个术语指虚拟内存

进程的地址空间由虚拟内存子系统映射到主内存和物理交换设备。内核会按需在它们之间移动内存页,这个过程称作交换。

文件系统换页:
文件系统换页由读写位于内存中的映射文件页引发。对于使用文件内存映射mmap的应用程序和使用了页缓存的文件系统,这是正常的行为。这也被称作好的换页。
有需要时,内核可以调出一些页释放内存。这睦说法变得有点复杂:如果一个文件系统页在主契中修改过,页面换出要求将该页写回磁盘。相反,如果文件系统页没胡修改,因为磁盘已经存在一份副本,页面掏出仅仅释放这些内存以便立即重用。因术语页面换出指 个页被移出内存--这可能包括或者不包括写一个存储设置

匿名换页:
匿名换页牵涉进程的私有数据:进程堆和栈。被称为匿名是由于它在操作系统中缺乏有名字的地址(例如,没有文件系统路径)。匿名页面换出要求迁移数据到物理交换设备或者交换文件。
匿名换页拖累性能,因此被称为“坏的”换页。当应用程序访问被调出的页时,会被读页的磁盘IO阻塞。这就是匿名页面换入,它给应用程序带来同步延时。匿名页面换出可能不会直接影响 应用程序性能,因为它由内核异步执行。
性能在没有匿名换页的情况下处理最难状态。要做到这一点,可以通过配置应用程序常驻于内存并且监控页面扫描、内存使用率和匿名换页,来确保不存在内存短缺的迹象。

交换:
交换是在请在与物理交换设备或者交换文件之间移动整个进程。
交换出一个进程,要求进程的所有私有数据必须被定稿交换设备,包括线程结构和进程堆。源文件系统而且没有修改的数据可以被丢弃,需要的时候再从原来的位置读取。
因为进程和一小部分元数据总是常驻于内核内存中,内核仍能知道已交换出的进程。至于要将哪个进程交换回来,内核会考虑线程优先级、磁盘等待时间以及进程的大小。长期等待和较小的进程享有更高的优先级。

文件系统缓存占用
系统启动之后内存的占用增加是正常的,因为操作系统会将可有内存用于文件系统缓存以提高性能。该原则是:如果有可用的主内存,就有效地使用它。

使用率和饱和度
主内存的使用率可由已占用的内存除以总内存得出。文件系统缓存占用的内存可当作未使用,因为它可以被应用程序重用。
对内存的需要超过了请在的被称作主存饱和。这时操作系统会使用换页、交换或者在Linux中用OOM终结者来释放内存。以上任一操作都标志着请在饱和

分配器
当虚拟内存处理多任务物理内存时,在虚拟地址空间中实际分配和内存堆放通常由分配器来处理。用户态库或者内核程序向程序员提供简单的内存使用接口(例如malloc()、free())。
当分配器对性能有显著的影响,一个系统通常会提供多个可选择的用户态分配器库。分配器可以利用包括线程级别对象内的技术以提高性能,但是如果分配变碎并且损耗变高,它们也会损害性能。

字长
处理器可能会支持多种字长,例如32位和64位,这样两种应用程序可以运行。地址空间受限于字长的寻址空间,因此32位的地址空间放下来需要4GB以上的应用程序,必须用64位或者更大的字长来编译。

硬件
内存硬件包括主存、总线、CPU缓存和MMU内存管理单元

主存
目前常见的请在类型是动态随机存取内存DRAM。这是一钟易失性的内存–它存储的内容在断电时会丢失。由于每个比特仅由两个逻辑零件组成:一个电容和一个晶体管,DRAM能提供高容量的存储。其中电容需要定期更新以保持其电荷。

延时
主内存的访问时间可以用CAS列地址控制器计量:从发送需要读取的地址给一个内存模块,到数据可能被读取之间的时间。这个数值取决于内存的类型,对于内存IO传输,内存总线为了传输一个缓存行会发生多次此类延时。CPU和MMU读取新数据时也可能涉及其他延时。

主存架构
通过共享系统总线,每个CPU访问所有内存有均匀的访问存延时。如果上面运行的是单个操作系统实例并可以在所有处理器上运行时,又称为对称多处理器架构

总线
物理上主存如何连接系统取决于主存架构。实际的实现可能会涉及额外的CPU与内存之间的控制器和总线。可能访问方式如下。
共享系统总线:单个或多个处理器,通过一个共享的系统总线、一个内存桥控制器以及内存总线。
直连 互联
DDR SDRAM(双倍速率同步动态随机访问内存)
对于任何架构,内存总线速度常常取决于处理器和主板支持的内存接口标准。

多通道
系统架构可能支持并行使用多个内存总线来增加带宽。觉见的位数为双、三或者四通道 。

CPU缓存
处理器通常会在芯片中包含硬件缓存以提高内存访问性能。这些缓存可能包括如下级别,速度递减和大小递增。
L1:通常分为指令缓存和数据缓存。
L2:同时缓存指令和数据
L3:更大一级的缓存。

一级缓存通常按虚拟内存地址空间寻址,二级及以上按物理内存地址寻址,具体取决于处理器。

MMU内存管理单元负责虚拟到物理地址的转换。它按页做转换,而页内的偏移量则直接映秀。

多种页大小

现代处理器支持多种页大小,允许操作系统和MMU使用不同的页大小,如4KB、2KB、1GB。
linux支持超大页,为特别的大页尺寸,如2MB,留出部分物理内存。

TLB:
映射记录数量有限制,使用更大的页可以增加从其缓存转换的内存范围(它的触及范围),从页减少TLB未命中而提高系统性能。TLB能进一步按每个不同页大小分设单独的缓存,以提高在缓存中保留更大范围映射的可能性。

软件
内存管理软件包括虚拟内存系统、地址转换、交换、换页和分配。与性能密切相关的内容包括这些部分:内存释放、空闲链表、页扫描、交换、进程地址空间和内存分配器

内存释放
系统中的可用内存过低,内核有多种方法释放内存,并添加到页空闲链表中

空闲链表:一个未使用的页列表也称为空闲内存,它能立刻用于分配。通常的实现是多个空闲页链表,每个本地组NUMA一个
回收:内存低于某个阈值,内核模块和内核分配器会立刻释放任何可轻易释放的内存。
linux中页面缓存:文件系统缓存。一个称作交换倾向的可调参数能调节系统倾向性,决定是通过换页还是交换来释放内存。交换:页面掏出守护进程dswapd执行的换页。它找出最近不使用的页并加入到空闲链表,其中包括应用程序内存。页页掏出涉及写入文件系统或者一人交换设备,仅在配置了交换文件或设备时才可用。OOM终结者:内存耗尽终结者搜索并杀死可牺牲的进程以释放内存,采用select_baand_process()搜索而后用oom_kill_process()杀死进程。在系统日志/var/log/messages中以 “Out of memory:Kill process”表现。

空闲链表:最初的UNIX内存分配器使用内存映射和首次匹配扫描。BSD引入虚拟内存换页时,空闲链表和页面掏出守护进程也被同时引入。空闲链表能立刻定位可用内存。

释放的内存添加到表对以便将来分配。通过页面掏出守护里程释放的内存--它可能包含有坐标的文件系统页缓存--被加到表尾。如果在未被重用前有对任一页的请求,它能被取回并从空闲链表中移除。

使用单个空闲链表是一种简化,具体实现依内核的类型及版本而不同。

Linux   用伙伴分配器管理页。它以2的幂的方式向不同尺寸的内存分配器提供多个空闲链表。术语伙伴指找到相信的空闲内存页以被同时分配
伙伴空闲链表处于如下等级结构的底端,起始于每个内存节点pg_data_t
节点:内存库,支持NUMA   
区域:特定用途的内存区域(直接内存访问(DMA)、普通、高位内存)
迁移类型:不可移动,可回收,可移动……
尺寸:数量为2的幂次方的页面。

在节点的空链表内分配能提高内存的本地性和性能。

    页染色是虚拟和物理页地址的映射。它可能利用了散列、轮询调度,或者其他的方式。这是另一个提高访问性能的策略

回收:
回收大多是从内核的slab分配器缓存释放内存。这些缓存包含slab大小的未使用内存块,以供重用。回收这些内存交还经系统进行分配。

Linux中,内核模块也可以调用register_shrinker()以注册特定的函数回收自己的内存。

页扫描:
内核页面掏出守护进程管理利用掏出页释放内存。当主存中可用的空闲链表低于阈值时,页面换出守护进程会开始扫描。

页仅按需启动。通常平衡的系统不会经常做页扫描并且仅以短期爆发方式扫描。如前所述。基于Solaris的系统在页面扫描前会利用其他机制释放内存,因此若页扫描多于几秒通常是内存压力问题的预兆。

Linux:页面换出守护进程被称作kswaps(),它扫描非活动和活动内存的LRU(最近最少被使用)页列表以释放页面。它的激活基于空闲内存和两个提供滞后的阈值。


进程地址空间

进程地址空间是一段范围的虚拟页,由硬件和软件同时管理,按需映射到物理页。这些地址被划分为段以存放线程栈、进程可执行文件、库和堆。
应用程序可执行段包括分离的文本和数据段。库也由分离的可执行文本和数据段组成。这些不同的段类型如下。

可执行文本:包括可执行的进程CPU指令。由文件系统中的二进制应用程序文本段映射。它是只读的并带来执行权限。
可执行数据:包括已初始化的变量,由二进制应用程序的数据段映射而来。在读写权限,因此这些变量在应用程序的运行过程中可以被修改。它也带有私有标记,因此这些修改不会被写回磁盘。
堆:应用程序的临时工作内存并且是匿名内存(无文件系统位置)。它按需增长并且用mollac()分配。
栈:运行中的线程栈,映射为读写

库的文件段可能与其他使用相同库的进程共享,它们各自有一份库数据段的私有副本。

堆增长:

不停增长的堆通常会引起困惑。它是内存泄漏吗?对于大多数分配器,free()不会将内存还给操作系统,相反的,会保留它们以备将来分配。这意味着进程的常驻内存只会增长,并且是正常现象。进程缩减内存的方法如下。

Re-exec:从空的地址空间调用exec()
内存映射:使用mmap()和munmap(),它们会归还内存到系统。

一些分配器支持mmap运行模式、

分配器:
多种用户和内核级的分配器可用于内存分配。
内存分配器的特征如下。
简单API:如malloc()、fee()。
高效的内存使用:处理多种不同大小的内存分配时,当存在许多浪费内存的未使用区域时,内存使用可能会变得碎片化。分配器会尽可能合并未使用的区域,因此大块的分配可使它们提高效率。
性能:内存分配会很频繁,而且在多线程的环境里它们可能会因为竞争同步基元而表现糟糕。分配器可设计为慎用锁,并利用线程级或者CPU级的缓存以提高内存本地性。
可观测性:分配器可能会提供统计数据和排错模式以显示如何被调用,以及调用分配的代码路径。

以下部分描述内核级分配器--slab分配器和SLUB--以及用户级分配器--libmalloc、libumem和mtmalloc

slab:
内核slab分配器管理特定大小的对象缓存,使它们能被快速地回收自用,并且避免页分配开销。这对于经常处理固定大小结构的内核内存分配来说特别有效。

libumem
是一个用户空间版的slab分配器。可能通过链接或预加载库为多线程应用程序提高性能。

libumem 自设计之初就考虑到可扩展性,以及排错分析能力,并尽可能减少时间和空间的开销。其他的内存分析工具在处于分析模式时会减速分析目标--有时严重到问题不再重现,并且经常使目标不适合生产环境使用。

工具方法

工具法遍历可用的工具,检查它们提供的关键指标。尽管是一个简单的方法,但它有可能忽视这些工具不可见或者看不清楚的问题,并且操作比较费时。

页扫描:寻找连续的页扫描(超过10秒),它是内存压力的预兆。Linux中,可以使用sar -B并检查pgscan列。在Solaris中,可以使用vmstat(1M)并检查sr列。

换页:换页是系统内存低的进一步征兆。Linux中,可以用vmstat(8)并检查si和so列(这里,交换指匿名换页)。Solaris中,vmstat -p 按类型显示换页,检查匿名换页。

vmstat:每秒运行vmstat检查free列的可用内存。

OOM终结者:仅对Linux有效,这些事件可以在系统日志/var/log/message,或者从dmesg(1)中找到。搜索“Out of memory”。

交换:仅对Solaris有效,运行vmstat并检查W列,它显示交换出的线程,这往往事后才能注意到。

top/prstat:查看哪些进程和用户是(常驻)物理内存和虚拟内存的最大使用者(列名参考man)。这些工具也会总结内存使用率。

dtrace/stap/perf:内存分配的栈跟踪,确认内存使用的原因。

使用特征归纳
实施容量规则、基准和负载模拟时,归纳内存使用特征分析是一项重要的活动。发现错误的配置,可能促成最大的性能提升。

由于更多内存被用于缓存工作数据, 这些特征会因时间而变化。内核或者应用程序内存也可能随时间持续增长--除正常的缓存增长外--还有由软件错误导致的内存泄漏。

这个方法以及要衡量的特征(谁、为什么、什么、如何)

监测随时间失衡的内存使用率,特别是按进程监测,有助于发现是否存在内存泄漏及其泄漏速度。

泄漏检测

当应用程序或者内核模块无尽增长,从空闲链表、文件系统缓存,最终从其他进程消耗内存时,这就出现了这个问题。初决注意到这个问题可能是因为系统为应对无尽的压力而做换页。

内存泄漏:一种类型的软件bug。忘记分配过的内存没有释放。通过修改软件代码,或者应用补丁及进行升级能修复。
内存增长:软件在正常地消耗内存,远高于系统允许的速率。通过修改软件配置,或者由软件开发人员修改软件内存的消耗方式来修复。

分析

介绍基于linux操作系统的内存分析工具。如何使用见前述的策略。

vmstat 虚拟和物理存储器统计信息
sar 历史统计信息
slabtop 内核块分配统计信息
ps  进程状态
top 监视每进程存储器使用率
pmap 进程地址空间统计信息
Dtrace 分配跟踪

Linux vmstat

该版本的vmstat不在第一行输出自系统启动至今的memory列的汇总数据,而立刻显示当前状态。
swpd:交换出的内存量。
free:空闲的可用内存。
buff:用于缓冲缓存的内存。
cache:用于页缓存的内存。
si:换入的内存
so:掏出的内存。

如果si和so列一直非0,那么系统正存在内存压力并换页到交换设备或文件(见swap8)。用其他工具可以研究什么在消耗内存,例如能观察每进程内存使用的工具。

拥有大量内存的系统中,数据列会不对齐而影响阅读。你可以试着用-S选项进行修改输出单位为MB,选项-a可以输出非活动和活动页缓存的明细:

Linux sar统计信息

-B  pgpgin/s    页面换
-B  pgpgout/s   页面换出
-B  fault/s 严重及轻微牛缺页
-B  majflt/s 严重缺页
-B  pgfree/s 页页加入空闲链表
-B  pgscank/s 被后台页面掏出守护进程扫描过的页面(kswapd)
-B  pgscand/s 直接页面扫描
-B  pgsteal/s 页面及交换调整缓存回收
-B  %vmeff 页面盗取、页面扫描比率,显示页面回收的效率
-H hbhugfree 空闲巨型页面存储器
-H hbhugused 占用的巨型页面存储器
-r  kbmemfree 空闲存储器
-r  kbmemused 占用存储器(不包括内核)
-r  kbbuffers 缓冲高速缓存尺寸
-r  kbcached 页面高速缓存尺寸
-r  kbcommit 提交的主存储器:服务当前工作负载需要量的估计
-r  %commit 为当前工作负载提交的主存储器,估计值
-r  kbactive 活动列表存储器尺寸
-r  kbinact 非活动列表存储器尺寸
-R  frpg/s 释放的存储器页面,负值表明分配
-R  bufpg/s 缓冲高速缓存增加值(增长)
-R  campg/s 页面高速缓存增加值(增长)
-S  kbswpfree 释放交换空间
-S  kbswpused 占用交换空间
-S  kbswpused 调整缓存的交换空间:它同时保存在主存储器和交换设备中,因此不需要磁盘IO就能被页面掏出
-S  kbswpcan 页面换入
-W  pswping/s 页面换出
-W  pswpout/s   



更重要的是要记住这些关于使用率和高级内存子系统运行的具体信息,在需要的时候都能找到的。要更加深入地了解,可能需要阅读源代码中mm部分,准确的说是mm/vmscan.c。开发人员常常讨论应该如何统计这些信息,在linux-mm邮件列表中有许多文章提供了更深刻的理解。

%vmeff是衡量页回收效率的一个有趣指标。高数值意思着成功地从非活动列表中回收了页(健康),低数值意味着系统在挣扎中。Man手册指出100%是高数值,小于30%是低数值。

slabtop
Linux的slabtop命令可以通过slab分配器输出内核slab缓存的使用情况。类似top,它实时更新屏幕

输出包括顶部的汇总信息和slab列表,其中包括对象数量OBJS、多少是活动的ACTIVE、使用百分比USE、对象大小OBJ SIZE字节和缓存大小CACE SIZE字节。
这个示例中,用选项-sc按缓存大小排序,最大值在顶端。
slab统计信息取自/proc/slabinfo,也可以用vmstat -m输出

top
命令监控排名靠前的运行中进程并且显示内存使用统计信息。
顶部的概要显示了主存Mem及虚拟内存Swap的总量、使用量和空闲量。缓冲缓存buffers和页缓存cache大小也同时显示。
prstat
能输出微状态统计信息,如文字和数据缺页计数。

prstat -mLcp 4937

pmap
命令列出进程的内存映射,显示它们的大小、权限及映射的对象。这使进程内存使用能被仔细地检查以及量化共享内存。

Dtrace
用户级的分配器跟踪用pid provider。这是一个动态跟踪provider,它意味着任何时刻都可以控制软件,不需要重启也不需要在此之前配置分配器运行于排错模式。

内核级的分配器自用fbt动态provider,也能用类似的方式跟踪。

缺页跟踪
能更深入地提示系统如何分配内存。可以利用fbt动态provider,或者在可用的情况下使用稳定的vminfo provider

严重缺页可以通过vminof::maj_fault探针跟踪

页面换出守护进程
如果需要,页面换出守护进程的内部运行也能用fbt provider跟踪。具体情况基于内核版本而不同。

其他Linux内存性能工具如下。
free: 报告空间内存,包括缓存区高速缓存和页缓存
dmesg: 检查来自OOM终结者Out of memory信息
valgrind: 一个包括memcheck在内的性能分析套件,它是一个内存使用分析的封闭程序,可用于发现泄漏。它能造成严重的系统开销,它的文档手册指出可引起目标系统慢20到30倍
swapon: 添加和观察物理交换设备或者文件。
iostat: 如果交换设备是物理磁盘或块,设备IO可能用iostat来观测,它指出系统是否在换页
perf: 利用它能观察CPI、MMU、TSB事件,以及源自CPU性能计数器的内存总线的停滞周期计数。它还提供缺页以及一些内核内存Kmem事件的探针。
/proc/zoneinfo: 内存区域NUMA节点的统计信息
/proc/buddyinfo: 内核页面伙伴分配器统计信息

内存调优
最重要的内存高估是保证应用程序保留在主存中,并且避免换页和交换经常发生。

内核可调参数、配置大页面、分配器和资源控制

Linux内存可调参数示例

vm.dirty_background_bytes       0       触发pdflush后台回写的脏存在器量
vm.dirty_backgroud_ratio            10      触发pdflush后台回写的脏系统存储器百分
vm.dirty_bytes                      0       触发一个定稿进程开始回写的脏存储器量
vm.dirty_ratio                      20      触发一个定稿进程开始回写的脏系统存储器比例
vm.dirty_expire_centisecs           3000    适用pdflush的脏存储器最小时间
vm.dirty_writeback_centisecs        500 pdflush活跃时间间隔
vm.min_free_kbytes              dynamic 设置期望的空闲存储器量
vm.overcommit_memory            0   
vm.swappiness                       60          相对于页面调整缓存回收更倾向用交换
vm.vfs_cache_pressure               100         回收高速缓存的目录和inode对象的程序。

多个页面大小
更大的页面能通过提高TLB缓存命中率来提升内存io性能。现代处理器支持多个页面大小,例如默认的4KB以及2MB的大页面。

在Linux中,有许多设置大页面的方法

分配器
有多种为多线程应用程序 提升性能的用户级分配器可供选用。可以在编译阶段选择,也呆以在执行时用LD_PRELOAD环境变更设置。

资源控制
基础的资源控制,包括设置主存限制和虚拟内存限制,可以用ulimit实现

文件系统
一种把数据组织成文件和目录的存储方式,提供了基于文件的存取接口,并通过文件权限控制访问。别处,还包括一些表示设备、套接字和的特殊文件类型,以及包含文件访问时间戳的元数据。

操作:文件系统的操作是对文件系统的请求,包括read()、write()、open()、close()、stat()、mkdir()以及其他操作

IO输入输出:不包括open()和Close()

逻辑IO:由应用程序发给文件系统的IO

物理IO:由文件系统直接发给磁盘的IO

吞吐量:当前应用程序和文件系统之间的数据传输率,单位是B/s

inode:一个索引节点是一种含有文件系统对象元数据的数据结构,其中有访问权限、时间戳以及数据指针。

VFS:虚拟文件系统,一个为了抽象与支持不同文件系统类型的内核接口。

卷管理器:灵活管理物理存储设备的软件,在设备上创建虚拟卷供操作系统作用。

文件系统逻辑操作:
read()、write()、open()、close()、seek()、sync()、link()、unlink()、mkdir()、ioctl()

系统工具管理操作:
mount()、umount()、sync()

研究文件系统性能的方法里,有一咱方法是把它当成一个黑盒子,只关注对象操作的时间延时

文件系统缓存
读操作从缓存返回(缓存命中)或者从磁盘返回(缓存未命中)。未命中的操作被存储在缓存中,并填充缓存热身。

文件系统延时
指的是一个文件系统逻辑请求从开始到结束的时间。它包括了消耗在文件系统、内核磁盘IO子系统以及等待磁盘设备–物理IO的时间。应用程序的线程通常在请求时阻塞,等待文件系统请求的结束。
一般的做法是使用内核跟踪工具打印出用户层发起文件系统逻辑IO的调用栈,通过研究调用栈可以看出,应用程序里哪个函数产生了IO

缓存
对应用程序这是透明的:它们的逻辑IO延时小了很多,因为可能直接从请在返回而不 是从慢得多的磁盘设备返回。

原则:如果还有空闲内存,不用来存放有用的内容。当应用程序需要更多的内存时,内核应该迅速从文件系统缓存中释放一些以备使用。
文件每户用缓存caching提高读性能,用缓冲buffering在缓存中提高写性能。文件系统和块设备子系统一般使用多种类型的缓存。

页缓存         操作系统页缓存
文件系统主存      ZFS ARC
文件系统二级缓存        ZFS L2ARC
目录缓存            目录缓存,DNLC
inode缓存     inode缓存
设备缓存            ZFS vdev
块设备缓存       块缓存buffer cache

随机与顺序IO
顺序IO里每个IO都开始于上一个IO结束的地址。
随机IO则找不出iO之间的关系,段义和量随机变化。

由于存储设备的某此特征的缘故,文件系统一直以来在磁盘上顺序和连续地存放文件数据,以努力减少随机IO的数目。当文件系统未能达成这个目标时,文件的摆放变得杂乱无章,顺序的逻辑IO被分解成随机的物理IO,这种我们称为碎片化。

预取
通过检查当前和上一个IO的文件偏移量,可以检测出当前是否是顺序读负载,并且做出预测,在应用程序请求前向磁盘发出读命令,以填充文件系统缓存。这样如果应用程序真的发同了读请求,就会命中缓存

而一量预测不准,文件系统会发起应用程序不需要的IO,不仅污染了缓存,也消耗了磁盘和IO传输的资源。

写回缓存
当数据定稿主存后,就认为写入已经结束并返回,之后再异步地把数据刷入磁盘。文件系统定入脏数据的过程称为刷新

这期间牺牲了可靠性。基于DRAM的请在是不可靠的,脏数据会在断电的情况下丢失,而应用程序却认为写入已经完成。并且数据可能被非完整写入,这样磁盘上的数据就是在一种破坏的状态。

文件系统默认采用写缓存策略,但同时也提供一个同步写的选项绕过这个机制,把数据直接写在磁盘上。

同步写
所有的数据以及必要的文件系统元数据被完整地定写入到永久存储介质如磁盘设备中。由于包含了磁盘IO的延时,所以肯定比异步写回缓存慢得多。
有些应用程序,例如数据库写日志,因完全不能承担异步定带来的数据损坏风险,而使用了同步写有两种形式:
单次同步写
当使用O_SYNC标志,或者其他变体,如O_DSYNC和O_RSYNC打开一个文件后,这个文件的写IO即为同步。

同步提交已写内容
一个应用程序可能检查点使用fsync()系统调用,同步提交之前异步写入的数据,通过合并同步写提供高性能。

裸IO和直接IO
裸IO:绕过了整个文件系统,直接发给磁盘地址。有些应用程序使用了裸IO特别数据库,因为它们能比文件系统更好地缓存自己的数据。其缺点在于难以管理,即不能使用常用文件系统工具执行备份、恢复和监控。
直接IO:允许应用程序绕过缓存使用文件系统。这有点像同步(但缺少O_SYNC选项提供的保证),而且在读取时也能用,它没有裸IO那么直接,文件系统仍然会把文件地址映射到磁盘地址,IO可能会被文件系统重新调整大小以适应文件系统在磁盘上的块大小(记录尺寸)

非阻塞IO
一般而言,文件系统IO要么立刻结束(如从缓存返回),要么需要等待(比如笔磁盘设备IO)。如果需要等待,应用程序线程会被阻塞并让出CPU,在等待期间给其他线程执行的机会。
某此情况下,非阻塞IO正合适,因为可以避免线程创建带来的额外性能和资源开销。在调用open()系统调用时,可以传入O_NONBLOCK 或者O_NDELAY选项使用非阻塞IO。这样读写时就会返回错误代码EAGAIN,让应用程序过一会儿再重试,而不是阻塞调用。

内存映射文件
对于某些应用程序和负载,可以通过把文件映射到进程地址空间,并直接存取内存地址的方法来提高文件系统IO性能。这样可以避免调用read()和write()存取文件数据时产生的系统调用和上下文件切换开销。如果内核支持直接复制文件数据缓冲到进程地址空间,那么还可能防止复制数据再次。
内存映射通过系统调用mmap()创建,通过nunmap()销毁。映射可以用madvise()调整,部分应用程序提供一个选项,以使用mmap系统调用。例如Riak数据库可使用mmap建立内存数据存储。

元数据
元数据可能是通过文件系统接口POSIX读出的信息,也可能是文件系统实现磁盘布局所需的信息。前者被称为逻辑元数据,后者被称为物理元数据。

逻辑元数据是用户读取或者写入的信息
元数据密集的负载通常指那些频繁操作逻辑元数据的行为,甚至超过了文件内容的读取,例如web服务器用stat()查看文件,确保文件在缓存后没有修改

物理元数据
为了记录文件系统的所有信息,有一部分元数据与磁盘布局相关,这就是物理元数据。依赖于文件系统类型,可能包括了超级块,inode、数据块指针(主数据、从数据、等待)以及空闲链表

逻辑IO与物理IO
应用程序向文件系统发起的IO(逻辑IO)与磁盘IO(物理IO)可能并不相称。

与应用程序IO相比,磁盘IO有时显得无关、间接、放大或者缩小。

无关:其它应用程序、其他租户、其他内核任务
间接:文件系统预取、文件系统缓冲
缩小:文件系统缓存、文件系统写抵消、压缩、归并、内存文件系统
放大:文件系统元数据、文件系统记录尺寸、郑管理器奇偶检验

操作并非不平等
操作是随机还是连续的、读取还是定写入的、同步写还是异步写、IO大小、是否包含其他操作类型,以及CPU执行消耗。

特殊文件系统
文件系统的目的通常是持久地存储数据,但有些特殊的文件系统也有着其他用途,比如临时文件/temp、内核设备路径/dev和系统统计信息/proc

访问时间戳
这会造成读取文件时需要更新元数据,读取变成了消耗磁盘IO资源的写负载。
有些文件系统对访问时间戳做了优化,合并及推迟这些写操作,以减少对有效负载的干扰。

容量
当文件系统装满时,性能会因为数个原因有所下降。当写入新数据时,需要花更多的时间来寻找磁盘上的空闲块,而寻找过程本身也消耗计算和IO资源。磁盘上的空闲空间变得更小更分散,而更小或随机的IO则影响了文件系统的性能。

文件系统IO栈
这张图展现了IO穿过内核的路径。从系统调用直接调用 磁盘设备子系统的是裸IO。穿过VFS和文件系统的是文件系统IO,包括绕过了文件缓存的直接IO

VFS
VFS虚拟文件系统接口,给不同类型的文件系统提供了一个通用接口。

Linux磁盘上磁盘上数据结构的名词则通常加上了文件系统类型的前缀,例如ext4_inode。这些VFS inode和VFS超级块都只存在内存中。

VFS接口可以作为测量任何文件系统性能的通用平台。这样同时还能利用操作系统提供的统计信息,或者静态及动态跟踪技术

文件系统缓存
inode缓存能够动态增长,其中保存了至少所有打开文件的inode被引用,和那些被DNLC映射的inode。除此之外,还有一些额外的inode放在了空闲队列上,以备不时之需。

页缓存
缓存了虚拟内存的页面,包括了映射过的文件系统页面。(缓冲区高速缓存,需要在每次查找时报文件地址翻译成磁盘地址)
页缓存有两个主要的内核驱动程序用户:segvn把文件映射到进程地址空间,segmap缓存文件系统的读写。

DNLC
目录名查找缓存(Directory Name Lookup Cache)记录了目录项到vnode的映射关系,它提升了路径名查找性能比如通常open()。当遍历目录路径时,每个名字的查找都可以通过DNLC直接找到vnode,而不是在目录里一项一项的对比。DNLC在设计上黑线性能和伸缩性,以父目录的vnode和目录项名称作为键值,存储在哈希表中。

缓冲区调整缓存
被存在了页缓存中,这防止了双重缓存和同步的开销。缓冲区调整缓存的功能依然健在,提升了块设备IO的性能。
缓冲区调整缓存的大小是动态的,可以从/proc里查看

页缓存
页缓存缓存了虚拟内存的页面,包括文件系统的页面,提升了文件和目录的性能。页缓存大小是动态的,它会不断增长消耗可用的内存,并在应用程序需要的进修释放

文件系统使用的内存脏页面由内核线程写回到磁盘上。有一个脏页面写回pdflush线程池,池中根据需要有2-8个线程。现在这已经被写回线程(flusher thread,线程名为flush)所取代,每个设备分配一个线程。这样能够平衡每个设备的负责,提高吞吐量。

页面因下面的万年历被写回到磁盘上:
过了一段时间30s;调用了sync()、fsync()或msync等系统调用;过多的脏页面(dirty_ratio);页缓存内没有可用的页面;

目录项缓存
目录项缓存Dcache记录了从目录项struct dentry到VFS inode的映射关系,和早期UNIX的DNLC很相似。它提高了路径名查找的性能:当遍历一个路径名时,查找其中每个名字都可以先检查Dcache,直接得到inode的映射,而不用到目录里一项项地。
包括了读-拷贝-更新遍历。该算法可以遍历路径名,而不更新目录项的引用计数,

inode缓存
这个缓存的对象是VFS inode,每个都描述了文件系统一个对象的属性。这些属性很多可以通过stat()系统调用获得,并被操作系统操作频繁访问。

文件系统特性
文件系统特性
块和区段
基于块的文件系统把数据存储在固定大小的块里,被存储在元数据块里的指针所引用。对于大文件,这种方法需要大量的块指针和元数据块,而且数据块的摆放可能会变得零零碎碎,造成随机IO。块连续和采用更大的数据块。

日志
文件系统日志记录了文件系统的更改,这样在系统空压宕机时,能原子地回放更改,要么完全成功,要么完全失败。

写时复制
把数据写到一个新块;更新引用指向新块;把老块放到空闲链表中

在系统宕机时这能够有效地维护文件系统的完整性,并且通过把随机写变成连续写,改善了写入性能。

擦洗
这项文件系统特性在后台读出文件系统里所有的数据块,验证校验和。赶在在RAID还能万利数据之前,在第一时间检测出坏盘。但是擦洗操作的读IO会严重影响性能,因此只能以低优先级发出。

文件系统种类
ext4
区段、大容量、通过fallocate()预分配、延时分配、日志校验和、更快fsck、多块分配器、纳秒时间戳以及快照
区段:区段提高了数据的连续性,减少了随机IO,提高了连续IO的大小
预分配:通过fallocate()系统调用,让应用程序预分配一些可能连续的空间,提高之后的写性能。
延时分配:块分配推迟到写入磁盘时,方便合并写请求,降低化。
更快的fsck:标记未分配的块和inode项减少fsck时间。

ZFS 
把文件系统、卷管器以有许多企业级特性整合在一起:池存储、日志、写时复制COW、自适应替换缓存ARC、大容量支持、变长块、动态条带、多预取流、快照、克隆、压缩、擦洗和128位校验和。热备、双重厅偶检验RAID、gzip压缩、SLOG、L2ARC、用户和组配额、三重奇偶检验RAID、数据消重,混合RAID分配以及加密。
池存储:所有的存储设备放到一池里,文件系统则从池里创建。这样所有的设备都能够同时用到,最大化了吞吐量和IOPS。可以使用不同的RAID类型建池:0、1、10、Z、Z2双重奇偶检验和Z3三重奇偶校验
COW:合并写操作并顺序写入
日志:ZFS整体写入事务组变更,一起成功或失败,保证磁盘上的结构永远一致。日志也通过批量客户以提高 异常写的吞吐量。
ARC:自适应替换缓存通过使用几种缓存算法达到缓存的高命中,算法包括了最近使用MRU和最常使用MFU。内存在两种算法之间平衡,根据模拟以某个算法完全主导内存时系统的性能,做出相应的调整。
变长块:每个文件系统都可根据相应的负载选择一个可配置的最大块大小。
动态条带:为了达到最大吞吐量,条带横跨了所有的存储设备,并在添加额外设备时能自动增长。
智能预取:ZFS对不同类型的数据用相匹配的预取策略,分别针对元数据、znode文件内容以及vdev虚拟设备
多预取流:由于文件系统来回寻道UFS的问题,一个文件的多个读取流会产生近似随机IO的负载。ZFS跟踪每一个预取流,允许加入新的流,有效地发起IO
快照:COW的架构使得快照能瞬间建立,按需复制新数据块。
ZIO流水线:设备IO由一个分步骤的流水线处理,每个步骤都有一个线程池服务提高性能。
压缩:ZFS支持多种算法,但CPU开销对性能有所影响。其中轻量级lzjb可以通过少量消耗CPU来减少IO负载,提高存储性能。
SLOG:独立的ZFS意图日志使日志可以同步写入单独的设备,避免和磁盘池的负载竞争。SLOG写入的数据在系统宕机时仅只读以供回放。这些措施极大地提高了同步写的性能。
L2ARC:二级ARC是主存之外的第二缓存,通过基于闪存的固态硬盘SSD缓存随机地读负载.
vdev缓存:和最初的缓冲区调整缓存类似,ZFS给每个虚拟设备分配了单独的vdev缓存,并支持LRU和预读。
数据消重:这是一个文件系统级别的特性,避免把一份相同的数据存放多份。这项功能对性能有很大的影响,可能有利(降低设备IO),也可能有弊(当内存不够容纳哈希表时,设备IO可能会被大大放大)
卷和池
文件系统一直以来建议在一块磁盘或者一个磁盘分区上。卷和池使用文件系统可以建立在多块磁盘上,并可以使用不同的RAID策略。
卷把多块磁盘组合成一块虚拟磁盘,在此之上可建立文件系统。在整块磁盘上建文件系统时(不是分片或者),卷能够隔离负载、降低竞争、缓和性能问题。
池存储把多块磁盘放到一个存储池里,池上可以建议多个文件系统。体现了这种差异。池存储比卷存储更灵活,文件系统可以增长或者缩小而不牵涉下面的设备。这种方法被现代文件系统采用。
池存在可以让所有的文件系统使用所有磁盘,以提高性能。负载并未隔离,有些情况下可以牺牲一些灵活性,使用多个池来隔离负载,这是因为磁盘设备在一开始必须被加入某一个池
条带宽度:与负载相匹配
观察性:虚拟设备的使用率可能不准确,需要检查对应的物理设备。
CPU开销:尤其是在进行RAID奇偶校验的计算时。不过随着更快的现代CPU的使用,这个问题逐渐消失
重建:当一块空磁盘加入到RAID组里,它被走入必要的数据以加入组。由于消耗IO资源长达几个小时甚至几天,可能严重影响性能。

文件系统方法
本节描述了文件系统分析和高估的各种策略和实践
磁盘分析 观察分析
延时分析 观察分析
负载特征归纳 观察分析,容量规划
性能监控 观察分析,容量规划
事件跟踪 观察分析
静态性能调优 观察分析,调优
缓存调优 观察分析,调优
负载分离 调优
内存文件系统 调优
微型基准测试 实验分析法

磁盘分析
以前通常的策略是关注磁盘性能忽视文件系统。这假定了IO瓶颈在磁盘,因此通过只分析磁盘,你能方便地在假定的罪魁祸首上集中火力。

延时分析
延时分析从测量文件系统操作的延时开始。这应该包括所有对象操作,而不限于IO(比如包括sync())
操作延时 = 时刻(完成操作) - 时刻(发起操作)

对于缓存命中率真高大于99%的文件系统,单位时间平均值可能完全缓存命中淹没。不幸的是,有些高延时离群点的个案虽然很重要,但很难从平均值里看出。检查全分布、每个操作的延时以及不同层延时的影响,包括文件系统缓存命中和未命中情况,可以帮助挑出这些离群点以供调查。

事务成本
文件系统延时,还能以一个应用程序事务内等待文件系统所有时间来表现:
文件系统消耗时间百分比 = 100 * 所有的文件系统阻塞延时 / 应用程序事务时间
文件系统操作的损耗因此得以从应用程序性能的角度量化,而性能改进也能被更准确地预测。测量的指标可以是一段时间内所有事务的均值,或者是单个事务

负载特征归纳
这项工作可以通过指出并排除不需要的工作获得最大的性能收益
操作频率和操作类型
文件IO吞吐量
文件IO大小
读写比例
同步写比例
文件随机和连续访问比例

高级负载特征归纳检查清单
文件系统缓存命中率是多少?未命中率是多少?
文件系统缓存有多大?当前使用情况如何?
现在还使用了其他什么缓存(目录、inode、调整缓冲区)和它们的使用情况?
哪个应用程序或者用户在使用文件系统?
哪些文件和目录正在被访问?是创建和删除吗?
碰到了什么错误吗?是不是由于一些非法请求,或者文件系统自身的问题?
为什么要发起文件系统IO(用户程序调用路径)?
应用发起的文件系统IO中同步的比例占到多少?
IO抵达时间的分布是怎么样的?

谁测量、为什么测量、测量什么、如何测量。

性能特征归纳
下面的问题遍布了负载的性能数据:
文件系统操作的平均延时是多少?
是否有高延时的离群点?
操作延时的全分布是什么样的?
是否拥有并开户了文件系统或磁盘IO的系统资源流控?

性能监控
操作频率和操作延时
操作频率是负载的最基本特征,而延时则是其性能结果。延时是好是差,取决于负载、环境和延时需求。如果不清楚,可以针对已知好的和差的情况分别做微基准测试,调查延时情况。
操作延时的指标可以每秒平均值为单位,也可以包含其他数据,如最大值和标准差。理想情况下,最好看看延时的全分布,如通过直方图和热度图以发现离群点或者其他模式。

可以只记录单个操作类型读、写、统计、打开、关闭、等等的频率和延时数据。这对调查负载和性能变化有很大帮助,因为可以找出不同操作类型之间的差异。

事件跟踪
事件跟踪捕获文件系统每个操作的细节。这是观察分析最后的手段。由于捕获和保存信息到日志文件,这种方法增加了性能开销。这些日志信息可能包含了每个操作的下列信息。
文件系统类型。
文件系统挂载点。
操作类型:读取、写入、统计、打开、关闭、建目录、等等。
操作大小如果适用:字节数
操作开始时间戳:向文件系统必起操作的时间。
操作结束时间戳:文件系统完成操作的时间。
操作完成状态:错误。
路径名(如果适用)。
进程ID。
应用程序名。
有了开始和结束的时间戳,就可以计算操作的延时。许多跟踪框架允许一边跟踪一边计算,这样日志里就能包含延时数据。此外还可以过滤输出,只记录那些慢于某个阀值的操作。文件系统的操作频率有可能达到每秒百万次,正确地过滤将会很有帮助。

静态性能调优
挂载并当前使用了多少个文件系统?
文件系统记录大小?
启用了访问时间戳吗?
还启用了哪些文件系统选项(压缩、加密等)?
文件系统缓存是怎么配置的?最大缓存大小是多少?
其他缓存(目录、inode、调整缓冲区)是怎么配置的?
有二级缓存吗?用了吗?
有多少个存储设备?用了几个?
存储设备是怎么配置的?用了RAID吗?
用了哪个文件系统?
用了哪个文件系统的版本(或者内核)?
有什么需要考虑的文件系统bug/补丁吗?
启用文件系统IO的资源控制了吗?
有时按照了某种负载来配置文件系统,但后来却用在了其他的场景里。这个方法能让你重新审视那些配置选项。

缓存调优
先检查有哪些缓存,接看看它们是否投入使用,使用的情况如何和缓存大小,然后根据缓存调整负载,以及根据负载调整缓存。

负载分离
有些类型的负载在独占文件系统和磁盘设备时表现的更好。这个方法又被称为使用“单独转轴”,因为施加两种不同的负载会导致随机IO,这对旋转的磁盘特别不利。

例如,让日志文件和数据库文件拥有单独的文件系统和磁盘,能提高数据库性能。

微型基准测试
文件系统和磁盘的基准测试工具可以用来澧多种类型文件系统的性能,或者某种负载下,同一文件系统不同设置一的性能。
操作类型:读写和其他文件系统操作的频率。
IO大小:从1B到1MB甚至更大。
文件偏移量模式:随机或者连续
随机访问模式:统一的随机分布或者帕累托分布。
写类型:异步或同步(O_SYNC)
工作集大小:文件系统缓存是否放下
并发:同时执行的IO数,或者执行IO的线程数。
内存映射:文件通过mmap()访问而非read()、write()
缓存状态:文件系统缓存是“冷的”未填充还是挺好的。
觉的组合包括了随机读、连续、随机写和连续写。

最重要的参数通常是工作集大小:基准测试时访问的数据量。这可能是当前使用文件和总大小,取决于基准测试的配置。一个较小的工作集会导致所有访问都从请在DRAM里缓存中返回。而一个较大的工作集则可能使用得访问大部分从存储设备磁盘返回。期间的性能差异可能达到几个数量级。

分析
Linux文件系统分析工具
strace 系统调用高度器
Dtrace 动态跟踪文件系统操作和延时
top 包括内存使用概要
vmstat 虚拟内存统计信息
sar 多种统计信息,包括历史信息
slabtop 内核slab分配器统计信息
/proc/meminfo 内核内存使用情况

vfsstat
是一个vfs层的类iostat工具。它打印出了单位时间内文件系统操作逻辑IO的总结信息,包括了用户应用程序感知到的平均延时。这份资料与应用程序性能iostat提供的统计信息更加直接的联系。后者展示了包括异步调用在内的磁盘IO

r/s,    w/s:文件系统每秒读写次数
kr/s,kw/s:  文件系统每秒读写KB数
ractv,wactv:正在执行中的平均读写操作数
read_t,writ_t:VFS读写操作等待时间平均百分比
%r,%w:VFS读写操作等待时间平均百分比
d/s,del_t:每秒IO流控延时时间和平均延时时间

慢事件跟踪
Dtrace能够打印出每个文件系统操作的详细信息。然而,由于 包括了文件系统缓存命中,在文件系统级别跟踪会产生无比繁冗的输出。一个解决办法是仅仅打印出那较慢的操作,有助于分析一种特定类型的问题:延时离群点。

高级跟踪
在需要向睛挖掘分析时,动态跟踪可以更加深入地探索文件系统。
sysfs.d 系统调用 按照进程和挂载点打印出读和
fsrwcount.d 系统调用 按照文件系统和类型统计读、写系统调用
fsrwtime.d 系统调用 按照文件系统测量读、写系统调用的时间
fsrtpk.d 系统调用 测量文件系统每KB读取时间
rwsnoop 系统调用 跟踪系统调用里的读和写,并列出文件系统详细信息
mmap.d 系统调用 跟踪mmap()对文件的调用,列出详细信息
fserros.d 系统调用 列出文件系统调用错误
fswho.d VFS 统计进程和文件的读写信息
readtype.d VFS 比较逻辑和物理文件系统读
writetype.d VFS 比较逻辑和物理文件系统写
fssnoop.d VFS 使用fsinfo跟踪文件系统调用
solvfssnoop.d VFS 使用Solaris上使用fbt跟踪文件系统
sollife.d VFS 在Solaris上显示文件创建和删除
fsflush_cpu.d VFS 显示文件系统定稿跟踪器的CPU时间
fsflush.d VFS 显示文件系统统计信息
dnlcps.d DNLC 按照进程显示DNLC命中
ufssnoop.d UFS 直接使用fbt跟踪UFS调用
ufsreadahead.d UFS 显示UFS连续IO的预计率
ufsiiss.d UFS 跟踪UFS inode缓存未命中,列出详细信息
zfssnoop.d ZFS 直接使用fbt跟踪ZFS调用
zfslower.d ZFS 跟踪缓慢的ZFS读写
zioprint.d ZFS 显示ZIO事件转储
ziosnoop.d ZFS 显示ZIO事件跟踪,带有详细信息
ziotype.d ZFS 按照存储池显示ZIO类型统计
perturbation.d ZFS 显示给定民振动期间的ZFS读写时间
spasync.d ZFS 显示存储池分配器SPA同步操作的跟踪信息,列出详细信息
nfswizard.d NFS 统计NFS客户端性能
nfs3sizes.d NFS 比较NFSv3逻辑和物理读大小
nfs3fileread.d NFS 按文件比较NFSv3逻辑和物理读
tmpusers.d TMP 通过跟踪open()展示tmp和tmpfs用户
tmpgetpage.d TMP 测量tmpfs是否有换页发生,列出IO时间

slabtop
命令打印出有关内核slab缓存的信息,其中有些用于文件系统缓存
dentry: 目录缓存
inode_cache: inode缓存
ext3_inode_cache: ext3的inode缓存
ext4_inode_cache: ext4的inode缓存

/proc/meminfo
文件提供了内存使用状况的分解,如直free的一些工具也读个文件:
这个包括了缓冲区高速缓存Buffers和页缓存Cached,并提供了系统内存使用情况的其他概况分解

调优
高估的细节–可以调整的选项和设置值–取决于操作系统版本、文件系统类型和预期的负载。

应用程序调用
通过fsync()合并入一组写请求。相较于使用open()标志位O_DSYNC/O_RSYNC的一人写入,这种方法提高了同步写的性能。

posix_fadvise()建议标志位
POSIX_FADV_SEQUENTIAL       指定的数据范围会被连续访问
POSIX_FADV_RANDOM       指定的数据范围会被随机访问
POSIX_FADV_NOREUSE      数据不会被重用
POSIX_FADV_WILLNEED     数据会在不远的将来重用
POSIX_FADV_DONTNEED     数据不会在不远的将来重用

内核可以自用这个信息提高性能,决定什么时候预取数据以及什么时候缓存数据。内核还可以根据应用程序的建议,提高高优先级数据的缓存命中率。

madvise()
这个库函数调用对一块内存映射进行操作,
MADV_RANDOM     偏移理将以随机顺序访问
MADV_SEQUENTIAL 偏移量将以连续顺序访问
MADV_WILLNEED       数据还会再用请缓存
MADV_DONTNEED       数据不会再用请勿缓存

ZFS数据集可调参数
recordsize 512~128K 建议文件块大小
compression on|off|lzjb|gzip|gzip-[1-9]|ale|lz4 在后端IO堵塞的情况下,轻量级的算法(如lzjb)可以在某些情况下提高性能
atime on|off 访问时间戳更新
primarycache all|none|meadata ARC策略;使用none或者metadata(仅缓存元数据)能够降低因低优先级文件系统(例如归档)造成的缓存污染
secondarycache all|none|meadata L2ARC策略
logbias latency|throughput 同步写的建议:latency使用日志设备,而throughput使用池设备
sync standard|always|disable 同步写行为

最重要的调优参数一般 记录尺寸,需要让它和应用程序IO匹配。它一般默认为128KB,这个值在随机小IO的情况下效率不高,注意这个对于小于记录尺寸的文件无效,因为这些文件是以等于文件大小的动态记录尺寸存放的。

硬盘
磁盘IO可能会造成严重的应用程序延时,因此是系统性能分析的一个重要。在高负载下,磁盘成为了瓶颈,CPU持续空闲以等待磁盘IO结束。发现并消除这些瓶颈能让性能和应用程序吞吐量翻好几番。

名词“磁盘”指定系统的主要存储设备。这包括了旋转磁盘性盘片和基于闪存的固态盘SSD。引入后者主要是为了提高磁盘的IO性能,而事实上的确做到了。然而,对容量和IO速率的需求仍在不断增长,闪存设备工不能完全解决性能问题。
虚拟磁盘:存储设备的模拟。在系统看来,这是一块物理磁盘,但是,它可能由多块磁盘组成。
传输总线:用来通信的物理总结,包括数据传输IO以及其他磁盘命令。
扇区:磁盘上的一人存储块,通常是512B大小 。
磁盘命令:除了读写之外,磁盘还会被指派执行其他百数据传输的命令(例如缓存写回)。
吞吐量:对于磁盘而言,吞吐量通常指当前数据传输速率,单位是B/s。
带宽:这是存储传输或者控制器能够达到的最大数据传输速率。
IO延时:一个IO操作的执行时间,这个词在操作系统领域广泛使用,,早已超出了设备层。注意在网络领域,这个词有其他的意思,指发起一个IO的延时,后面还跟着数据传输时间。
延时离群点:非同寻常的高延时磁盘IO。

简单磁盘
磁盘接受的iO请求要反在队列里等待,要么正在处理中。这个简单模型就像超市的收银口,顾客们排除来等待服务。这也很适合用排除理论进行分析。

缓存磁盘
磁盘缓存的加入使用些读请求可以从更快的内存介质中返。只要物理磁盘设备里的一小块内存DRAM就可以达到这种效果。

虽然缓存的命中 可以带来非常低的延时,高延时的缓存未命中却仍然是磁盘设备的主旋律。

盘上的缓存也可以用作写缓存以提高写性能。在数据写到缓存后,磁盘就通知写入已经结束,之后把数据定稿到较慢的永久磁盘介质中。与之相对的另一种做法叫做写穿缓存,只有当写完全进入到下一层再返回。

控制器
一种简单的磁盘控制器,把CPU IO传输总线和存储总结以及相连的磁盘设备桥接起来。这人设备又称为主机总线适配喊叫(host bus adaptor,HBA)

测量时间
存储设备的响应时间指的是从IO请求到结束的时间。它由服务和等待时间组成。

服务时间:IO得到主动处理服务的时间,不包括在队列中等待的时间。
等待时间:IO在队列中等待服务的时间。

响应时间、服务时间和等待时间全部取决于测量所处的位置。
从操作系统角度来说,服务时间可以从IO请求发到磁盘设备开始计算,到结束中断发生为止。它并不包括在操作系统队列里等待的时间,仅仅反映了磁盘设备对操作请求的总体性能。
而对于磁盘而言,服务时间指从磁盘开始主动服务IO开始算起,不包括在磁盘队列里等待的任何时间。

而今磁盘有了自己的内部队列,操作系统的服务时间却包括了在设备队列里等待时间。操作系统的这个指标,其实应该称为“磁盘响应时间”更为准确。
磁盘响应时间可以描述从操作系统角度观测的服务时间,而IO响应时间则是从应用程序角度出发,可包括系统调用层以下的时间总和(服务时间、所有的等待时间和代码执行时间)
块设备接口的服务时间通常作为磁盘性能的一个指标,但是,始终牢记这只是一种简化。表现了项下三种可能的驱动层。其中任何一种都可能实现自己的队列,抑或阻塞在互斥量上,增加IO延时。从块设备接口测量,这些延时都会被 统计在内。

计算时间
磁盘服务时间通常不从操作系统直接观测而来,相反,平均磁盘服务时间通过IOPS和使用推算出来:
磁盘服务时间 = 使用率/IOPS

时间尺度
磁盘IO的时间尺度千差万别,从几十微秒到数千毫秒。在最慢的一端,单个慢磁盘IO可能导致糟糕的应用程序响应时间;而最快的一端,只有在数量很大的时候性能才会出现问题

磁盘IO延时时间尺度示例
磁盘缓存命中 <100us 1s
读闪存 约100~1000us 1~10s
机械磁盘连续读 约1ms 10s
机械磁盘随机读7200转 约8ms 1.3分钟
机械磁盘随机读(慢,排除) >10ms 1.7分钟
机械磁盘随机读(队列较长) >100ms 17分钟
最差情况的虚拟磁盘IO(磁盘控制器、RAID-5、排除,随机IO) >1000ms 2.8小时

对于一个工作在企业存储领域的人来说,我觉得任何大于10ms的磁盘IO都过于漫长,很可能有性能问题。云计算领域则能容忍更高的延时,特别是对于基于web的应用程序,本来客户端和浏览器之间的网络延时就已经很高了。在这些环境里,磁盘淡有在超过100ms时才是副科级一(单个IO时间,或者在一个应用程序请求期间的时间总和)    

    显示了一块磁盘可以返回两种延时:一种是磁盘缓存命中(低于100us),另一种是缓存未命中(1-8ms,甚至更慢,取决于访问模式和磁盘类型)。由于这两种延时磁盘都返回,以一个平均延时来表达难免有误导之嫌。

缓存
许多软件栈尝试通过缓存读和缓冲写来避免磁盘IO抵达磁盘。列出了完整的列表,囊括了应用程序和文件系统的缓存。
设备缓存 ZFS vdev
块缓存 缓冲区高速缓存
磁盘控制器缓存 RAID卡缓存
存储阵列缓存 阵列缓存
磁盘缓存 磁盘数据控制器DDC附带DRAM

随机vs连接IO
根据磁盘上IO的相对位置(磁盘偏移量),可以用术语随机和连续描述磁盘IO负载。
连续负载也称为流负载。术语流一般用于应用程序层,用来描述对“磁盘”(文件系统)的流式读和写。
在磁盘旋转磁盘时代,随机与连续磁盘IO模式之间的对比是研究的重点。随机IO带来的磁头寻道和IO之间的盘片旋转导致额外的延时。磁头从扇区1到扇区2需要寻道和IO之间的盘片旋转导致额外的延时。磁头从扇区1到扇区2需要寻道旋转两个动作(实际路径是越直越好)。性能调优的工作包含识别并通过一些手段排除随机IO,例如缓存、分离随机IO到不同的磁盘,以及以减少寻道距离为目的的数据摆放。

从操作系统角度看到的磁盘偏移量并不一定是物理磁盘的偏移量。例如,硬件提供的虚拟磁盘可能把一块连续偏移量范围映射到多块磁盘。例如,硬件提供的虚拟磁盘可能把一块连续偏移量范围映射到多块磁盘。磁盘自己可能会按自己的方式重新映射偏移量(通过磁盘数据控制器)。有时随机IO并不通过检查偏移量的方式确定,而是由服务时间的上升推断。

读写比
与IOPS或者吞吐量相关。还可以通过一段时间内的比例来表示,例如,“系统启动后读的比例占到80%。”
理解这个比例有助于设计和配置系统。一个读频率较高的系统可以通过增加缓存来获得性能提升,而一个写频率较高的系统则可以通过增加磁盘来提高最大吞吐量和IOPS。
读和写本身可以是不同的负载模式:读可能是随机的,而写可能是连续的(特别是写时复制的文件系统)。它们的IO大小可能不尽相同 。

IO大小
更大的IO一般提供了更大的吞吐量,尽管单位IO的延时有所上升。
磁盘设备子系统可能会改变IO大小(例如量化到512字节块)。由于IO从应用程序层发起,大小可能会因一些内核组件而被放大或者缩小,比如文件系统、卷管理和设备驱动。
有些磁盘设备,特别是基于闪存的设备,对不同的读写大小有非常不同的行为。比如,一个基于闪存的磁盘驱动可能在4KB读和1MB写时表现得最好。理想的IO大小可以查阅磁盘供应商的文档,或者通过微基准测试获得。当前使用中的IO大小可以通过观察工具获得

IOPS并不平等
IOPS并非生而平等,不能在不同设备和负载的情况下进行简单比较。一个IOPS值本身没有太多意义,并不能单独用来准确比较负载。
有意义的IOPS需要包含其他细节:随机或者连续、IO大小、读写比。另外考虑使用基于时间的指标,比如使用率和服务时间,以反映性能结果,并且可以更简单地进行比较。

非数据传输磁盘命令
除了读写IO,磁盘还可以接收其他命令。比如,可以命令带有缓存的磁盘RAM把缓存写回磁盘上。这种命令不是数据传输,数据之前已经通过写命令发送给磁盘。这些命令会影响性能,造成磁盘运转而让其他IO等待。

使用率
注意使用率是一段时间内的总结。磁盘IO可能突然爆发,特别是在批量刷新缓存时,而这种爆发可能被长时间的统计所摊平。

虚拟磁盘使用率
对于基于硬件的虚拟磁盘(比如磁盘控制器),操作系统可能只知道虚拟磁盘的忙时间,却不不清楚底下磁盘的性能。这导致在某些情况下,操作系统报告的虚拟磁盘使用率和实际磁盘的情况有很大出入
包含写回缓存的虚拟磁盘可能在负载的时候看上去并不是很忙,因为磁盘控制器马上返回写完成。
一块100%占用的虚拟磁盘是建立在多块物理磁盘上的,可能还可以接受更多的工作。在这种情况下,100%可能意味着有些磁盘一直都很忙,但并不是所有的磁盘在所有的时间内都忙,有一些磁盘仍然有空闲时间。

操作系统应该显示物理磁盘的使用率以供查看

饱和度
饱和度度量了因超出资源服务能力而排队的工作。对于磁盘设备,它可以通过操作系统的磁盘等待队列的长度计算得出。
这给100%以上的使用率提供了性能度量。一块100%使用率的磁盘可能并未饱和(排除),或者非常饱和,导致排除IO严重影响性能。
可以假设低于100%使用率的磁盘没有饱和。但是这取决于使用率窗口:一段时间内50%的磁盘使用率可能意味着一半时间内的100%使用率,外加剩下时间内完全空闲。所有单位时间的汇总都可能被相似的问题困扰。如果了解确切信息很重要的话,可以跟踪检查IO时间。

IO等待
IO等待是针对单个CPU的性能指标,表示当CPU分发队列在睡眠态里有线程被阻塞在磁盘IO上时消耗的空闲时间。这就把CPU空闲时间划分成无所事事的真正空闲时间,以及阻塞在磁盘IO上的时间。
IO等待可能是一个令人非常困惑的指标。如果另一个CPU饥饿型的进程也开始执行,IO等待值可能会下降:CPU现在有工作要做,而不会闲碰上。但尽管IO等待指标下降了,磁盘IO还是和原来一样阻塞线程。与此相反,有时候系统管理员升级了应用程序软件,新版本的软件提高了效率,使用较少的CPU,把IO等待问题暴露了出来,这会让系统管理员认为软件升级导致了磁盘问题,降低了性能。然而事实上磁盘性能并没有变化,CPU性能却能提高。
一个更可靠的指标可能是应用程序线程阻塞在磁盘IO上的时间。这个指标捕捉了应用程序线程因磁盘IO导致的延时,而与CPU正在执行的其他工作无关。这个指标可以用静态或者动态跟踪的方法测量。
一 种观点认为,任何IO等待都是系统瓶颈的迹象,然后对系统进行调优以最小化它–即使IO仍和CPU并发运行。并发IO更可能是非阻塞IO,也较小直接导致问题。非并发IO容易因IO等待暴露出来,更可能成为阻塞应用程序IO的瓶颈。

同步vs异步
如果应用程序和磁盘IO是异步的,那磁盘IO延时可能不直接影响应用程序性能,理解这点很重要。通常这发生在写回缓存上,应用程序IO早已完成,而磁盘IO稍后发出。
应用程序可能用预读执行异步读,在磁盘读取的时候,可能不阻塞应用程序。文件系统也有可能使用类似的手段来预热缓存(预取)

磁盘vs应用程序IO
磁盘IO是多个内核组件的终点,包括文件系统和设备驱动。磁盘IO与应用程序发出的IO在频率和大小上都不匹配,背后有很多原因
文件系统放大、缩小和不相关的IO。
由于系统内存短缺造成的换页。
设备驱动IO大小:IO大小 的向上取整,或者IO的碎片化。
出乎意料的不匹配会让人感到困惑

磁盘类型
市面上最常用 的两种磁盘顾炎武是磁性旋转机械盘和基于闪存的SSD。这两种磁盘都提供了永久的存储;与易失性存储相反,里面存储的内容在断电之后仍然不会丢失。
磁性旋转盘
又被称为硬盘驱动器(hard disk drive,HDD),这种类型的磁盘由一块或者多志盘片构成,称为磁碟,上面涂满了氧化铁颗粒。
SSD正式替代旋转磁盘,可以想象有一天这些旋转磁盘都装饰退役。

寻道和旋转
磁盘旋转磁盘的慢IO通常由磁头寻道时间和盘片旋转时间造成,这二者通常需要花费数毫秒。最好的情况是下一请求的IO正好位于当前服务IO的结位置,这样,磁头就不需要那首或者额外等待盘片旋转。前面曾有叙述,这是连续IO,而需要磁盘那道或者等待盘算旋转的IO被称为随机IO
可以聊低寻道和旋转等待时间
缓存:彻底消除IO。
文件系统布置和行为,包括写时复制。
把不同的负载分散到不同的磁盘,避免不同负载之间的寻道。
把不同的负载移到不同的系统。
电梯寻道,磁盘自身执行。
高密度磁盘,可以把负载落盘的位置变得更紧密。
分区配置,例如短行程。
另外一个降低旋转延时时间的办法就是使用更快的磁盘。
磁盘有多种不同的旋转速度,包括包分钟5400转、7200转、10000转和15000转rpm

理论最大吞吐量
如果已知一块磁盘每个磁道上的最大扇区数,磁盘吞吐量可以通过以下公式计算出来:
最大吞吐量 = 每磁盘最大扇区数 * 扇区大小 * rpm / 60s
这个公式对于精确盘中此类信息的较早磁盘较为有用。现代磁盘给操作系统提供了一个虚拟的磁盘镜像,这类属性是虚拟的。

短行程
指的是只把磁盘外侧的磁道用来服务负载,剩下的部分要么留着不用,要么留给那些低吞吐量的负载(例如归档)。由于磁头移动被限制在了一人较小的区域,寻道时间降低了,并且由磁头在空闲时靠在外侧边缘,空闲后的第一次那首时间也降低了。外侧的磁盘由于扇区分区的缘故具有更高吞吐量。在浏览公开的磁盘测试数据时要特别小心的是否采用了短行程分布,特别是那些不公布成本的数据,很可能使用了很多短行程磁盘。

扇区分区
磁盘的磁道长度各不相同,磁盘中心区域的磁盘较短,而外侧的磁道较长。相比固定的每磁道扇区数(和位数),由于物理上较长磁盘能够写入更多的扇区(又称为多区域记录)增加了扇区个数。

扇区大小
存储行业为磁盘设备开发了一种新标准,叫做高级格式(Advanced Format),以支持更大的扇区大小。特指4KB。这降低了IO计算的开销,提高了吞吐量,降低了每个扇区存储的元数据量。磁盘固件仍然可以通过一种叫做高级格式512B的仿真标准来提供512B大小的扇区。具体取决于各个磁盘,这可能会增加写开销,在把512映射到4KB扇区时造成一个额外的读-改-写周期。其他已知的问题还包括不对齐的4KB IO,横跨两个扇区,放大服务请求扇区IO

磁盘缓存
这些磁盘共有的一部件是一小块内存RAM,用来缓存读取的结果和缓冲要定稿的数据。这块内存还允许IO命令在设备上排队,以更高效的方式重新排序。这被称为标记命令,而在SATA上名为原生指令排队

电梯寻道
是提高命令队列效率的一种方式。它根据磁盘位置把IO重新排序,最小化磁头移动。结果类似大楼的电梯,不以楼层请求的顺序提供服务,而是在大楼里上上下下扫一遍,并在当前请求的楼层停靠。
这种行为可以通过检查磁盘IO的跟踪记录进行验证,把IO按照结束时间排序和按照开始时间排序,结果并不一样:IO完全是乱序的。

ECC
磁盘在每个扇区的结尾存储一个纠错码,这样驱动器可以在数据被读出时进行验证,并有可能纠错。

振动
虽然磁盘设备制造都非常清楚振动的问题,但这些问题在业界的了解程序并不高,也未得到相应的重视。

台式磁盘
当前有一类旋转磁盘的性能问题我们称之为怠工磁盘。这些磁盘有时返回很慢的IO,超过1s,却不报造任何的错误。事实上它最好报告一个错误而不是托这么久才返回,因为如果这样,操作系统或者磁盘控制器就可以实施一些改正性的措施,例如在冗余的环境下把磁盘下线并且报告错误。怠工磁盘比较麻烦,特别当它们作为虚拟磁盘的一部分由存储阵列暴露出来时,这种情况下操作系统并不能直接看到它们,因而提高了问题的难度

磁盘数据控制器
机械磁盘给系统提供了一套简单的接口,并表明了固定的每磁盘扇区数比例和连续和可寻址偏移量范围。事实上磁盘上的一切受磁盘数据控制器掌控–一个磁盘内部的微处理器,由固件中的逻辑控制。控制器决定磁盘如何排布这些可寻址的偏移量,其中可以实现一些算法,例如扇区。要有这个意识,但是很难分析–操作系统无法得知磁盘数据控制器的内部情况。

固态驱动器
因为使用固态电子元器件得名,存储可以编程的非易失性存储器的形式存在,一般比旋转磁盘的性能高得多。没有了移动部件,这些磁盘物理上可以使用更长的时间,也不会因为振动的问题影响性能。
这类磁盘的性能通常在不同的偏移量下保持一致,对于给定的IO大小也能预测出IO延时。负载的随机和连续特征不再像旋转磁盘那样事关重大。所有的这些降低了研究和容量规划的难度。

闪存
基于闪存的SSD是一种读性能非常高的存储设备,尤其在随机读方面比旋转磁盘高了好几个数量级。大多数使用NAND闪存制造,使用基于电子的陷阱电荷存储介质,可以在没有能源的情况下永久地存储电子。名称里的闪和数据的写入方式有关,要求一次性擦除整个存储块并重写内容。由于这种定稿的开销,闪存的读写性能不对称:读得快写得慢

控制器
SSD的控制器有以下任务
输入:每个页面的读和写,只能写已擦除的页面,一次性擦除32-64页
输出:仿真一个硬盘驱动器的块接口,任意扇区的读或写。
控制器的闪存转换层负责输入和输出之间的转换,同时必须跟踪空闲块。事实上它使用自己的文件系统来完成这个工作,例如一个日志结构文件系统。
对于写负载来说,写的特征可能会成为问题,特别是当写入的IO尺寸大小闪存块大小的时候。

寿命
把NAND闪存用作存储介质有好几个问题,包括燃尽、数据消失和读干扰。SSD控制器可以通过移动数据来解决这些问题。通常它使用磨损均衡技术,把定散布在不同的数据块里以减少单块上的写周期。而超量配置存储则预留额外的空间,这需要时可以拿出来顶替坏块。
虽然这些技术提高了寿命,SSD每个块的写入周期仍然是有限的,具体取决于闪存的类型驱动采用的缓解策略。

病理学
下面是一些需要知道的闪存可能出现的问题:
由于老化造成的延时离群点。另外SSD还会多次尝试,以读取正确的数据(通过ECC检查)
由于碎片化造成的高延时(清理FTL块映射的格式化可能可以解决这个问题)
由于SSD做内部压缩造成的低吞吐量。

接口
接口是驱动器支持与系统通信的协议,一般通过一个磁盘控制器实现。下面简单介绍了SCSI、SAS和SATA接口。你需要检查当前的接口和支持的带宽,因为一段时间后随标准的的推陈出新,它们就会发生变化 。
SCSI
并行SCSI由于采用了共享总线,会因总线竞争而导致性能问题。
SAS
串行SCSI接口被设计成一种调整点对点传输,避免并行SCSI的总线冲突问题。

存储类型
存储可以通过多种方式提供给服务器使用。磁盘设备、RAID、存储阵列和网络连接存储

磁盘设备
最简单的架构是服务器里有几块磁盘,每一个都由操作系统分别控制。磁盘连接到一个磁盘控制器,控制器可能是主板上的内置电路或者扩展卡,让磁盘设备被发现并且可以访问。这个架构下磁盘控制器仅仅作为一个管道,使得系统可以与磁盘通信。典型的个人电脑或者笔记本电脑就有一个磁盘以这种方式连接。

RAID
高级磁盘控制器可以为磁盘设备提供冗余独立磁盘阵列的架构。RAID可以把磁盘组合成一个又大双快又可靠的虚拟磁盘。控制器通常包含一个板载缓存RAM以提高读和写的性能。

由磁盘控制器提供的RAID称为硬件RAID。专用硬件上执行大量消耗CPU的校验和以及奇偶校验更为高效。然而,处理器的不断进步使得CPU开始有富余的周期和核心,减少卸载校验计算的需要。一些存储解决方案已经回到了软件RAID这样即降低了复杂度和硬件开销,又提高 了操作系统的监控。

种类
RAID-0虽然性能最好,但由于缺乏冗余,在大多数生产环境不会采用
0 连接 一次填充一块盘 由于多块盘的参与,还是可以提高随机读的性能
0条带 并发使用盘,把IO分割多给多块盘 最好的随机和连续IO性能
1镜像 多块盘为一组,存放相同的内容,互为备份 不错的随机和连续读性能(可以从所有的盘同时读取,取决于现实)。写受限于镜像中最慢的盘,吞吐量的开销加倍
10 RAID-0条带化建立在RAID-1组上提供容量和冗余,互为备份 性能和RAID-1差不多,但让更多组盘参与,类似RAID-0,增加了带宽
5 数据存储在条带上,横跨多块盘,还有额外的奇偶校验信息提供冗余 由于读-改-写周期和校验和计算造成写性能较差
6 每个条带有两块校验盘的RAID-5 类似RAID-5,不过更差

读-改-写
当包含校验和的数据以形式存储时,就像RAID-5,写IO可能会导致额外的读IO和计算时间。这是由于写小于条带宽度,所以需要读出整个条带,修改数据,重新计算校验和,然后再把整个重新写入。

存储阵列
可以把许多磁盘接入系统。它们使用高级磁盘控制器,这样RAID可以配置使用,它们通常也提供一个更大的缓存以提高读和写和性能。这些缓存通常由池供电,这样能以写回模式工作,如果电池失败,可以转回到穿模式,并很快由于等待读-改-写周期造成突然的写性能下降而引起注意。

网络连接存储
NAS通过现有网络暴露给系统,支持的网络协议有NFS、SMB、CIFS或者iSCSI,通常由名为NAS设备的专用系统提供。

操作系统磁盘IO栈
一个磁盘IO栈里的组件和层次取决于操作系统、版本和采用的罗硬件技术

块IO设备一般可以通过操作系统性能工具监控iostat,它也是静态跟踪的一个通常位置,直接到最近还可以使用动态跟踪。Linux改进了内核的这一部分,加上了一些特性,组成了块层

电梯层提供了通用功能,例如排序、合并以及聚合请求发送。这包括了前面描述过的降低旋转磁盘头移动的电梯寻道算法。

这些功能成功提高了吞吐量,降低了IO延时。IO调度器使IO能够排队排序以优化发送,具体由附加的高度策略决定。这可以更进一步提高 并更公平地平衡性能,特别是对那些有着高IO延时的设备而言更有意义。

空操作:不高度,在高度被认为没有必要时使用。
截止时间:试图强制给延时设定截止时间,例如,可以毫秒单位选择和写的失效时间,对于需要确定的实时系统较有帮助。有些IO请求因为新发起的IO插队而一直得不到磁盘资源,造成了延时离群点。有可能是写把读饿坏了,也有可能是由于采用了电梯寻道,一个区域里的密集IO饿坏了其他区域的IO。
预期:截止时间调度器增加版,通过记性方法预测IO性能,提高整体吞吐量。具体例子如下,由于预测在附近的磁盘位置会有另一个读请求到达,在读之后就暂停几毫秒

    CFQ:完全公平队列高度器把IO时间片分配给进程,类似CPU调度,确保磁盘资源的公平使用。它还允许通过ionice命令对用户进程设定优先级和类别。

方法
工具法 观察法
USE方法 观察分析
性能监控 观察分析,容量规划
负载特征归纳 观察分析、容量规则
延时分析 观察分析
事件跟踪 观察分析
静态性能调优 观察分析,容量规划
缓存调优 观察分析、调优
资源控制 调优
微基准测试 实验分析
伸缩 容量规划,调优

这些可以单独或者综合使用。调查磁盘问题时,我的建议是按以下顺序使用这些策略:USE方法、性能监控、负载特征归纳、延时分析、微基准测试、静态分析和事件跟踪。

工具法
iostat:使用扩展模式寻找繁忙磁盘(超过60%使用率),较高的平均服务时间(超过大概10ms),以及高IOPS(可能)。
iotop:发现哪个进程引发了磁盘IO。
dtrace/stap/perf:包含了iosnoop工具,仔细检查磁盘io延时,以发现延时离群点
磁盘控制器专用工具(厂商提供)

USE方法
磁盘设备:使用率:设备忙碌时间;饱和度:IO在队列里等待和程度;错误:设备错误。
第一个要检查的是错误。它们可能由于系统工作正常而被忽略–只是慢多了–但磁盘失效了:一般磁盘被配置在一个能够容忍失效的冗余盘阵里。与操作系统里看到的标准磁盘错误计数器不同,磁盘驱动器支持更多种类的错误计数器,并且可以通过特殊工具查看

磁盘控制器:
使用率: 当前值对比最大吞吐量,对操作频率也同样的比较。
饱和度:由于控制器饱和造成的IO等待程度。
错误:控制器错误。
此处指标率指吞吐量(每秒字节数)和操作频率(每秒操作数)。操作包括了读写以及其他的磁盘命令。吞吐量和操作频率也可能受限于磁盘控制器和系统这间的传输,以及磁盘控制器和每块磁盘之间的传输总线。每个传输总线都需要进行同样的检查:错误、使用率、饱和度。

磁盘控制器和传输总线常常被忽视。幸运的是,由于它们的上限常超过了连接的磁盘,因此这一般不是系统瓶颈所在。如果磁盘吞吐量或者IOPS永远稳定在一个水平,尽管负载有所不同,那么问题就可能出在磁盘控制器或者传输总线上了。

性能监控
性能监控可以发现一段时间内存的问题和行为的模式。
磁盘使用率
响应时间
数秒内100%磁盘使用率很可能意味着问题。超过60%的使用率可能就会因为不断增长的队列面而导致糟糕的性能,具体取决于你的环境。

负载特征归纳
下面是用来刻画磁盘IO负载的几项基本特征,这些数据放在一起,能够刻画出磁盘工作任务的一个大致模样:
IP频率
IO吞吐量
IO大小
随机和连续比例
读写比
描述了随机和连续比例、读写比和IO大小。IO频率IOPS和IO吞吐量的定义。
这些特征每一秒都在发生变化,特别是对于寻些使用了写缓存和定时刷新的应用程序及文件系统而言。为了更好地归纳特征,除了平均值还要记录最大值,最好是检查一段时间内值的全分布。

高级负载特征归纳/检查清单
归纳负载特征还可能需要包括其他一些细节信息。可以考虑下面列出的这些项目,在深度研究磁盘问题时可以用来做检查清单。
系统IOPS是多少?每个磁盘呢?每个控制器呢?
系统吞吐量是多少?每个磁盘呢?每个控制器呢?
哪个应用程序或者用户正在使用磁盘?
哪个文件系统或者文件正被访问?
碰到什么错误了吗?这些错误是源于非法请求,还是磁盘的问题?
IO在可用磁盘之间均衡吗?
每条参与的传输总线上的IOPS是多少?
每条参与的传输总线上的吞吐量是多少?
发出了哪些非数据传输磁盘命令?
为什么会发起磁盘IO(内核调用路径)?
磁盘IO里应用程序同步的调用占到多少?
IO到达时间的分布是什么样的?

性能特征归纳
每块有多忙(使用率)?
每块盘的IO饱和度是多少(等待队列)?
平均IO服务时间是多少?
平均IO等待时间是多少?
是否存在高延时的IO离群点?
IO延时的全分布是什么样的?
是否有例如IO流控的系统资源控制,存在并且激活了吗?
非数据传输磁盘命令的延时是多少?

延时分析
调查一般止于磁盘接口这一层:发起IO请求和完成中断之间的时间。如果这个时间与应用程序感受到的IO延时一致时,通常可以安全地推断出IO延时源于磁盘,这样你就可以专心调查其中的原因。如果延时有所不同,则需要从操作系统里的其他层发,找其他来源。

事件跟踪
事件跟踪指的是每个IO事件的信息都被跟踪并分别记录在案。对于观察分析方法而言,这是最后的手段。由于捕捉和保存这些信息的缘故,它增加了一些性能开销。这些信息通常写入日志文件以供日后分析。对于每个IO,日志文件应该至少包括以下信息
磁盘设备ID
IO类型
IO偏移量
IO大小
IO请求时间戳:IO发到设备的时间(也称为IO策略)
IO完成时间戳:IO事件完成的时间(完成中断)
IO完成状态:错误

其他信息还可能包括的话PID、UID,应用程序名称、文件名和所有非数据传输命令的事件(以及这些命令的自定详细信息)。
根据IO请求时间戳和完成时间戳可以计算出磁盘IO延时。在阅读日志时,单独排序第一项有帮助----可以看到磁盘IO是如何被设备重新排序。到达的分布也可以从时间戳里看出来。
由于磁盘IO是分析大热门,常常有静态跟踪点可以加以利用,用来跟踪请示和完成。另外,还可以使用动态跟踪进行高级分析,并捕捉下面这些类似的跟踪日志:
块设备驱动IO
接口驱动命令(例如sd)
磁盘设备驱动命令

静态性能调优
现在有多少块盘?是什么类型?
磁盘固件是什么版本?
有多少个磁盘控制器?接口类型是什么?
磁盘控制器卡是插到高速插槽上的吗?
磁盘控制器的固件是什么版本?
配置了RAID了吗?是怎么配置的,包括条带宽度?
多路径是否可用并配置了吗?
磁盘设备驱动是什么版本?
存储设备驱动有什么操作系统的缺陷、补丁?
磁盘IO有资源控制吗?

缓存调优
系统里可能存在多种不同的缓存,包括应用程序、文件系统、磁盘控制器和磁盘自己。总体来说,先检查有哪些缓存,接着看它们是否投入使用、使用的情况如何、缓存大小,然后根据缓存调整负载,根据负载调整缓存。

资源控制
操作系统可能会提供一些控制方法,把磁盘IO资源分配给一些或者几组进程。控制可能包括固定极限的IOPS和吞吐量,或者通过更加灵活的方式共享资源。

微基准测试
方向:读或写
磁盘偏移量模式:随机或连续
偏移量范围:全盘或小块范围
IO大小:512B(通常最小值)~1MB
并发度:同时发起的IO数量,或者执行IO的线程数。
设备数量:单块磁盘测试,或者多块磁盘(以得到控制器和总线限制)

磁盘
最大磁盘吞吐量MB/s:128KB读,连续
最大磁盘操作频率IOPS:512B读,仅对偏移理0
最大磁盘随机读IOPS:512B读,随机偏移量
读延时剖析(平均毫秒数):连续读,按照512B、1KB,2KB,4KB重复测试
随机IO延时剖析:512B,按照全地址扫描,指定开始地址区间,指定结束地址区间重复测试。

磁盘
最大磁盘吞吐量:128KB,连续。
最大磁盘操作频率IOPS:512B读,仅对偏移量。
最大磁盘随机读IOPS:512B读,随机偏移量。
读延时剖析(平均毫秒数):连续读,按照512B、1KB、2KB、4KB重复测试。
随机IO延时剖析:512读,按照全地址扫描,指定开始地址区间,指定结束地址区间重复测试。

磁盘控制器
最大控制器吞吐量MB/s:128KB,仅对0地址。
最大控制器操作频率IOPS:512读,仅对0地址。
可以一个个地施加负载,并注意极限,要找到一个磁盘控制器的极限可能需要超过一打的磁盘。

伸缩
下面是一个简单的基于资源的容量规划方法
1、确定目标磁盘负载的吞吐量和IOPS。
2、计算支撑这个负载需要的磁盘数量。记得考虑RAID配置。
3、计算支撑这个负载需要的磁盘控制器数量。
4、检查未超过传输的极限,并按需伸缩传输线。
5、计算单位磁盘IO的CPU周期数,以及需要的CPU数量。

分析
iostat 各种单个磁盘统计信息
sar 磁盘历史统计信息
pidstat/iotop 按进程列出磁盘IO使用情况
blktrace 磁盘IO事件跟踪
Dtrace 自定义静态和动态跟踪
megaCli LSI控制器统计信息
smartctl 磁盘控制器统计信息

iostat
汇总了单个磁盘的统计信息,为负载特征归纳、使用率和饱和度提供了指标。它可以由任何用户执行,通常是在命令行调查磁盘IO问题使用的第一个命令。统计信息的来源直接由内核维护,因此这人工具的开销基本可以忽略不计。
-c 显示CPU报告
-d 显示磁盘报告
-k 使用KB代替512B块数目
-m 使用MB代替512B块数目
-p 包括单个分区的统计信息
-t 时间戳输出
-x 扩展统计信息
-z 不显示空活动汇总

默认情况下,iostat显示一行系统总结信息,包括了内核版本、主机名、日期、架构和CPU数量,然后是自启动以来的CPU(avg-cpu)和磁盘块设备(在Device:之下)统计信息。每个磁盘设备都占一行,基本信息在数据列中。
tps:每秒事务数IOPS   
kB_read/s,kB_wrtn/s: 每秒读取KB数和每秒写入KB数
kB_read,kB_wrtn:总共读取和写入的KB数。

例:包括了仅输出磁盘报告的-d选项,以及路过全零的选项-z(空闲设备)
iostat -xkdx 1
输出如下:
rrqm/s:     每秒合并放入驱动请求队列的读请求数。
wrqm/s: 每秒合并放入驱动请求队列的写请求数。
r/s:    每秒发给磁盘设备的读请求数。
w/s:    每秒发给磁盘设备的写请求数。  
rkB/s:  每秒从磁盘设备读取的KB数。
wkB/s:      每秒向磁盘设备写入的KB数。

 非零的rrqm/s和wrqm/s项说明为了提高性能,连续的请求在发往设备之前已经被合并了。这个指标也是工作负载为连续的标志。r/s和w/s列显示了实际发给设备的请求数。

avgrq-sz:       平均请求大小,单位为扇区512B
avqu-sz:        在驱动请求队列和在设备中活跃的平均请求数。
await:          平均IO响应时间,包括在鸡翅请求队列里等待和设备的IO响应时间(ms)
r_await:        和await一样,不过只针对读(ms)
w_await:        和await一样,不过只针对写(ms)
svctm:          (推断)磁盘设备的IO平均响应时间(ms)
%util:          设备忙处理IO请求的百分比(使用率)

输出性能里最重要的指标是await。如果应用程序和文件系统使用了降低写延时的方法,w_await可能不那么重要,而你可以主要关注r_await。
对于资源使用和容量规划,%util仍然很重要,不过记住这只是繁忙度的一个度量。可以通过施加负载更好地了解这些设备:IOPS(r/s + w/s)以及吞吐量(rkB/s + wkB/s).

sar
系统活动报告器(system activity reporter)可以用来监控当前活动并被配置用来归档和报告历史统计信息。
sar磁盘汇总通过选项-d输出
sar -d 1
主要区别于iostat的指标:
tps:设备每秒数据传输量
rd_sec/s、wr_sce/s:每秒读取和写入扇区数

pidstat
默认输出CPU使用情况,还可以使用选项-d输出磁盘IO统计信息

DTrace
能从内核角度检查磁盘IO事件,包括块设备接口IO,IO调度器事件,目标驱动IO和设备驱动IO。
以下部分将介绍DTrace的磁盘IO分析功能,并演示如何在基于Linux和Solaris的系统中应用这些功能。

层次 稳定provider 不稳定provider
应用程序 取决于应用 pid
系统库 pid
系统调用 syscall
VFS fsinfo fbt
文件系统 – fbt
块设备接口 – fbt
目标驱动 – fbt
设备鸡翅 – fbt

io:::start: 一个IO请求被发到设备上
io:::done: 一个IO请求在设备上完成
io:::wait-start: 一个线程开始等待一个IO请求
io:::wait-done 一个线程等完了一个IO请求

args[0]->b_count: IO大小(字节数)
args[0]->b_blkno: 设备IO偏移量(块)
args[0]->b_flags: 位元标志位,包括表示读IO的B_READ。
args[0]->b_error: 错误状态
args[1]->dev_statname: 设备实例名+实例、小编号
args[1]->dev_pathname: 设备路径名
args[2]->fi_pathname: 文件路径名(如果知道)
args[2]->fi_fs: 文件系统类型

iotop
包含磁盘IO版本的top。
批量模式-b可以提供滚动输出(不清理屏幕)。-0显示IO进程每5秒输出一次-d5
iotop -bod5

iosnoop(1m)通过块设备接口同时跟踪所有磁盘,并为每个磁盘IO打印一条输出。它有多人可选项,以输出额外的细节。

blktrace
启用内核块驱动跟踪机制获取跟踪裸数据,供blkparse处理以产生可读的输出。为使用方便,btrace工具调用blktrace和blkparse下面两个命令是等价的
blktrace -d /dev/sda -o - | blkparse -i -
btrace /dev/sda

MegaCli
磁盘控制器(主机总线适配器)由系统外部的硬件和固件组成。操作系统分析工具,甚至是动态跟踪它无法直接观察到它们内部。有时它们的工作状态,可以通过仔细观察磁盘控制器如何响应一系列IO的输入和输出推断出来。

smartctl
磁盘有控制磁盘操作的逻辑,包括排除、缓存和错误处理。与磁盘控制器类似,操作系统不能直接看到磁盘的内部行为,这些信息是通过观察IO请求和延时来推断的。

许多现代的驱动器提供了SMART(自监控、分析和报告技术)数据,包括了多种健康统计信息。

smartctl  --all -d megaraid, 0 /dev/sdb
高估的另一个重要领域是存储配置,这是静态性能高估方法研究的一部分。

操作系统可调参数
包括ionice、资源控制和内核可调参数
linux中的ionice命令可以设置一个进程的IO调度级别和优先级。调度级别为整数,
0,无:不指定级别,内核会挑选一个默认值 —尽力,优先级则根据进程nice值选定。
1、实时:对磁盘的最高级别访问。
2、尽力:默认调度级别,包括优先级0~7,0为最高级。
3、空闲:在一段磁盘空闲的期限过后才允许进行IO

资源控制
linux中的控制组块IO子系统为进程和进程组提供了存储设备资源控制机制。控制可以是按比例的权重方式(类似共享)或者一个固定的限制。限制可以单独针对读和写,以及对IOPS或者吞吐量

可调参数
/sys/block/sda/queue/scheduler:选择IO高度器策略,是空操作,最后期限、an预期还是cfq

sd_max_throttle 的调优信息可以通过分析同时运行的命令数获得,并发现它和上限之间的距离。

磁盘控制器可调参数
可用的磁盘控制器可调参数取决于磁盘控制器型号和厂商。

网络分析是跨硬件和软件的。这里的硬件指的是物理网络,包括网络接口卡,交换机,路由器和网关。这里的系统软件指的内核协议,通常是TCP、IP,以及每个涉及的协议行为

接口:interface port,指网络物理连接器。指操作系统可见并且配置的网络接口端口的逻辑实例
数据包:通常指IP级可路由的报告。
帧:一个物理网络级的报文,例如以太网帧
带宽:对应网络类型的最大数据传输率,通常以b/s为单位测量
吞吐量:当前两个网络端点之间的数据传输率,以b/s或者B/s为单位测量。
延时:网络延时指一个报文往返端点所需要的时间,或者指建立连接所需的时间,不包括之后的数据传输时间

TCP/IP
应用层 传输层 网络层 数据链路层 物理层

OSI模型
应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
应用层HTTP、DNS 表示层XDR、MIME 会话层套接字 传输层 IP ICMP 数据链路层 以太网 物理层 铜缆光缆

网络和路由
网络是一组由网络协议地址联系在一起的相连主机。报文隔离的基础,使其仅在源和目标网络间传输,这样能更有效地使用网络基础架构。

路由管理称为包的报文跨网络传输
成对主机间由单手unicast传输连接。多播传输允许一个发送者可能跨越多个网络同时传输给多个目标。它的传递依赖于路由配置的支持。
路由包需要的地址信息包含在IP数据包头中

网络协议具有不同的性能特征,源自初始的协议设计、扩展,或者软硬件的特殊处理。
例如不同的IP协议版本IPv4和IPv6,可能会由不同的内核代码路径处理,进而表现 出不同的性能特性。
通常系统的可调参数也能影响协议性能,如修改缓冲区大小,算法及不同的计时器装置。这些特定协议的区别在稍后的部分介绍。
包的长度以及它们的负载也会影响性能,更大的长度增加吞吐量并且降低包的系统开销。对于TCP、IP和以太网,包括封装数据在内的包长度介于54~9054B,其中包括54B(或者更长,取决于选项或者版本)的协议包头

封装
包封装会添加元数据到负载前(包头)、之后(包尾),或者两者。这不会修改负载数据,尽管它会轻微地增加报文总长度,而这也会增加一些传输的系统开销

包长度
包的长度通常受限于网络接口的最大传输单元 MTU长度,许多以太网中它设置为1500B。以太网支持接近9000B的特大包(帧),也称为巨型帧。这能够提高网络吞吐性能,同时因为需要更少的包而降低了数据传输延时。

延时是一个重要的网络性能指标并通用不同的方法测量,包括主机名解析时、ping延时、连接延时、首字节延时、往返时间,以及连接生命周期。这些都是以客户机连接到服务器的延时来描述的。

主机名解析延时
与远程主机建立连接时,主机名需要解析为IP地址,例如用DNS解析。该过程所需的时间独立计算为量为主机名解析延时,该延时最坏的情况是主机名解析超时,通常需要几十钞。
某些情况下应用程序的运行不需要解析主机名,因此可以禁用以避免这种延时。

ping延时
用ping命令测量的ICMP echo请求到echo响应所需的时间。该时间用来衡量主机对之间包括网络跳跃的网络延时,而且它测量的是包往返的总时间。因为简单并且随时可用,它的使用很普遍:许多操作系统默认会响应ping

在接收端,ICMP echo请求通常做中断处理并立即返回,这尽可能地减少了内核代码运行的时间。在发送端,由于时间戳由于用户层测量,并且包括内核上下文切换和内核代码路径时间,它可能包括少量附加的时间。

缓冲
尽管存在多种网络延时,利用发送端和接收端的缓冲,网络吞吐量仍能保持高速率。较大的缓冲可能通过在阻塞和等待确认前持续传输数据缓解高往返延时带来的影像。
TCP利用缓冲以及可变的发送窗口提升吞吐量。网络套接字也保有缓冲,并且应用程序可能也利用它们自己的缓冲在发送前聚集数据。
外部的网络组件,如交换机和路由器,也会利用缓冲提高它们的吞吐量。遗憾的是这些组件中使用的调整缓冲,如果包长时间在队列中,会导致被称为缓冲膨胀的问题。这会引发主机中的TCP阻塞避免,它会限制性能。
缓冲功能由端点提供效果最佳,它通常遵守端到端争论的原则。

连接积压队列
另一类型 的缓冲用于最初的连接请求。TCP的积压队列实现会把SYN请求在用户级进程接收前列队于内核中。过多的TCP连接语法超过进程当前的处理能力,积压队列会达到极限而丢弃客户机可以迟些时候再重新传输的SYN包。这些包的重新传输会增加客户连接的时间。
测量因积压队列导致的丢包是一种衡量网络连接饱和度的方法。

接口协商
带宽:例如,10、100、1000、 10 000MS/s
双工(模式):半双工或者全双工。

网络接口通常用它们的最大带宽及协议描述,例如,1GBs以太网。必要时,这个接口可以协商使用较低的速率。如果对端不能支持更高的速率,或者不能处理连接介质的物理问题(线路故障)时,这就可能发生。
全双工模式允许双向同时传输,利用分离的通道传输和接收能利用全部带宽。半双工模式仅允许单向的传输。

使用率
网络接口的使用率可以用当前的吞吐量除以最大带宽来计算。考虑以可变的带宽和自动协商的双工模式,计算它不像看上去这么直接。
对于全双工,使用率适用于每个方向并且用该方向当前的吞吐量除以当前协商的带宽来计量。通常只有一个方法最重要,因为主机通常是非对称的:服务器偏重传输,而客户端偏重接收。

本地连接
网络连接可以发生于同一个系统的两个应用程序间。这就是本地连接而会使用一个虚拟的网络接口:回送接口。
分布式的应用程序环境常常会划分为通过网络通信的逻辑模块。这包括web服务器、数据库服务器和应用服务器。如果它们运行于同一个主机,它们的连接通过本机实现。
通过IP与本机通信是IP套接字的进程间通信技巧。另一个技巧是UNIX域套接字UDS,它在文件系统中建立一个用于通信的文件。由于省略了TCPIP的内核代码以及协议包封闭的系统开销,UDS的性能会更好。

架构
本节介绍网络架构:协议、硬件和软件。这些注重性能特性的部分已经作为性能分析和高估的背景知识总结过。有关包括网络通信概述在内的具体信息。

协议
本节将总结TCP和UDP的性能特性

TCP
传输控制协议TCP是一个常用 的用于建立可靠网络连接的互联网标准。TCP由以及之后增补定义
就性能而言,即使在高延时的网络中,利用缓冲和可变窗口,TCP也能够提高吞吐量。TCP还会自用阻塞控制以及由发送方设置的阻塞窗口,因而不仅能保持调整而且在跨越不同并变化的网络中保持合适的传输速率。阻塞控制能避免会导致网络阻塞进而损害过度发送。
以下是TCP性能特性总结,包括了自最初定义以后的增补
可变窗口:允许在收到确认前在网络上发送总和小于窗口大小的多个包,以在高延时的网络中提供高吐量。窗口的大小由接收方通知以表明当前它愿意接收的包数量。
阻塞避免:阻止发送过多数据进而导致饱和,它会导致丢而损害性能。
缓启动:TCP阻塞控制的一部分,它会以较小的阻塞窗口开始而后按一定时间内接收到的确认ACK逐渐增加。如果没有收到,阻塞窗口会降低。
选择性确认SACK:允许TCP确认非连续的包,以减少需要重传输的数量。
快速重传输:TCP能基于重复收到的确认重传输被丢弃的包,而不是等待计时器超时。这只是往返时间的一部分而不是通常更慢的计时器。
快速恢复:通过重设连接开始慢启动,以在检测到重复确认后恢复TCP性能。
三次握手
连接的建立需要主机间的三次握手。一个主机被动地等待连接,另一方主动地发起连接。作为术语的澄清:被动和主动源自,然而它们通常按套按字API分别被称为侦听和连接,按客户端、服务器模型,服务器侦听而客户端发起连接。

图中指出客户端的连接延时,它截止于收到最终的ACK,之后数据传输开始
图中展示的是最佳状况下的握手延时。包可能被丢弃,并由于超时和重新传输而增加延时。

重复确认检测
快速重传输和快速恢复会利用重复确认检测算法。它运行于发送方,其工作方式如下:
1、发送方发送一个序号为10的包
2、接收方返回一个序号为11的确认包
3、发送方发送包11、12、13
4、包11被丢弃
5、接收方发送序号为11的确认包以响应包12和13,表明它仍然在等待
6、发送方收到重复的序号为11的确认包。

阻塞控制:Reno和Tahoe
Reno:三次重复确认触发器,即阻塞口减半、慢启动阈值减半、快速传输和快速恢复
Tahoe:三次重复确认触发器,即快速重传输、慢启动阈值减半、阻塞窗口设置为最大报文段长度MSS和慢启动状态

Nagle算法
该算法通过推迟小尺寸包的传输以减少网络中的这些包的数量,从而使更多的数据能到达与合并。仅当有数据进入数据通道并且已经发生延时时,它才会推迟数据包。
系统可能会提供禁用Nagle的可调参数。当它的运行与延时ACK发生冲突时该参数就显得必要了。

延时ACK
该算法推迟最多500ms发送ACK,从而能合并多个ACK。其他TCP控制报文也能合并,进而减少网络中包的数量。

SACK与FACK
TCP选择性确认SACK算法允许接收方通知发送文收到非连续的数据块。如果缺乏这一特性,为保留顺序确认的结构,一个丢包会最终导致整个发送窗口被重新传输。这会损害性能,大多数支持SACK的现代操作系统都会避免这种情况。

UDP
用户数据协议UDP是一个常用于发送网络数据报文的互联网标准。就性能而言,UDP提供如下特性。
简单:简单而短小的协议头降低了计算与长度带来的系统开销。
无状态:降低连接与传输控制带来的系统开销。
无重新传输:这些给TCP增加了大量的连续延时。

硬件
网络硬件包括接口、控制器、交换机和路由器。尽管这些组件由其他工作人员(网络管理员)来管理,理解它们的运行还是必要的。

接口
物理网络接口在连接的网络上发送并接收称为帧的报文。它们处理包括传输错误在内的电器、光学或者无线信息。
接口类型基于第2层网络标准。每个类型都存在最大带宽。高带宽接口通常延时更低,而成本更高。这也是设计新服务器的一个关键选择,要平衡服务器的售价与期望的网络性能。

控制器
物理网络接口由控制器提供给系统。它集成于系统板或者利用扩展卡。
控制器由微处理器驱动并通过IO传输通道接入系统。其中任意一个都可能成为网络吞吐量或者IOPS的瓶劲

交换机、路由器
交换机提供两个连入的主机专用的通信路径,允许多对主机间的多个传输不受影响。此技术取代了集线器,它在所有主机间共享所有包。当主机同时传输时,这种共享会导致竞争。接口以“载波侦听多路访问、碰撞检测”算法发现这种冲突,并按指数方式推迟直到重新传输成功。
路由器在网络间传递数据包,并且用网络协议和路由表来确认最佳的传递路径。在两个城市间发送一个数据包可能涉及十多个甚至更多的路由器,以及其他的网络硬件。路由器和路由经常是设置为动态更新的。因此网络能够自动响应网络和路由器的停机,以及平衡负载。在这意味着在任意时点,不可能确认一个数据包的实际路径。由于多个可能的路径,数据包有可能被乱序送达,这会引起TCP性能。

软件
网络通信软件包括网络栈、TCP和设备驱动程序。

网络栈
涉及的组件和层依操作系统的类型、版本、协议以及使用的接口而不同。描绘了一个通用的模型,展示其软件组件。
现代内核中网络栈是多线程的,并且传入的包能被多个CPU处理。传入的包与CU的映射可用多个方法完成:可能基于源IP地址哈希以平均分布负载,或者基于最近处理的CPU以有效利用CPU缓存热度以及内存本地性。

应用程序    库   系统调用    虚拟文件系统  套接字 传输控制协议  用户数据报协议 网际控制报文协议    地址解析协议  数据链接(通用网络驱动软件) igb bnx  网络接口卡

Linux系统中、TCP、IP以及通用网络驱动软件是内核的核心组件,而设备驱动程序是附加块。数据包以struct sk_buff数据类型穿过这些内核组件。
数据包的处理率能够通过调用多个CPU处理包和TCPIP栈来实现。记录了不同的实现方法
RSS,接帐端 缩放:现代的NIC能支持多个队列并且计算包哈希以置入不同队列,
RPS,接收数据包转向:对于不支持多个队列的NIC的RSS软件实现。
PFS、接收流转向: 不过偏向前一个处理套接字的CPU,以调高CPU缓存命中率以及内存本地性。
加速接收数据流转向:
XPS,传输数据包转向:对于支持多个传输队列的NIC,这支持多个CPU传输队列。
当缺乏数据包的CPU负载均衡策略时,NIC会中断同一个CPU,进而达到100%使用率并成为瓶颈
基于例如RFS实现的缓存一到性等因素而映射中断到多个CPU,能够显著提升网络性能。这也能够过irqbalancer进程实现,它能分配中断请求IRQ给CPU

TCP
突发的连接由积压队列处理。这里有两个些类队列,一个在TCP握手完成(也称为SYN积压队列)前处理未完成的连接,而另一个处理等待应用程序接收

早期的内核仅使用一个队列,并且易受SYN洪水攻击。SYN洪水是一种DOS攻击类型,它从伪造的IP地址发送大量的SYN包到TCP侦听端。这会在TCP等待完成握手时填满积压队列,进而阻止真实的客户连接。
有两个队列的情况下,第一个可作为潜在的伪造连接的集结地,仅在连接建立后才迁移到第二队列。第一个队列可能设置得很长以吸收海量SYN并且优化为仅存放最少的必要元数据。

利用套接字的发送和接收缓冲能够提升数据吞吐量。

对于写通道,数据缓冲在TCP发送缓冲区,然后送往IP发送。尽管IP协议有能力分段数据包,TCP仍试图通过发送MSS长度的段给IP以避免这种情况。这意味着发送单位对应分段单位,否则一个被丢弃的数据段会导致 整个分段前的数据包被重新传输。由于避免了分段和组装常规数据包,这种实现方式能提升TCP、IP栈的效率。

发送和接收和缓冲区大小是可调的。以在每个连接上消耗更多主内存为代价,更大的尺寸能够提升吞吐性能。如果服务器需要处理更多的发送或者接收,某个缓冲区可以设置得比另一个大。基于连接活跃度,Linux内核会自动增加这些缓冲区。

网络设备驱动
网络设备驱动通常还有一个附加的缓冲区--环形缓冲区--用于在内核内存与NIC间发送和接收数据包。
随着10GE以太网的引入,利用中断结合模式的利于的功能变得

方法
工具法 ——–观测分析
USE方法 ——–
负载特征分析
延时分析
性能监控
数据包嗅探
TCP分析
挖掘分析
静态性能调优
资源控制
微基准测试

这些方法可以单独使用或者混合使用。建议是从这些方法开始,按如下的次序使用:性能监测、USE方法、静态性能调优和工作负载特征归纳

工具法
工具法是一个可用工具的遍历,检查它们提供的关键指标。它有可能忽视这些工具不可见或者能见度低的问题,并且操作比较费时
netstat -s:查找高流量的重新传输和乱序数据包。哪些是高重新传输率依客户机而不同,面向互联网的系统因具有不稳定的远程客户会比仅拥有同数据中心客户的内部系统具有更高的重新传输率。
netstat -i:检查接口的错误计数器
ifconfig:检查“错误”、“丢弃”、“超限”
吞吐量:检查传输和接收的字节率,高吞吐量可能会因为到达协商的线速率而受到限制,它也可能导致系统中网络用户的竞争及延时。
tcpdump/snoop:尽管需要大量的CPU开销,短期使用可能就中以发现谁在使用网络并且定位可消除的不必要的操作。
dtrace/stap/perf:用来检查包括内核状态在内的应用程序与线路间选中的数据。

USE方法
可以用来定位差劲和跨所有组件的错误。对于 每个网络接口,及每个方向–传输TX与接帐RX
使用率:接口忙于发送或接收帧的时间。
饱和度:由于接口满负载,额外的队列、缓冲或者阻塞的程序
错误:对于接收,校验错误,帧过短(小于数据链路径报文头)或者过长、冲突;对于传输,延时碰撞

可以先检查错误,因为错误通常被快速检查到并且最容易理解。
操作系统或者监视工具通常不直接提供使用率。对于每个方向RX、TX,它能用当前的吞吐量除以当前的协商速度。当前的网络吞吐量以B/s测量并且包括所有协议报文头
对于实施了网络带宽限制的环球产,如出现在一些云计算环境中,除去物理限制之外,网络使用率或许需要按应用的限制来测量。
网络接口的饱和度和难以测量。由于应用程序能力比接口传输能力更快地发送数据,一定程序的网络缓冲是正常的。因为应用程序线程阻塞于网线传输的时间会随饱和度的增加而增加,这有可能是可以测量的。同时检查是否有其他与接口饱和度紧密相关的内核统计信息
TCP层的重传统计信息通常是现成的并且可作为网络饱和度的指标。然而,它们是跨服务器与客户机衡量,并且可能出现于任何一跳
USE方法可用于网络控制器,以及它们与处理器之间的传输通道。由于这些组件是观察工具比较少见,基于网络接口统计信息和网络拓扑推测指标可能更容易。

工作负载特征归纳
做容量规划、基准测试和负载模拟时,分析应用负载的特征是一项重要的演练。经由定位可削减的不必要操作,它可能促成一些最丰富的性能提升。
网络接口吞吐量、网络接口IOPS、TCP连接率:主动和被动,每秒连接数

除了描述这些系统级的特征,也可以用每个接口描述它们。如果观察到吞吐量达到线带率,就能够发现接口的瓶颈。如果应用了网络带宽限制(资源控制),可能在达到线速率前就节流网络吞吐量。

高级工作负载特征归纳、核对清单
分析工作负载特征归纳可以涵盖更多具体信息。需要缜密的研究CPU问题时,这可作为核对清单:
平均数据包的大小是多少?RX、TX?
协议是什么?TCP还是UPD?
活跃的TCP、UPD端口是多少?B/s、每秒连接数?
哪个进程在主动地使用网络?

延时分析
研究不同的延时有助于理解和表述网络性能。这包括网络延时–一个有些模样两可的术语,通常指的是连接初始化时间
系统调用发送、接受延时 套接字读取、写入调用耗时
系统调用连接延时 用于建立连接;注意有些应用以非阻塞的系统调用处理它
TCP连接初始化时间 三方握手所需要的时间
TCP首字节延时 连接建立到接收到第一个字节数据所需要的时间
TCP连接持续时间 建立到关闭所需的时间
TCP重传输 如果发送,会为网络IO增加数千毫秒的延时
网络往返时间 一个数据包从客户机到服务器再返回的时间
中断延时 从网络控制器接收一个数据包中断到它被内核处理的时间
跨栈延时 一个数据包穿越内核TCP、IP栈的时间

单位时间间隔平均:以客户机、服务器对计量最佳,以便隔离蹭网络的别
完整分布:直方图或者热图
每操作延时:列出每个事件包括源与目标的IP地址的具体信息
一个常见的问题源是出现TCP重传输所导致的延时异常值。使用包括最小延时阀值过滤在内的完整分布或者每操作延时跟踪能发现它们

性能监控
性能监测能发现当前的问题以及随着时间的推移的行为模式。它能捕促最终用户数量变化,包括分布式系统监测在内的定时活动,以及包括网络备份在内的应用程序活动。
关键网络指标如下。
吞吐量:网络接口接收与传输的每秒字节数,最好能够包括每个接口。
连接数:TCP每秒连接数,它是另一个网络负载的指标。
错误:包括丢包计数器。
TCP重传输:计量它是有帮助的,能与网络问题相关联。
TCP乱序数据包:也会导致性能问题

对于使用了网络带宽的环境,例如一些云计算环境,应用的限额统计数据也需要收集

数据包嗅探
数据包嗅探从网络捕促数据包,因而能以检查每一个的方式检查报文和数据。就CPU和存储系统开销而言,它的代价高昂,对于观察性分析这可能是取后手段。由于需要每秒处理数有时百万的数据包并且对任何系统开销都敏感,网络内核代码路径通常是对周期优化的。为试图减少这种系统开销,内核可能会利用环形缓冲区通过共享内存映射向用户级跟踪工具传递包数据,Linux是PF_RING选项而不是每数的包的PF_PACKET。
数据包捕促日志可以创建于服务器端,然后用其他工具分析。一些工具仅仅输出内容,其他的能做高级的包数据分析。尽管通读数据包捕促日志非常耗时,但它可能非常有启发性–显示当前网络中正在发生什么,以及数据包对间的延时。这使得应用工作负载特征归纳和延时分析方法成为可能。
时间戳
整个数据包,包括所有协议头、部分或全部负载数据、元数据:数据包数量、丢包数量
这个输出对每个数据包有一行总结,包括IP地址、TCP端口其他TCP包头的具体信息。由于数据包捕促是以消耗CPU为代价的活动,大多数实现都包含了丢弃事件的能力,而不是在过载时捕促它们。丢弃的数据包数量可能包含在日志中。
除了利用环形缓冲区外,数据包的实现通常允许用户提供过滤表达式并且用于内核过滤。这通过禁止不需要的数据包传递到用户去从而减少了系统开销。

TCP分析
TCP发送、接收缓冲的使用
TCP积压队列的使用
积压队列满导致的内核丢包。
阻塞窗口大小,包括零长度通知。
TCP TIME-WAIT间隔中接收到的SYN。
当一个服务器频繁地用相同的源和目的IP地址连接另一个服务器的同一个目标端口时,紧后一个行为可能成为一个可扩展性能问题。每个连接唯一的区别要素是客户端的源商品—一个短暂的端口–对于TCP它是一个16位的值并且可能进一步受到操作系统参数的限制。综合可能是60s和TCP TIME-WAIT间隔,调整率的连接会与新连接碰撞。这种情况下,发送一个SYN包,而那个短暂的端口仍然与前一个处于TIME-WAIT的TCP会话关联。如果被误认为是旧连接的一部分(碰撞),这个新的SYN包可能被拒收。为了避免这种问题。

挖掘分析
通过挖掘每个处理数据包的层次直到网络接口驱动,能按需要研究内核网络栈的内部运行。内部运行往往非常复杂,所以这是一个非常消耗时间的操作。
检查网络可调参数是否需要调整(而不是通过实验修改)
确认内核网络性能特性是否生效,例如包括CPU扇出和中断合并。
解释内核丢包。

静态性能调优
静态性能调优注重解决配置完成的环境中的问题。对于网络性能,检查静态配置中的如下方法
有多少网络接口可供使用?当前使用中的有哪些?
网络接口的最大速度是多少?
当前协商的网络接口速度是多少?
网络接口配置的MTU是多少?
网络接口是否使用了链路聚合?
有哪些适用于设备驱动的可调参数?IP层?TCP层?
有哪些可调参数已不再是默认值 ?
路由是如何配置的?默认路由是什么?
数据路径中网络组件的最大吞吐量是多少(所有组件,包括交换机和路由器背板)?
数据转发是否启用?该系统是否作为路由器使用?
DNS是如何设置的?它距离服务器有多远?
该版本的网络接口固件是否有已知的性能问题?
该网络设备驱动是否有已知的性能问题?内核TCP、IP栈呢?
是否存在软件施加的网络吞吐量限制(资源控制)?它们是什么?

资源控制
操作系统可能按连接类型、进程或者进程组,设置控制以限制网络资源。控制可能包括如下类型。
网络带宽限制:由内核应用的针对不同协议或者应用程序的允许带宽(最大吞吐量)
IP服务品质:由网络组件应用的网络流程优先排序。有多种实现方式,如IP报文头包括业务类型位,其中包括优先级,这些位已在新QoS方案中重定义,其中包括基区分服务。可能会存在其他协议层为同样目的应用的优先排序。
你的网络可能存在高低优先混合的流量。低优先级可能包括传输备份以及性能监视流量。高优先级可能是生产服务与客户机间的流量。任意一个资源控制方案都能节流低优先级流量,而为高优先级流量提供更令人满意的性能。

微基准测试
许多基准测试工具可用于网络。调查分析式应用程序环境的吞吐量问题是地,它们有助于确认网络能否至少达到预期的网络吞吐量。如果达不到,能用微基准测试工具调查网络性能。它们通常比应用程序更简单而且调试起来更快。网络经调优达到需要的速度后,可将注意力转回应用程序本身。
可测试的典型要素如下
方向:发送或者接收
协议:TCP或者UDP
线程数:
缓冲长度
接口MTU长度

如 10GB/s这样的调整网络接口,可能需要由多个客户线程驱动达到最大带宽。

分析
netstat 多种网络栈和接口统计信息
sar 统计信息历史
ifconfig 接口配置
ip 网络接口统计信息
nicstat 网络接口吞吐量和使用率
ping 测试网络连通性
traceroute 测试网络路由
pathchar 确认网络路径特征
tcpdump 网络数据包嗅探器
wireshark 图形化网络数据包检查器
Dtrace、perf TCP、IP栈跟踪:连接、数据包、丢包、延时

netstat
基于使用的选项,netstat命令能报告多种类型的网络统计数据,就像具有多种功能的组合工具。选项介绍如下:
默认:列出连接的套接字
-a:列出所有套接字的信息
-s:网络栈统计信息
-i:网络接口信息。
-r:列出路由表。
其他选项能修改输出,例如-n不能解析IP地址为主机名,以及-v(可用时)显示冗长的详细信息。
数据列包括网络接口Iface、MTU,以及一系列接收RX-和传输TX-的指标。
OK:成功传输的数据包
ERR:错误数据包
DRP:丢包
OVR:超限
丢包和超限是网络接口饱和的指针,并且能和错误一起用USE方法检查。
-c连接模式能与-i一并使用,每秒输出这些累积的计数器。它提供计算数据包速率的数据。
输出列出了多项按协议分组的网络数据,主要是来自TCP的。所幸的是,其中多数据有较长的描述性名称,因此它们的意思显而易见,不幸的是这些输出缺乏一致性而且有拼写错误,用程序处理这段文字比较麻烦
许多与性能相关的指标以加粗强调,用以指出可用的信息。其中许多指标要求对TCP行为的深刻理解,包括近些年引入的最新功能和算法。下面是一些值得查找的示例指标。
相比接收的总数据包更调整的包转发率:检查服务器是否应该转发路由数据包
开放的被动连接:监视它们能显示客户机连接负载
相比发送的数据段更高的数据段重传输率: 能支持网络的不稳定。这可能是意料之中的
套接字缓冲超限导致的数据包从接收队列中删除:这是网络饱和的标志,能够通过增加套接字缓冲来修复–前提是有足够的系统资源支持应用程序。
一些统计信息名称包括拼写错误。如果其他的监控工具建立在同样的输出上,简单地修复它们可能有问题。这类工具最好能从/proc资源读取这些统计信息,它们是/proc/net/snmp
和/proc/net/netstat 例如:
netstat可以接受以秒为单位的时间间隔,它按每个时间间隔连续地输出累加的计数器。后期处理这些输出可以计算每个计数器的速率。

sar
系统活动报告工具sar可以观测当前活动并且能配置为保存和报告历史统计数据
-n DEV: 网络接口统计信息
-n EDEV: 网络接口错误
-n IP: IP数据报统计信息
-n EIP: IP错误统计信息
-n TCP: TCP统计信息
-n ETCP: TCP错误统计信息
-n SOCk 套接字使用

ifconfig
命令能手动设置网络接口。它也可以列出所有接口的当前配置。用它来检查系统、网络以及路由设置有助于静态性能调优
这些计数器与之前介绍的netstat -i命令一致。

IP
命令能配置网络接口和路由,并且观测它们的状态和统计信息。

nicstat
最初为基于Solaris系统编写,nicstat这个开源工具输出包括吞吐量和使用率在内的网络接口统计信息。nicstat延续传统的资源统计工具。iostat和mpstat的网络。
tcpdump
可以捕促并检查网络数据包。它输出数据包信息到STDOUT,或者白马 数据包写入文件以供稍后的分析。后者通常更实用:过高的数据包速率导致了不能实时研究它们。
输出显示出被内核丢弃而没有传给tcpdump的数据包数量,这发生在数据包速率过高时
每一行输出显示数据包时间、它的源和目标IP地址,以及TCP报头值。研究它们能理解TCP内部工作的细节,包括高级功能如何服务于你的工作负载。
-n选项用来禁用IP地址解析为主机名。其他多种可用的选项包括打印冗余长的细节-v、链路层报头-e,以及十六进程地址转储。
在性能分析过程中,把时间戳列改为显示数据包间的时间差-ttt,或者自第一个数据包以来的时间
用表达式描述如何过滤数据包能聚集于感兴趣的部分。为了效率,这是在内核处理的
就CPU成本和存储而言,捕促数据包是昂贵的。在可能的情况下尽量短时间使用tcpdump以限制其对性能的影像

Wirekshark
对于深层次的分析用命令行会很费时,wireshark工具提供了一个捕促数据包和检查的图形化接口,并可以从tcpdump或者snoop的转储文件中导入数据包,有益的功能还包括识别网络接口以及与之相关的数据包,进行能分别研究它们,另外还能释放数据百种协议包头。

DTrace
可以在内核和应用程序内部检查网络事件,包括套接字连接、套接字IO、TCP事件、数据包传输、积压队列丢包、TCP重传输,以及其他细节。

应用、系统库、系统调用、套接字、TCP、UDP、IP、链路层、设备驱动

套接字连接
由处理网络通信的应用程序函数、系统套接字库、系统调用层、或者在内核中可以跟踪套接字。通常偏好系统调用层,因为它文档齐全,系统开销低(基于内核)并且是系统级的
用connect()计数出站连接:

套接字IO
套接字建立后,按文件描述符在系统调用层能跟踪后续的读写事件,实现方法如下。
套接字文件描述符关联数组:跟踪syscall::socket:return并且建立一个关联数据,例如is_socket[pid,arg1]=1; .对比将来的IO系统调用检查该数组可以确认哪个文件描述符是套接字。记得消除syscall::close:entry的值。

套接字延时
考虑到能在系统调用层面跟踪套接字事件,延时分析可以做如下测量。
连接延时:对于同步的系统调用,这是connect()消耗的时间。对于非阻塞的IO,这是执行connect()到poll()或者select()报告套接字不绪时间。
首字节延时:自执行connect或者从accept返回,直接第一字节数据由任何一个IO系统调用从套接字接收到的时间。
套按字持续时间:同一个文件描述符由socket()到close()的时间。要聚集连接的持续时间,可以由connect或者accept开始计时。

套按字内部活动
用fbt provider能跟踪套接字的内核运行。列出以sock开头的函数
dtrace -1n ‘fbt::sock*:entery’
输出已被截短–它列出的内容超过了100个探针。可以单独跟踪这里的任意一个,以及它们的参数和时间戳,这有助于回答任何有关套接字行为的问题。

TCP事件
类似套接字,TCP的内核运行也能用fbt provider跟踪。
accept-established 接受一个入站连接
connect-request 启动一个出站连接
connect-established 建立一个出站连接
accept-refused 拒绝一个连接请求
connect-refused 拒绝一个连接请求
send 发送一个数据段
receive 接收一个数据段
state-change 一个会话发生状态改变是跟踪一个深层次的事件并且检查它的反向跟踪栈,例如Linux中,用stat跟踪ip_output();

高级网络跟踪脚本
soconnect.d 跟踪客户机套按字connect,显示进程和主机
soaccept.d 跟踪服务器套按字accept,显示进程和主机
soclose.d 跟踪套接字连接持续时间,connect到close
socketio.d 按进程和类型显示套接字IO
socketiosort.d 按进程和类型显示套接字IO,并按进程排序
socketiosort.d 跟踪连接以及套接字层的首字节延时
solstbyte.d 列出最繁忙的套按字状态工具
soerror.d 识别套接字错误
ipstat.d 每秒的IP统计信息
ipio.d 探测IP发送、接收
ipproto.d IP封装原型摘要
ipfbtsnoop.d 跟踪IP数据包:fbt跟踪示范
tcpstat.d 每秒的TCP统计信息
tcpaccept.d 入站TCP连接摘要
tcpacceptx.d 入站TCP连接摘要,解析主机名
tcpconnect.d 出站TCP连接摘要
tcpioshort.d 实时跟踪TCP发送、接收,附带基本细节
tcpio.d 实时跟踪TCP发送、接收,附带标记翻译
tcpbytes.d 按客户机和本地端口汇总TCP负载字节数
tcpsize.d 显示TCP发送、接收IO大小分布
tcpnmap.d 检查可能的TCP端口扫描活动
tcpconnlat.d 按远程主机计量TCP连接延时
tcp 1 stbyte.d 按远程主机计量TCP首字节延时
tcp_rwndclosed.d 识别TCP零接收窗口时间,附带延时
tcpfbtwatch.d 观察入站TCP连接
tcpsnoop.d 跟踪TCP IO,附带进程细节
udpstat.d 每秒的UDP统计细节
udpio.d 实时跟踪UDP发送、接收,附带基本细节
icmpstat.d 每秒的ICMP统计信息
icmpsnop.d 跟踪ICMP,附带细节
superping.d 提高ping往返时间的准确度
xdrshow.d 显示外部数据表示调用以及调用的函数
macops.d 按接口和类型计数介质访问层MAC的操作
ngesnoop.d 实时跟踪以太网nge驱动事件
ngelink.d 跟踪nge连接状态的改变
perf
perf创建内核tcp_sendmsg()函数的动态跟踪点,然后跟踪它五秒并显示调用图:
输出显示sshd的栈跟踪,它导致内核调用tcp_sendmsg()并由TCP连接发送数据。还有一些预定义的网络跟踪点事件,
skb跟踪点用于套接字缓存事件,而net用于网络设备。这些也有助于网络调查。

其他工具
其他Linux网络性能工具如下。
strace(1):跟踪套接字相关的系统调用并检查其他的选项。
lsof(8):按进程ID列出包括套接字细节在内的打开的文件。
ss(8):套按字信息
nfsstat(8):NFS服务器和客户机统计信息
iftop(8):按主机总结网络接口吞吐量
/proc/net:包含许多网络统计信息文件

调优
通常能提供较高性能网络的可调参数已经调整好。网络栈通常也被设计为动态响应不同的工作负载,以提供最佳性能

Linux
可调参数可以用sysctl(8)命令查看和设置,并写入到/etc/sysctl.conf。它们也能在/proc文件系统中读写,位于/proc/sys/net下。
例如,要查看适用于TCP参数,可在sysctl(8)的输出中搜索字符tcp得到

套按字和TCP缓冲
所有协议类型读rmem_max和写vmem_max的最大套按字缓冲大小可以这样设置:
数值的单位是字节。为支持全速率的10GbE连接,这可能需要设置到16MB或更高。
tcp_moderate-rcvbuf = 1
为TCP读和写缓冲设置自动调优参数:
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 87380 16777216
每个参数有三人数值:可使用的最小、默认和最大字节数,长度从默认值 自动调整。要提高吞吐量,尝试增加最大值。增加最小值和默认值 会使每个连接消耗更不必要的内存。

TCP积压队列
首个积压队列,半开连接
tcp_max_syn_backlog = 4096
第二个积压队列,传递连接给accept()的监听积压队列:
net.core.somaxconn = 1024
以上两者或许都需要由默认值调高,例如调到4096和1024或者更高,以便列好地处理突发的负载

设备积压队列
增加每CPU的网络设备积压队列长度
net.core.netdev_max_backlog = 10000
为了10GbE的NIC,这可能需要增加到10000

TCP阻塞控制
Linux支持可插入的阻塞控制算法。列出当前可用:
net.ipv4.tcp_available_congestion_control = cubic reno

一些可能支持加载但未加载。例如添加htcp:

net.ipv4.tcp_available_congestion_control = cubic reno htcp

选择当前的算法:
net.ipvr.tcp_congestion_control = cubic

TCP 选项
其他可设置的TCP参数包括SACK和FACK扩展,它们能以一定CPU为代价在高延时的网络中提高吞吐性能
net.ipv4.tcp_sack = 1
net.ipv4.tcp_fack = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0
安全tcp_tw_reuse可调参数能利用一个TIME-WAIT会话。这使得两个主机间有更高的连接率,例如web服务器和数据库服务器之间,而且不会达到16b的TIME-WAIT会话临时 端口极限。
tcp_tw_recycle是另一个重利用TIME-WAIT会话的方法,尽管没有tcp_tw_reuse安全

网络接口
ifconfig eth0 txqueuelen 10000

资源控制
控制组的网络优先级子系统能对进程或者进程组的出站网络通信应用优先级。相对于低优先级的网络通信,例如备份或者监视,这能够照顾高优先级的网络通信,例如生产负载。配置的优先级数值会翻译为一人IP ToS水平并包含于数据包中。

配置
除了可调参数,如下的配置选项也能用于高估网络性能。
以太网巨型帧:如果网络基础架构支持巨型帧,由默认的MTU1500增加到9000左右能提高网络吞吐性能。
链路聚合:多个网络接口能组合并聚合带宽成为一个接口。这需要交换机支持并且配置以便正常。
套接字选项:应用程序能用setsockopt()高估缓冲区大小。增加它以提高网络吞吐性能。

性价比
许多公有去提供 商通常按小时和内核大小销售服务实例,8GB实例的价格大约1GB实例的8倍。其他的资源,如CPU,根据内存大小按比例放大定价。

它们是完全一致的。额外的层往往显示为栈中额外的帧
对于文件系统访问,可以设置分区挂载回环文件系统,它们本身挂载于宿主的文件系统。这个策略用于稀疏根分区模型:一个在分区间共享只读文件的方法。如果使用了回环文件系统,文件系统IO会产生少量的CPU系统开销。

其他租户
其他租的存在对性能很可能有一些虚拟化技术无关的负面影响:
由于其他租户消耗并清空记录,CPU缓存命中率可能会降低。
CPU执行可能被短时间地中断以供其他租户设备执行中断服务例程。
可能会存在系统资源的竞争,源自正在被其他租户使用的资源(例如磁盘、网络接口)
最后一个因素由资源控制管理。尽管这些影响同样存在于传统的多用户环境,它们在去计算中更普遍。

资源控制
OS虚拟化基础架构管理邻居间的安全,而资源控制管理性能。表列出了资源控制的范围并用Joyent公有云的SmartOS Zone配置作为实例。这些资源控制被分为配制和优先级,它们可按每访客由去运营商或者软件设置。
限制是锁在消耗的最大值。优先级引导资源消耗,按重要值平衡邻居间的使用。两者都可以适当使用———对于一些资源,那意味着两者都需要。

基准测试
在可控的状态下做性能基准测试,对不同的选择做比较,让我们可以在生产环境遇到性能极限之前对性能极限做了解。这些极限可能是系统资源的,虚拟化环境中的软件限制,也可能是目标应用程序的限制。前面的章节已经考察了这些组件,介绍了这些极限的类型和分析它们所用的工具。

之前的章节还介绍了用简单的人工作负载来做极限调查的微基准的工具。其它的基准测试类型还包括客户工作负载模拟,用以试图复制客户的使用模式并做跟踪重放。不论你使用何种类型,重要的是对基准测试做分析,从而才能更加清楚测量对象。基准测试只能告诉你系统能以多快的速度运行基准测试,你需要理解这些结果并决定如何把它们应用到你的环境中。

事情
系统设计:对比不同的系统、系统组件或者应用程序。基准测试提供的数据可以帮助进行采购决策,特别是可以了解可选项间的价格、性能比。在某些情况下,能使用发表了的行业基准,避免了客户自己执行基准测试。
高估:测试可调参数及配置选项,以确定哪 些值得针对生产环境负载做进一步研究。
开发:产品开发过程中,会用到非回归测试和极限调查。
容量规划:为容量规划确定系统和应用程序 的极限。或者是为性能模型提供数据,或者直接找到性能极限。
排错:确认组件是否仍然以最高的性能运行。例如,测试主机间最大的网络吞吐量,检查是不是存在网络问题。
市场营销:出于市场营销用途需要确定产品的最高性能(也被称为基准营销)

好的基准测试的本质
可重复性:有利于比较
可观测性:性能能襁分析与理解
可迁移性:基准测试可以适用于竞争对手以及不同的产品版本。
简单的展示:所有人都能理解它的结论
符合现实:测量值 反映顾客体验到的现实情况
可执行性:开发人能快速地测试修改

当为了购买而比较不同系统时,另一个必须要老师的特性是:价格/性能比。价格可以定量为设备五年的固定资产成本

基准测试分析
测试的是什么?
有哪 些限制因素?
有哪些干扰会影响结果?
从结果可以得出什么结论?

要明白上述内容,需要深刻地理解基准测试软件的动作、系统的响应,以及结果与目标环境是如何关联的。
考虑到基准测试工具及访问其他所运行的系统,满足这些需求的最佳方法是在运行基准测试时分析性能。
除了使用现成的基准测试工具,你可能发现自己开发定制的基准测试软件会更有效,或者至少定制了负载发生器。为着力于你的测试所需,这些可以保持精简,让分析和排错变得更快。
当阅读其他人的基准测试结果时,某些情况下你没有基准测试工具或者系统的访问权限。基于可用的资料,考虑之前的项目,然后提问,系统环境是怎样的?它是如何配置的?你可能会就这些问题向供应商寻求答案。

基准测试之罪
要做好基准测试就不能做过就不管了。基准测试工具会生成数字,但这些数字可能并不能反映你所想的事情,你的结论可能因此是错误的。
好的基准测试要求严格地检查实际测量的是什么,并且要求理解测试了什么,以此得出有交换和结论。
比如,许多工具都声称或者暗示它们能测量磁盘性能但实际上测量的是文件系统性能。这二者之间的差别可能是多个数量级的,因为文件系统能利用缓存和缓冲来使用内存IO代替磁盘IO。即便基准测试工具能正确运行并测试了文件系统,但你对于磁盘的结论可能错得离谱。
对数字是否可信的缺乏直觉的初学者,理解基准测试会特别困难。如果你买的温度计显示你所在的房间温度是1000华氏度,你会立刻知道出错了。对于基准测试你就不一定行了,因为它所提供的数字你可能不熟悉。

基准测试信仰
人们容易轻信流行的基准测试工具是值得依赖的,如果该工具还是开源的并且已经在了很长一段时间,尤其如此。这种“普及等于有效”的误解被称之为“诉诸群从逻辑”(拉丁语“吸引众人”)
分析你所使用的基准澧是非常耗时的,并且 要特定的专业技能才能做这件事。并且,对于一个流行的基准测试,分析哪些肯定有效的看起来挺浪费的。
尽管软件bug时有发生,但问题可能不出在基准测试软件上,而出在对基准测试结果的解读上。

缺乏分析的数字
没有提供分析细节,仅仅提供了基准测试的结果,说明基准测试作者缺管经验并且假定了基准测试结果是可信且不会更改的。而通常这只是调查的开始,并且这个调查通常会发现结果是错误的或令人困惑的。
每个基准测试数字都应该附有说明,介绍遇到的极限和和进行的分析。我这亲戚总结这个风险:
如果你研究一个基准测试结果的时间少于一周,它很可能是错误的。

本书的大部分内容着眼于在基准测试过程中执行的性能分析。在你没有时间做细致分析的情况下,最好列出那些滑时间去检查的假设并且把它们放在结论里,例如:
假设基准工具没有软件故障。
假设磁盘IO测试实际测量的是磁盘IO。
假设基准测试工具按磁盘IO推到极限。
假设这种类型的磁盘IO与这人应用程序是相关的。
如果事后该基准测试结果被认为重要并值得投入更多精力,以上就可以作为一份待办的事项清单

复杂的基准测试工具
基准测试工具不会由于自身的复杂性而影响基准测试分析,这点非常重要。理想情况下,该程序是开源的进而可以研究,并且足够短小能快速地阅读和理解。
对于微基准测试,建议挑选那些用C语言编写的工具。对于客户模拟基准测试,建议使用的工具与客户的编程语言相同,以减少差别。
一个常见的问题是基准测试的基准测试–报告的结果会受限于基准测试软件本身。复杂的基准测试套件,由于代码量巨大而难以理解和分析,很难做到这一点。

测试错误的目标
尽管有大量可用的测试多种工作负载的基准测试工具,但其中的大部分与目标应用程序可能并没有关系

例如,一个常见的错误是测试磁盘性能—用的是磁盘基准测试工具——然而目标环境工作负载是完全运行于文件系统缓存并且与磁盘IO无关。
类似的,一个开发产品的软件工程师团队可能标准化了一个特定的基准测试,并全力按基准测试软件测量的结果提高性能。然而如果它没有模拟客户的工作负载,努力只会用来优化错误的软件行为。
某个基准测试曾经一度能对工作负载做正确的测试,但是因为多年没有更新过,现在它测试的项目都是错误的。

忽略错误
基准测试工具能产生结果并不意味着该结果反映的测试是成功的。某此,乃至全部的请求都可能导向一个错误的结果。尽管这个问题在前述的诸罪中介绍过,但它是如此常见,值得单独提出。
在最近的一次Web服务器性能基准测试中,我再次碰到了这一个问题。运行测试报告Web服务器的平均延时对于需求来说太高了:平均超过一秒。一些快速分析确认了出错的位置:所有的请求被防火墙阻挡,Web服务器没有参与测试。请注意,是所有请求。显示的延时都是基准测试客户机超进后出错的时间。

忽略差异
基准测试工具,特别是微基准测试,通常所实施的一套稳定且连续的工作负载,基于的是一系列真实环境特征的测量值的平均值,诸如每天不同的时间或时间间隔。例如,一个磁盘工作负载可能是平均速率的500读/s和50写/s。基准测试工具能模拟这个速率,或者按10:1的比例模拟读、写,也能测试更高的速率。
这种方式忽略了差异:操作的速率可能是可变的。操作的类型也可能变化,并且一些类型可能同时发生。例如,每10s可能发生突发的写,而同步的读取是稳定的。突发的写入在生产环境中可能会导致问题,例如使读取队列等待。但是如果基准测试仅施加稳定的平均速率,这些就不地被模拟出来。

忽略干扰
考虑哪 些外部干扰可能会影响结果。系统定时的活动,例如系统备份,会不会在基准测试期间运行?对于云计算环境来说,干扰同系统中的其它不可见的租户。
通常的解决干扰的策略是主基准测试运行更长时间——数分钟而不是数秒。一般来说,基准测试的持续时间不要少于一秒。短期的测试可能会意外地受到设备中断(处理中断服务程序时线程会挂起)、内核CPU调度决策(在迁移排除的线程之前做等待以保持CPU的亲和性),以及CPU缓存的热度影响。多次运行基准测试并且检查标准差。标准差应该尽可能小,以确保可重复性。
注意收集数据,以便在存在扰动时可以对其做研究。这是可能需要收集操作延时的分布——不仅仅是基准测试总的运行时间——进而观察到异常值并且记录其细节。

改变多个因素
当比较两个测试的基准结果时,要注意理解二者之间所有不同的因素。
例如,若两台主机都是通过网络做基准测试,那么它们之间的网络是否相同?如果一台主机经过更多跳的低速网络,或者一个更拥堵的网络会怎么样?任何一个这样的额外因素都会导致基准测试结果错误。
在云环境中,基准测试执行需要通过创建实例、测试这些实例,然后推毁这些实例来完成。这引入了一些不可见因素的可能性:创建实例的系统可能快一些或慢一些,或者负载更高而且还面临其它租户的竞争。建议测试多个实例并且记录平均值,这样可以避免在一个特别快或者特别慢的系统中做测试所导致的异常值 。

基准测试竞争对手
你的市场营销部门可能希望基准测试结果求你 的产品可以击败竞争对手。这通常是一个糟糕的想法。下面我会解释这原因。
如果你必须要对竞争对手的产品做基准测试,你要花大量的时间来调优它们的产品。利用前面章节介绍的技巧分析性能,还要地最佳实践、客户论坛和bug数据库做调查研究。可能你甚至需要聘请外部专家来高估系统。然后,对你自己公司的产品也要花费同样的精力,最终做到的基准测试才能针锋相对。

误伤
当基准你自己的产品时,要尽力确保性能最佳的系统和配置已被测试,所测试系统已经被推至真正的极限。

具有误导性的基准测试
具有误导性的基准测试结果在行业内是常见的。或者是无意疏忽了基准测试实际测量的信息,或者是故意忽视。这些基准测试结果常常在技术上是正确的,但却以错误的方式展示给了客户。

基准测试特别版
有一种卑鄙的做法——一部分人认为这是罪恶的并且应该禁止的——就是开发基准测试特别版。
欺骗
最后一个基准测试之罪是欺骗:发布虚假的结果。幸运的是,这极其少见或者并不存在;我还没见过数据是完全编造出来的情况,即使在最血腥的基准测试混战之中。

基准测试的类型

微基准测试
利用人造的工作负载对某类特定的操作做测试,例如,执行一种类型文件系统IO、数据库查询、CPU指令,或者系统调用。它的优势是简单:对组件的数量和怕牵涉的代码路径做限制更地研究目标从而快速地确定性能差异的根源。因为来自其它组件的变化已经尽可能地被剥离了,所以通常测试是可重复的。在不同系统上执行微基准测试一般都很快,这是因为微基准测试是特意人为的,不会轻易地与真实工作负载模拟相混淆。
能用的微基准测试结果,要能够映射目标工作负载。微基准测试可能测试多个维度,但是只有一两人可能是相关的。性能分析或者对目标系统建模有助于决定什么微基准测试结果是合适的以及合适到什么程度。
CPU:UnixBench、SysBench
内存IO:lmbench
文件系统:Bonnie、Bonnie++、Sysbench、fio
磁盘:hdparm
网络:iper

如果需要,可以加入更多的测试。所有这些测试要考虑如下所示的两个额外因素。
工作集大小:访问的数据大小(例如,总文件大小)
- 大小远小于主存:数据完全缓存于文件系统缓存,可以调查文件系统软件的性能。
- 大小远大于主存:将文件系统缓存的影响降到最低,促使基准测试向磁盘IO测试靠拢
线程数:假设一个小的工作集大小
- 单个线程——测试的基于当前CPU时钟速度的文件系统性能。
- 多线程——使所有CPU饱和——测试的是系统的最大性能,即文件系统和所有CPU。
考虑这些因素会得到一个巨大的测试矩阵。有一些统计分析的技巧能精简需要的测试集。关注于最高速度的基准测试被称为晴天性能测试。为了避免忽视什么问题,你可能还会考虑用到阴天性能测试,这是针对非最理解情况的测试,如竞争、干扰,以及工作负载变化。

模拟
许多基准测试会模拟客户应用程序的工作负载,基于生产环境的工作负载来决定所要模拟的特征。例如,一个生产环境的NFS工作负载由如下的操作类型和可能性能组成:读40%、写7%、getatt19%、readdir1%,以及其它。除此之外的特征也能被测量并模拟。
模拟所生成的结果与客户在真实世界所执行的工作负载是相似的。即便不一样,至少也足够像了。可能黑线的因素太多,用微基准测试调查地非常费时。相较于微基准测试,模拟能覆盖复杂系统相互作用的影像,这点是微基准测试可能缺失的。
第6章介绍过的CPU的whetstone和Dhrystone基准测试就是模拟的两个例子。Whetston开发于1972年,用以模拟当时科学计算的工作负载。
工作负载的模拟可以是无状态的,每个服务器请求与前一个请求是无关的。例如,前面介绍的NFS服务器工作负载可以基于测量的可能性用一系统类型随机选择的操作来进行模拟。
模拟也可以是有状态的,每个请求都依赖于客户的状态,至少依赖于前一个请求。可能会发现NFS读和写往往是成组到达的,以至于写操作前的操作也是写的可能性相比写之前的操作是读的可能性要高很多。这样的工作负载最好用马尔可夫模型模拟,马尔可夫模型是用状态来表示请求的还能测量状态转换的可能性。
模拟的问题是忽略了变化,客户的使用模式会因时间的推移而变化,因而要求模拟也随之更新和调整以保证两者的关联性。这件事可能存在阻力,然而,基于旧版本基准测试的结果,是不能拿来与新版本的基准测试做对比的。

回放
第三种基准测试类型 试图回话目标的跟踪日志,用真实捕促到的客户机的操作来测试性能。这看起来很理想——与在生产环境做测试一样,是这样吗?是的,不过它琮是有问题的:当服务器的特征和所响应的延时发生变化时,所捕捉到的客户工作负载很可能不能反映出这些差异,与模拟客户工作负载相比,可能并不会更好。如果太信任这种方法,情况会更糟。

第三种基准测试类型是试图回话目标的跟踪日志,用真实捕促到的客户机的操作来测试性能。这看起来很理想——与生产环境做测试一样,是这样吗?是的,不过它还是有问题的:当服务器的特征和所响应的延时发生变化时,所捕促到的客户工作负载很可能不能反映出这些差异,与模拟客户工作负载相比,可能并不会更好。如果太信任这种方法,情况会更糟。考虑这么一人假想的情况:某客户正在考虑升级存储基础架构。当前生产环境的工作负载被跟踪并在新的硬件上回放。不幸的是性能变差了,丢了订单。问题在于:跟踪、回话操作在磁盘IO层。

行业标准
行业标准的基准测试是由独立组织制定的,旨在创建公平和相关的基准测试。通常是成套的微基准测试和工作负载模拟,这些成套的微基准测试和工作负载模拟定义清晰并且记录成了文档,在确定的指导原则下执行,才能得到期望的结果。供应商可以参与。制定标准的组织会提供应商软件以运行基准测试。测试的结果一般需要完全公开配置的环境,以供审核。
除了TPS这一测量值,可做同样用途的其他测量值如下。
MIPS:每秒百万指令数。不过这是一人性能的测试值,执行的操作依赖于指令的类型,难于在不同的处理器架构间比较。
FLOPS:每秒浮点操作数——作用与MIPS类似,但是对应的是重度浮点运算的工作负载。
行业基准测试通常测量的是基准测试的自定义指标,它仅适用于与自身的比较。

TPC建立并管理多种关于数据库性能的行业基准测试,如下所示。
TCP-C:模拟了大量用户对一个数据库执行事务处理的完整计算环境。
TPC-DS:模拟了一人决策支持系统,包括查询和数据维护。
TPC-E:在线事务处理OLTP工作负载,建模了一个经纪公司的数据库。客户的事务涉及交易、帐户查询和市场研究。
TPC-H:一个决策支持基准测试,模拟了特别的查询和并发的数据修改
TPC-VMS:TPC虚拟测量单系统也收集其它的虚拟化数据库的基准测量。

方法
不论是微基准测试、模拟还是回放,本节都讨论了基准测试执行的方法和实践。
被动基准测试、主动基准测试、CPU剖析、USE方法、工作负载属性、自定义基准测试、逐渐增加负载、合理性检查、统计分析

被动基准测试
这是一种做过就不管了的基准测试策略——基准测试运行后忽略它直到结束。它的主要目标是收集基准测试数据。基准测试通常就是这样运行的。为了与主动基准测试做比较,被动基准测试也作为独立的方法描述。
1、选择一个基准测试工具。2、用多种选项的组合来运行它。3、装饰这些结果做成一套幻灯片。4、将这些幻灯片交给管理层。

由于基准测试软件的bug而无效。
受限于基准测试软件(例如,单线程)
受限于一个与基准测试目标无关的组件(例如,一个拥堵的网络)
受限于配置(没有开户性能的属性、不是最优化的配置)
受干扰的影响(测试不可重复)
测试了完全错误的目标。

被动基准测试容易执行但也容易出错。供应商执行被动基准测试,可能会引起假警报而浪费工程资源或者损失销售机会。客户执行被动基准测试,可能会做出错误的产品选择并在这之后长期困扰企业。

主动基准测试
主动基准测试,在基准测试运行的同时用其它工具分析性能——不只是在测试完成后。你确定基准测试所要测试的对象,并且你理解它是什么。主动基准测试能确定被测系统的真实极限,或者基准测试本身的极限。记录所遇到极限的具体细节,当分析基准测试结果时这是非常有帮助的。
作为奖赏,主动其次测试是一个让你锻炼性能观测工具使用技能的好机会。理论上,你在检查的是一个已知的负载并且能看到负载是如何在这些工具中呈现的。
最好的情况是,该基准测试能够配置并可以运行在稳定的状态,这样在数小时或数天后就可以做分析了
作为示例,我们使用微基准测试工具Bonnie++来做第一个测试。它的主页是这样描述的
磁盘——开始是空闲的,在基准测试过程中显示出的写入吞吐量比Bonnie++报告的K/sec结果要低得多
主动性能分析,会从其它多种角度审视测试是如何运行的,能对结果有更好的理解。结论是最终的限制是单线程CPU速度,而且85%的CPU时间都用在了用户模式。
一般而言,Bonnie++作为基准测试工具并不糟糕;很多情况下它很有用。我选择这个作为示例是因为这个例子很出名,我曾对此做过研究,发现类似的情况并不罕见。这仅仅是其中一例。
需要注意的是,新实验版本的Bonnie++已经把按字符测试改为1字节的文件系统IO。针对这个测试对比不同版本的Bonnie++,会有显著的不同。

CPU剖析
值得把对基准测试目标和基准测试软件做CPU剖析单独列为一个方法,因为用这种方法能快速地得到一些发现。这常常作为主动基准测试调查的一部分执行。
是快速地检查 所有的软件正在做什么,看看有什么有趣的事情出现。这能将研究的范围收缩到最关键软件组件:那些正在运行基准测试的组件。
用户级的栈和内核级的栈都能做性能分析。

示例
在一个建议的新系统 执行了一次微基准测试,结果令人失望:磁盘吞吐率比旧系统更差。我被要求查清问题的原因,我预计可能是磁盘或者磁盘控制器较差并需要更换。
我采用USE方法作为开始,发现尽管基准测试还在执行,但磁盘不是很忙。CPU有一些处于系统时间内核的使用
对于磁盘基准测试而言,你可能不会认为CPU是一个有趣的分析目标。考虑到内核有一定的CPU使用,尽管没有什么期待,我还认为值得快速检查一下看看有什么情况。

USE方法
在基准测试过程中应用USE方法能确保找到一个限制。该限制可能是某个组件达到了100%的使用率,或者你还未把系统推到极限。

工作负载特征归纳
这种方法通过对生产工作负载做特征归纳,可以确定某一给定基准测试与当前生产环境的相关性。

逐渐增加负载
这是一个用于确定系统所能处理的最大吞吐量的简单方法。以较小的递增添加负载并测量产出吞吐量直至到达极限。结果能绘制成图以展示扩展性的特性。该扩展性的特征可以用作直观研究或者用于扩展性模型
在客户机中运行的实例逐一递增,逐步增加负载直到达到一个极限。这可以连同资源使用率(USE方法)一起绘制成图以研究扩展性的特性,确认资源确实已被耗尽。

完整性检查
有一个检查基准测试结果的方法就是调查所有的特征没有问题。 这包括检查结果是否需要某些组件超过其已知的极限,例如,网络带宽 、控制器带宽、互联带宽,或磁盘IOPS。如果有极限被超过,就值得详细调查。多数情况下,用这个方法会最终发现基准测试的结果是假的。

统计分析
选择基准测试工具、基准测试工具的配置,以及需要捕捉系统性指标。
执行基准测试,收集大量结果的数据集和指标。
用统计分析解读数据,生成报告。
与着眼于在运行时分析系统的主动基准测试不同,统计分析有着重于分析结果。它也不同于被动基准测试,被动基准测试不做任何分析。
这种方法适用于系统访问时间受限且昂贵的大规模系统环境。例如,仅有一个“最大配置”的系统可用,而同时有多部门都希望运行测试
销售:概念验证期间,运行一个模拟客户负载以显示最大配置的系统能有怎样的交付。
市场营销:获得最好数值用于市场营销活动。
支持:调查只有最大配置系统在严重负载下才出现的异常状态。
软件工程:对新功能和代码修改测试性能。
质量控制:执行非回归测试和认证

每个部门在系统里运行它们的基准测试的时间非常有限,更多的时间花在运行后结果 的分析上。
由于指标收集的代价较高,要花费额外的努力确保指标是可靠的和可信的, 以避免发现问题将来重做。除上从技术上检验它们是如何产生的,你还要收集更多统计信息,这样才能更快地发现问题。这些可能包括统计信息的变化、完整分布、误差范围以及其它的信息。针对代码修改做基准测试或者执行非回归测试昌,理解这引起误差范围至关重要,这样结果才有意义。
还有,要尽可能从运行的系统里收集性能数据,这样之后才能对这些数据做取证分析。数据收集到的工具可能有sar、第三方产品,还有将所有统计数据转会的可定制工具。

基准测试
基准测试结果可能基于一套极端的硬件配置、对特别情况的调优(条带磁盘)、一次幸运的眷顾(不可重复),或者一个测量错误。其中任意一点都是能被确认的,只要你能在自己的数据中心运行并做你自己的分析:主动基准测试。不过,会消耗你大量的时间。
下面是一些其它可能被问到的问题
总体
—测试的系统是怎样的配置?
—测试的是单个系统,还是一个系统集群?
—被测系统的成本是多少?
—基准测试客户端的配置是怎样的?
—测试的时长是多少?
—结果是平均值还是峰值?平均值是什么?
—其它的分布数据细节(标准差、百分位数,或者所有的分布细节)?
—基准测试的限制因素是什么?
—操作的成功、失败比是多少?
—操作的属性是什么?
—为模拟工作负载会选择听任的属性吗?它们是如何选择的?
—基准测试会模拟变化吗,或者模拟的是一个平均工作负载?
—基准测试结果是否用其它分析工具确认过?
—误差范围能否与基准测试结果一同展示?
—基准测试结果是事具有可重现性?
CPU、内存相关的基准测试:
—使用的什么处理器?
—有任何CPU被停用吗?
—安装了多少主存?是什么类型?
—有用到任何自定义的BIOS设置吗?
存储相关的基准测试:
—存储设备的配置是怎么样的(使用了多少,类型,RAID配置)?
—文件系统的配置是怎样的(使用了多少,还有调优)?
—工作集大小是多少?
—工作集被缓存的程序是多少?缓存于何处?
—访问了多少文件 ?
网络相关的基准测试
—网络配置是怎样的(使用了多少网络接口,它们的类型和配置)?
—TCP设置是怎么高估的?

内核空间
系统调用接口、内核子系统、设备驱动程序
运行于内核空间,牌进程上下文,代表某个特定的进程执行。
运行于内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断。
运行于用户空间,执行用户进程
Linux支持动态加载内核模块。尽管Linux内核也是单内核,可是允许在时候动态地卸除和加载部分内核代码
Linux支持对称多处理SMP机制,尽管许多Unix的变体也支持SMP,但传统Unix并不支持这种机制
Linux内核可以抢占。与传统的Unix不同,Linux内核具有允许在内核运行的任务优先执行能力。
Linux对线程支持的实现比较有意思:内核并不区分线程和其他的一般进程。对于内核来说,所有的进程都一样——只不过其中的一些共享资源而已。
Linux提供具有设备类的面向对象的设备模型,热插拔事件,以及用户空间的设备文件系统
Linux忽略了一些被认为是设计得拙劣的Unix特性,像STREAMS,它还忽略了那些实际上已经根本不会使用的过时标准。
Linux体现了自由这个词的精髓。现有的Linux特性集就是Linux公开开发模型自由发展的结果。如果一个特性没有任何价值或者创意很差,没有任何人被迫去实现它。相反的,在Linux的发展过程中已经形成了一种值得称赞的务实态度:任何改变都要针对现实中确实存在的问题,经过完善的设计并正确简洁的实现。于是,许多其他现代Unix系统包含的特性,如内核换页机制,都被毫不迟疑的引人进来。

内核源码树
内核源码树由很多目标组成,而在多数目录又包含了更多的子目录。源码树的根目标以及其子目标如下。
arch 特定体系结构的源码
crypto Crypto API
Documentation 内核源码文档
drivers 设备驱动程序
fs VFS和各种文件系统
include 内核头文件
init 内核引导和初始化
ipc 进程间通信代码
kernel 像高度程序这样的核心子系统
lib 通用内核函数
mm 内存管理子系统和VM
net 网络子系统
srcipts 编译内核所有的脚本
security Linux安全模块
sound 语音子系统
usr 早期用户空间代码

进程描述符中包含的数据能完整的描述一个正在执行的程序:它打开的文件、进程的地址空间、挂起的信号、进程的状态,还有其他更多信息

分配进程描述符:
Linux通过slab分配器分配task_struct结构,这样能达到对象利用和缓存着色(cache coloring)的目的。在2.6以前的内核中,各个进程的task_struct存放在它们内核栈尾端。这样做是为了让那些像x86这样寄存器较少的硬件体系结构只要通过栈指针就能计算出它的位置,从而避免使用额外的寄存器专门记录。由于现在用slab分配器动态生成task_struct,所以只需要在栈底或栈顶创建一个新的结构struct thread_info。这个新的结构能使在汇编代码中计算其偏移变得相当容易。

thread_info有一个指向进程描述符的指针

struct thread_info{
sturce task_struct *任务;
struct exec_domain *exec_domina;
unsigned long flags;
unsigned long status;
__u32 cpu;
__s32 preempt_count;
mm_segment_t addr_limit;
struct restart_block restart_block;
__u8 supervisor_stack[0]
}

每个任务的thread_info结构在它的内核栈的尾端分配。结构中task域中存放的是指向该任务实际task_struct的指针。

进程状态
TASK_RUNNING运行–进行是可执行的,它或者正在执行,或者在运行队列中等待执行。这是进程在用户空间中执行惟一可能的状态;也可以应用到内核空间中正执行的进程。
TASK_INTERRUPTIBLE可中断–进行正在睡眠也就是说它被阻塞,等待某些条件的达成。一旦这些条件达成,内核就会把进程状态设置为运行。处于此状态的进程也会因为接收到信号而提前被唤醒并投入运行。
TASK_UNINTERRUPTIBLE不可中断–除了不会因为接收到信号而被唤醒从而投入运行外,这个状态与可打断状态相同。这个状态通常在进程必须在等待时不受干扰或等待事件很快就会发生时出现。由于处于此状态的任务对信号不作响应,所以较之可中断状态,使用较少。
TASK_ZOMBIE僵死–该进程已经结了,但是其父进程还没有调用wait()系统调用。为了父进程能够获知它的消息,子进程的进程描述符仍被保留着。一旦父进程调用了wait(),进程描述符就会被释放。
TASK_STOPPED停止–进程停止执行;进程没有投入运行也不能投入运行。通常这种状态发生接收到SIGSTOP、SIGTSTP、SIGTTIN、SITTOU等信号的时候。此外,在调试期间接收到任何信号,都会使进程进入这种状态。

进程上下文
可执行程序代码是进程的重要组成部分。这些代码从可执行文件 载入到进程的地址空间执行。一般程序在用户空间执行。当一个程序调执行了系统调用或者触发了某个异常,它就陷入了内核空间。此时,我们称内核“代表进程执行”并牌进程上下文中。在此上下文中current宏是有效的。除非在此间隙有更高优行级的进程需要执行并由高度器做出了相应调整,否则在内核退出的时候,程序恢复在用户空间继续执行。
系统调用和异常处理程序是对内核明确定义接口。进程只有通过这些接口才能陷入内核执行——对内核的所有访问都必须通过这些接口。

进程家族树
所有的进程都是PID为1的init进程的后代。内核在系统启动的最后阶段启动init进程。该进程读取系统的初始化脚本initscript并执行其他的相关程序,最终完成系统启动的整个过程。
系统中的每个进程必有一个父进程。相应的,每个进程也可以拥有零个或多个子进程。拥有同一个父进程tast_struct,叫做parent的指针,还包括一个称为children的子进程链表。所以对当前进程,可以通过下面的代码获得其父进程的进程描述符;

fork()
Linux通过e()系统调用实现fork,这个调用通过一系统的参数标志来指明父、子进程需要共享的资源。fork()、vfork()和_cone()库函数都根据各自需要的参数标志去调用clone。然后由clone去调用do_fork()
do_fork完成了创建中的大部分工作,它的定义在kernel、fork.c文件中。该函数调用copy_process()函数,然后主进程开始运行。copy_process()函数完成的工作很有意思:
调用dup_task_struct()为新进程创建一个内核栈,thread_info结构和task_struct,这些值与当前进程的值相同。此时,子进程和你进程的描述符是完全相同的。
检查新创建的这个子进程后,当前用户所拥有的进程数目没有超出给他分配的资源的限制。
现在,子进程着手使用自己与父进程区别开来。进程描述符内的许多成员都要被清0或设为初始值。进程描述符的成员值并不是继承而来的,而主要是统计信息。进程描述符中的大多数都是共享的。
接下来,子进程的状态被设置为TASK_UNINTERRUPTIBLE从保证它不会投入运行。
copy_process()调用copy_flags()以更新task_struct的flags成员。表明进程是否拥有超级用户权限的PF_SUPERPRIV标志被清0.表明进程还没有调用exec()函数的PF_FORKNOEXEC标志被设置。
调用get_pid()为新进程获取一个有效的PID
根据传递给clone的参数标志,copy_process拷贝或共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间等。在一般情况下,这些资源会被 给定进程的所有线程共享;否则,这些资源对每个进程是不同的,因此被拷贝到这里。
让父进程和子进程平分剩余的时间片。
最后,copy_process()作扫尾工作并返回一个指向子进程的指针。
再回到do_fork函数,如果copy_process函数成功返回,新创建的子进程被唤醒并让其投入运行。内核有意选择子进程首先执行。因为一般子进程都会马上调用exec函数,这样可以避免写时拷贝的额外开销。

线程在Linux是的实现
线程机制是现代编程技术中常用的一种抽象。该机制提供了在同一程序内共享内存地址空间运行的一组线程。这些线程还可以共享打开的文件和其他资源。线程机制支持并发程序设计技术,在多处下器系统上,它也能保证真正的并行处理
每个线程都拥有惟一隶属于自己的task_struct,所以在内核中,它看起来就像是一个普通的进程(只是该进程和其他一些进程共享某些资源,如地址空间)。
clone(CLONE_VM | CLONE_FS|CLONE_FILES|CLONE_SIGHAND,0)
上面的代码产生的结果和调用fork差不多,只是父子俩共享地址空间、文件系统资源、文件描述符和信号处理程序。换个说法就是,新建的进程和它的父进程和它的父进程就是流行的所谓线程。
传递给clone的参数标志决定了新创建进程的行为方式和父子进程之间共享资源种类。表列举了这些clone用到的参数标志以及它们的作用。

CLONE_FILES         父子进程共享打开的文件
CLONE_FS            父子进程共享文件系统信息
CLONE_IDLETASK  将PID设置为0(只提供idle进程使用)
CLONE_PARENT        为子进程创建新的命名空间
CLONE_PTRACE        指定子进程与你进程拥有同一个父进程
CLONE_SETTID        继续调试了进程
CLONE_SETTLS        将TID回写至用户空间
CLONE_SIGHAND   为子进程创建新的TLS
CLONE_SYSVSEM   父子进程共享信号处理函数
CLONE_THREAD        父子进程放入相同的线程组
CLONE_VFORK     调用vfork,所以父进程准备睡眠等待子进程将其唤醒  
CLONE_UNTRACED  防止跟踪进程在子进程上强制执行CLONE_PTRACE
CLONE_STOP      以TASK_stopped状态开始进程
CLONE_SETTLS        为子进程创建新的TLS(thread-local storage)
CLONE_CHILD_CLEARTID        清除子进程的TID   
CLONE_CHILD_SETTID          设置子进程的TID
CLONE_PARENT_SETTID     设置父进程的TID
CLONE_VM                        父子进程共享地址空间

内核线程
内核经常需要在后台执行一些操作。这种任务可以通过内核线程(kernel thread)完成——独立运行在内核空间的标准进程。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间。它们只在内核空间运行,从来不切换到用户空间去。内核进程和普通进程一样,可以被高度,也可以被抢占。
Linux确实会把一些任务交给内核线程去做,像pdflush和ksoftirqd这些任务就是很明显的例子。这些线程在系统启动时由另外一些内核线程启动。实际上,内核线程也只能由其他内核线程创建。在现有内核线程中创建一个新的内核线程的方法如下
int kernel_thread(int (fn)(void ), void *arg, unsigned logn flags)
新的任务也是通过clone系统调用传递特定的flags参数而创建的。在上面的函数返回时,父线程退出,并返回一个指向子线程task_struct的指针。子线程开始运行fn指向的函数,arg是运动时需要的参数。一个特殊的clone标志CLONE_KERNEL定义了内核线程常用到的参数标志:CLOEN_FS/CLONE_FILLES/CLONE_SIGHAND。大部分的内核线程把这个标志传递给它们的flags参数。
一般情况下,内核线程会将它在创建时得到的函数永远执行下去。该函数通常由一个循环构成,在需要的时候,这个内核线程就会被唤醒和执行,完成了当前任务,它会自行休眠。

进程终结
不管进程是怎么终结的,该任务大部分要靠do_exit来完成,它要做下面这些繁琐的工作:
首先,将task_struct中的标志成员设置为PF_EXITING
其次,调用del_time_sync删除任一内核定时器。根据返回的结果,它确保没有定时器在排除。也没有定时器处理程序在运行。
如果BSD的进程计账功能是开户的,do_exit调用acct_process来输出计账信息。
然后调用_exit_mm函数放弃进程占用的mm_struct,如果没有别的进程使用它们,就彻底释放它们。
调用_exit_files()、_exit_fs()、exit_namespace和exit_sighand,以分别递减文件描述符、文件系统数据,进程名字空间和信号处理函数的引用计数。如果其中某些引用 计数的数值降为零,那么就代表没有进程在使用相应的资源,此时可以释放。
接着把存放在task_struct的exit_code成员中的任务退出代码置为exit()提供的代码中,或者去完成任何其他由内核机制规定的退出动作。退出代码存放在这里供父进程随时检索。
调用exit_notify向父进程发送信号,将子进程的父进程重新设置为线程组中的其他线程或init进程,并把进程状态设置成TASK_ZOMBIE。
最后,do_exitr的实现在kernel/exit.c文件中可以找到。
到此,与进程相关闻的所有资源都被释放掉。进程不可运行并处于TASK_ZOMBIE状态。它占用的所有资源就是内核栈、thread_info结构和task_struct结构。此时进程存在的惟一目的就是向它的父进程提供信息。父进程检索到信息后,或者通知内核那是无关的信息后,由进程所持有的剩余内存被释放,归还它们系统使用。

删除进程描述符
在调用了do_exit之后,尽管线程已经僵死不能再运行了,但系统还保留了它的进程描述符。前面说过,这样做可以让系统有办法在子进程终结后仍能获得它的信息。因此,进程终结时所需的清理工作和进程描述符的删除被分开执行。在父进程获得已终结的子进程的信息后,或者通知内核它并不关注那些信息后,子进程的task_struct结构才被释放。
wait这一族函数都是通过惟一的一个系统调用wait4来实现的,它的标准动作是挂起调用它的进程,直到其中的一个进程退出,此时函数会返回该子进程的PID。此外调用该函数时提供的指针会包含子函数退出时的退出代码。

list_for_each(list, &father->children){
        p = list_entry(list, struct task_stuct,sibling);
        reparent_thread(p,reaper,child_reaper);
}

    list_for_each(list, &father->ptrace_children){
        p = list_entry(list, struct task_stuct,sibling);
        reparent_thread(p,reaper,child_reaper);
}

此代码遍历了两个链表:子进程链表和ptrace子进程链表,给每个子进程设置新的父进程。

进程小结
在本章中,我们考察了操作系统中的概念进程,还讨论了进程的一般特性,它为何如此重要,以主进程与线程这间的关系。然后讨论了Linux如何存放和表示进程(task_struct和thread_info),如何创建进程(clone,fork),如何把新的执行映像装入到地址空间(exec系统调用族),如何表示进程的层次关系,父进程又是如何收集其后代的信息(通过wait系统调用族),以及进程最终如何死亡(强制或自愿地调用exit)
进程是最基本、最重要的一种抽象,位于每个现代操作系统的核心位置,也是最终导致我们拥有操作系统的根源
下一章讨论进程调度,那是微妙而有趣的方式,内核以这种方式决定哪个进程运行,何时运行,以何种顺序运行。

进程调度
进程调度,这使进程有效工作的一组代码
调度程序是内核的组成部分,它负责选择下一个要运行的进程。进程调度程序可卸任在可运行态进程之间分配有限的处理器时间资源的内核子系统。调度程序是诸如Linux这样的多任务操作系统的基础。只有通过调度程序的合理调度,系统资源才能最大限度地必挥作用,多进程才会有并发执行的效果。

最大限度地利用处理器时间的原则,只要有可以执行的进程,那么就总会有进程正在执行。但是只要系统中进程的数目比处理器的个数多,就注定某一个给定时刻会有一些进程不能执行。这些进程在等待运行。在一组处于可运行进程中选择一个来执行,是调度程序所需完成的基本工作。
多任务操作系统就是能同时并发地交互执行多个进程的操作系统。在多处理机上,这会使多人进程在不同的处理机上真正同时、并行地运行。在任一台机子上,这也能使多个进程在后台运行。相反,这些进程利用内核阻塞自己,直到某一事件(键盘输入、网络数据、过一段时间)发生。因此,现在Linux系统也许有100个进程在内核,但只有一个处于可运行状态。
多任务系统可划分为两类:非抢占式多任务和抢占式多任务。像所有Unix的变体和许多其他现代操作系统一样,Linux提供了抢占的多任务模式。在此模式下,由调度程序来决定什么时候停止一个进程的运行以便其他进程能够得到执行机会。这个强制的挂起动作就叫做抢占。进程在被抢占有之前能够运行的时间是预先设置好的,而且有一个专门的名字,叫进程的时间片timeslice。时间片实际上就是分配给每个可运行进程的处理器时间段。有效管理时间片能使调度程序从系统全局的角度做出调度决定,这样做还可以避免个别进程独占系统资源。我们将会看到,Linux进程调度程序采用动态方法计算时间片,这样带来了许多好处。

策略
决定调度程序在何时让什么进程运行。高度器的生命力往往决定系统的整体印象,并且,还要负责优化使用处理器时间。无论从哪个方面来看,它都是至关重要的。

IO消耗型和处理器消耗型的进程
进程可以被分为IO消耗型和处理器消耗型。前者指进程原大部分时间用来提交IO请求或是等待IO请求。因此,这样的进程经常处于可运行状态,但通常都是运行短短的一会儿,因为它在等待更多的IO请求时取后总会阻塞。
相反,处理器耗费型进程把时间大多用在执行代码上,除非被抢占,否则它们通常都一直不停地运行,因为没有太多的IO需求。但是,因为它们不属于IO驱动类型,所以从系统响应速度考虑,调度器不应该经常让它们运行。对于这类处理器消耗型 的进程,高度策略是尽量降低它们的运行频率,对它而言,延长其运行时间会更合适些。处理器消耗型进程的极端例子就是无限循环地执行。
调度策略通常要在两个矛盾的目标中间寻找平衡:进程响应迅速和最大系统利用率。为了满足上述需求,调度程序通常采用一套非常复杂的算法来决定最值得运行的进程投入运行,但它往往并不保证低优先级进程会被公平对待。因为交互式程序都是IO消耗型的,所以调度程序向这种类型的进程倾斜会缩短系统响应时间。Linux为了保证交互式应用,所以对进程的响应作了优化,更倾向于优先调度IO消耗型进程。虽然如此,但在下面你会持到,调度程序也未忽略处理器消耗型地过程。

进程优先级
优先级高的进程先运行,低的后运行,相同优先级的进程按轮转方式进行调度(一个接一个,重复进行)。在包括Linux在内的某些系统中,优先级高的进程使用的时间片也较长。调度程序总是选择时间片未用尽而且优先级最高的进程运行。用户和系统都可以通过设置进程的优先级来影响系统的调度。
Linux根据以上思想实现了一种基于动态优先级的调度方法。一开始,该方法先设置基本的优先级,然而它允许调度程序根据需要来加,减优先级。举个例子,如果一个进程在IO等待上耗费的时间多于其运行时间,那么该进程明显属于IO消耗型进程。它的优先级会被动态提高。作为一个反倒,如果一个进程的全部时间片一上就被耗尽了,那么该进程属于处理器消耗型——它的优先级会被动态地降低。
Linux内核提供了两级独立的优先级范围
第一种是nice值,范围从-20到+19,默认值为0
第二个范围是实时优先级,其值是可配置的,默认情况下它的变化范围是从0到99。任何实时进程的优先级都高于普通的进程。

时间片
它表明进程在被抢占前所能持续运行的时间。调度策略必须规定一个默认的时间片,但这并不是件简单的事。时间片过长会导致系统对交互的响应表现欠佳;让人觉得系统无法并发执行应用程序。时间片太短会明显增大进程切换带来的处理器耗时,因为肯定会有相当一部分系统时间用在进程切换上,而这些鉴别能够用来运行的时间片却很短。此处,IO消耗型 和处理器消耗型的进程之间的矛盾在这里也再次显露出来;IO消耗开进不需要长时间片,而处理器消耗型 的进程则希望越来越好。
从上面的争论中可以看出,任何长时间片都将导致系统交互表现欠佳。很多操作系统中都特别重视这一点,所以默认的时间片很短。不过Linux利用了优先级最高的进程总是在运行这一原则。
Linux调度程序提高交互式程序的优先级,让它们运行得更频繁。于是,高度程序提供较长的默认时间片给交互式程序。此处,Linux高度程序还能根据进程的优先级动态调整分给给它的时间片。从而保证了优先高的进程,假定也是重要性高的进程,执行的频率高,执行时间长。通过实现这样一种动态调整优先级和时间片长度的机制,Linux高度性能不但非常稳定而且也很强健。
进程时间片的计算 最小5ms,默认100ms,最大800ms

当一个进程的时间片耗尽时,就认为进程到期了。没有时间片的进程不会再投入运行,除非等到其他所有的进程都耗尽了它们的时间片。在那个时候,所有进程的时间片会被重新计算。

进程抢占
当一个进程进入TASK_RUNNING状态,内核会检查它的优先级是否高于当前正在执行的过程。如果是这样,调度程序会被唤醒,抢占当前正在运行的进程并运行新的可运行进程。此外,当一个进程的时间片变为0时,它会被抢占,调度程序被唤醒以选择一个新的进程。

调度策略的活动
理论上认为高度程序给文字编辑程序更高的优先级和更长的时间片,因为它是交互式的。这将保证文字编辑程序有充足的时间片可用。此外,由于拥有较高的优先级,所以文字编辑程序还能在需要的进修抢占视频编码程序——例如,用户敲键盘的瞬间。这样才能保证文字编辑程序对用户键盘输入的即时响应。这样做当印古什 影响视频编码程序,但由于文字编辑程序仅仅只当用户按键时间断地运行,所以视频编码程序可以在所有剩余时间中独享处理器。这样的优化同时提高 了这两种应用的性能。

Linux调度算法
充分实现O1调度,不管有多少进程,新调度程序采用的每个算法都能在恒定时间内完成。
全面实现SMP的可扩展性。每个处理器拥有自己的锁和自己的可执行队列。
强化SMP的亲和力。尽量将相关一组任务分配给一个CPU进行连续的执行。只有在需要平衡任务队列的大小时才在CPU之间移动进程
加强交互性能。即使在系统处于相当负载的情况下,也能保证系统的响应,并立即高度交互式进程
保证公平。在合理设定的时间范围内,没有进程会牌饥饿状态。同样的,也没有里程能够公平地得到 大量时间片。
虽然最觉的优先情况是系统中只有1~2个可运行进程,但是优化也完全有能力扩展到具有多处理器且每个处理器上运行多个进程的系统中。
新的调度程序实现了上述目标。

可执行队列
高度程序中最基本的数据结构是运动队列(ruqueue)。可执行队列定义于kernel/sched.c中,由于结构runqueue表示。可执行队列是给定处理器上的可执行进程的链表,每个处理器一个。每个个可投入运行的进程都惟一的归属于一个可执行队列。此外,可执行队列中还包含每个处理器的高度信息。所以,可执行队列也是每个处理器最重要的数据结构。
struct runqueue{
spinlock_t lock; 保护运行队列的自旋锁
unsingned long nr_running; 可运行任务数目
unsingned long nr_switches; 上下文切换数目
unsingned long expired_timestamp; 队列最后被换出时间
unsingned long nr_uninterruptible; 处于不可中断睡眠状态的任务数目
unsingned long long timestamp_last_tick; 最后一个调度程序的节拍
struct taask_struct *curr; 当前运行任务
struct taask_struct *idle; 该处理器的空任务
struct mm_struct *prev_mm; 最后运行任务的mm_struct结构体
struct prio_array *active; 活动优先级队列
struct prio_array *expired; 超时优先级队列
struct prio_array arrays[2]; 实际优先级数组
struct task_struct *migration_thread; 移出线程
struct list_head *migration_queue; 移出队列
atomic_t nr_iowait; 等待IO操作的任务数目
};

由于可执行队列是调度程序的核心数据结构体,所以有一组宏定义用于获取与给定处理器或进程相关的可执行队列。cpu_rq(processor)宏用于返回给定处理器可执行队列的指针。类似地,this_rq宏用业返回当前处理器的可执行队列。最后,宏task_rq返回给定任务所在的队列指针。
在对可执行队列进行操作心前,应该先锁住它。因为每个可执行队列惟一地对应一个处理器,所以很少出现一个处理器需要锁其他处理器的可执行队列的情况。在其拥有者读取或必定队列成员的时候,可执行队列包含的锁用来防止队列被其他代码改动。锁住运行队列的最常见情况发生在你想销售的运行队列上恰巧有一个特定的任务在运行。此时需要用到task_rq_lock和task_rq_umlock函数

优先级数组
每个运行队列都有两个优先级数组,一个活跃和一个过期的。它是prio_array类型的结构体。优先级数组是一种能够提供O1级算法复杂度的数据结构。优先级数据使可运行处理器的每五种优先级都包含一个相应的队列,而这些队列包含对应优先级上的可执行进程链表。优先级数组还拥有一个优先级位图,当需要查找当前系统内拥有最高优先级的可执行进程时,它可以帮助提高效率。
struct prio_array{
int nr_active;
unsigned long bitmap(BITMAP_SIZE);
struct list_head queue[MAX_PRIO];
};

重新计算时间片
许多操作系统在所有进程的时间片都用完时,都采用一种显式的方法来重新计算每个进程的时间片。典型的实现是循环访问每个进程,像这样:
for (系统中的每个任务){
重新计算优先级
重新计算时间片
}
在永定新的时间片长短时会用到进程的优先级和其他一些属性。但这种实现存在一些弊端:
可能会耗费相当长的时间。最环情况下,有情人眼里出西施进程的系统复杂度可能达到On。
重算时必须靠锁的形式来保护任务队列和每个进程描述符。这样做会加剧对锁的急用。
它实现的很粗糙
新的Linux高度程序减少了对循环的依赖。取而代之的是它为每个处理器维护两个优先级数组:即有活动数组也有过期数组。活动数组内的可执行队列上的进程都还有赶时间片剩余;而过期数组内的可执行队列上的进程都耗尽了时间片。当一个进程的时间片耗尽时,它会被移到过期数组,但在此之前,时间片已经给它重新计算好了。重新计算时间片现在变得非常简单,只要在活动和过期数组之间来加切换就行。因为数组是通过指针进行访问的,所以交换它们用的时间就是交换指针需要的时间。这个动作由schedule完成

schedule()
选定下一个进程切换到它去执行是通过schedule函数实现的。当内核代码想要休眠时,会直接调用该函数,另外如果有哪个进程将被抢占,那么该函数也会被唤起执行。schedule函数独立于每个处理器运行。因此,每个CPU都要对下一次该运行哪个进程做出自己的判断。

首先,要在活动优先级数组中找到第一个被设置的位。该位对应着优先级最高的可执行进程。然后,调度程序选择这个级别链表里的头一个进程。这就是系统中优先级最高的可执行程序,也是马上会被调度执行的进程。

计算优先级和时间片
进程拥有一个初始的优先级,叫做nice值。该数值变化范围为-20到+19,默认值为0,进程task_struct和staic_prio域就存放着这个值。这所以起名为静态优先级static priority,是因为它从一开始由用户指定后,就不能改变。而调度程序要用到的动态优先级存放在prio域里。动态优先级通过一个关于静态优先级和进程交互性的函数关系计算而来。
effective_prio函数可以返回一个进程的动态优先级。这个函数以nice值为基数,再加上-5到+5之间的进程交互性的奖励或罚分。
如果一个进程的大部分时间都在休眠,那么它就是IO消耗型 的。如果一个进程执行的时间比休眠的时间长,那它就是处理器消耗型的。这个标准可以向极端延伸;一个进程如果几乎所有的时间都用在休眠上,那么它就是一个纯粹的IO消耗进程,相反,一个进程如果几乎所有的时间都在执行,那么它就是纯粹的处理器消耗型进程。

睡眠和唤醒
休眠的进程牌一个特殊的不可执行状态。这点非常重要,否则,没有这种特殊状态的话,调度程序就可能选出一个本不愿意被执行的进程,更糟糕的是,休眠就必须以轮方式实现。
如果在进程开始休眠之前条件就已经达成了,那么循环会退出,进程不会存在错误的进入休眠的倾向。需要注意的是,内核代码在循环体内常常需要完成一些其他的任务,比如,它可能在调用schedule()之前需要释放掉锁,而在这以后再重新获取它们,或者响应其他事件。
唤醒操作通过函数wake_up进行,它会唤醒指定的等待队列上的所有进程。它调用函数try_to_wake_up(),该函数负载将进程设置为TASK_RUNNING状态,调用activate_task将此进程放入可执行队列,如果被唤醒的进程优先级比当前正在执行的进程的优先级高,还要设置need_resched标志。通常哪段代码促使等待条件达成,它就要负责随后调用 wake_up函数。举例来说,当磁盘数据到来以后,VFS就要负责对等待队列调用wake_up,以便唤醒队列中等待这些数据的过程。

负载平衡程序
负载平衡程序会拿当前处理器的可执行队列和系统中的其他可执行队列做比较。如果它发行了不均衡,就会把相对繁忙的队列中的进程抽到当前的可执行队列中来。理想情况下,每个队列上的进程数目应该相等。这是个难以企及的目标,但负载平衡程序做得已经非常接近。

在schedule执行的时候,只要当前的可执行队列为空,它就会被调用。此外,它还会被定时器调用;系统空闲时每隔1毫秒调用 一次或者在其他情况下每隔200毫抄调用一闪。在单处理器系统中,load_balance不会被调用,它甚至不会被编译进内核。因为那里面只有一个可执行队列,因此根本没有平均负载的必要。
首先,load_balance()调用find_busies_queue(),找到最繁忙的可执行队列。也就是说,该队列中的进程数目最多。如果 没有哪个可执行队列中的进程的数目比当前队列中的数目多25%或者25%以上,find_busies_queue()返回NULL,并且load_balance()也返回。否则,最繁忙的可执行队列就被返回。
其次,load_balance()从最繁忙的运行队列中选择一人优先级数组以便抽取进程。最好是过期数组,因为那里面的进程已经有相对较长的一段时间没有运行了,很可能不在处理器的调整缓存中。如果过期数组为空,那就只能选活动数组。
接着,load_balance()寻找到含有进程并且优先级最高的链表,因为把优先级高的进程平均分散开来才是最重要的。
分析找到的所有这些优先级相同的进程,选择一个不是正在执行,也不会因为处理器相关性而不可移动,并且不在高速缓存中的进程。如果有进程满足这些条件,调用pull_task()将其从最繁忙的队列中抽取到当前队列中。
只要可执行队列之间仍然不均衡,就重复上面两个步骤,继续从繁忙折队列中抽取进程到当前队列。这最终会消除不平衡,此时,解析对当前运行队列进行锁定,从load_balance()返回。

抢占和上下文切换
从一个可执行进程切换到另一个可执行进程。每当一个新的进程被选出来准备投入运行的时候,schedule就会调用该函数。它完成了两项基本工作:
调用定义在

你可能感兴趣的:(linux,shell,工具,性能,测试,命令)