src:http://www.ibm.com/developerworks/cn/aix/library/0909_unixcpu_wangwh/index.html
因为 Linux 系统特有的开放性,用户可以自主的添加,定义或者改变系统原有的功能。有很多开源项目提供了基于图形的或者命令行的工具,帮助用户更加方便的识别、监控甚至改变系统中的处理器的运行状态。考虑到通用的原则,本文仅介绍常见 Linux 发行版本中已经集成的接口或者工具。
查看 /proc/stat 文件获得 CPU 状态
/proc/stat 的内容包含了系统中的各项基本信息,例如 CPU 状态。用户可以详细了解系统中每个处理器自系统启动以来的运行状态,并以此分析和评估应用环境的工作是否正常。
# cat /proc/stat
cpu 507853 325 148706 190270813 190032 152 884 0 0
cpu0 178463 111 68890 95035802 93558 34 96 0 0
cpu1 329389 213 79816 95235010 96474 117 787 0 0
intr 93566218 3310 2 0 0 2 0 3 0 30 0 0 0 4 0 1623929 10015567 2440 20098 0 0 0 0 0 0 0
3712917 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0
…
…
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 202868862
btime 1242974927
processes 356350
procs_running 1
procs_blocked 0 |
命令输出的最初几行包含了系统中所有 CPU 的活动信息,在 kernel2.6.29 下的 /proc/stat 中,每行内容的处理器名后有九个数字,而早期的 kernel 会缺失一些内容,比如与虚拟化相关的信息。所有这些数值都是从系统启动开始累计到当前时刻的,并以 0.01 秒为单位。以下是这些数值的解释
- user: 用户模式占用的 CPU 时间
- nice: 低优先级用户模式所占用的 CPU 时间
- system: 系统核心态占用的 CPU 时间
- idle:cpu 空闲时间
- iowait:硬盘 IO 等待时间 ( 从 kernel 2.5.41 开始添加此项信息 )
- irq:硬中断时间 ( 丛 kernel 2.6.0-test4 开始添加此项信息 )
- softirq:软中断时间(同上)
- steal:在虚拟环境下,处理其他作业系统占用的 cpu 时间(丛 kernel 2.6.11 开始添加此项信息)
- guest: 作业系统运行虚拟 cpu 占用 cpu 的时间(丛 kernel2.6.24 开始添加此项信息)
根据 /proc/stat 文件的内容,用户可以自行计算任意时间段内 CPU 的占用率;
CPU 时间 =user+system+nice+idle+iowait+irq+softirq+steal+guest
选取两个时间点 T1 和 T2, CPU 在 T1 和 T2 时间段中的使用时间可以如下计算:
Total=(user2+system2+nice2+idle2+iowait2+irq2+softirq2+steal2+guest2)-( user1+system1+nice1+idle1+iowait1+irq1+softirq1+steal1+guest1)
CPU 空闲百分比:
idle = (idle2 - idle1) / Total * 100%
CPU 占用百分比:
occupy = (1 - idle) * 100%
除了最初的 CPU 行以外,/proc/stat 文件还有其他与系统性能相关的一些参数。其中 ctxt 是系统启动以来 CPU 发生的上下文交换的次数。
读取 /proc/loadavg 文件获得 CPU 状态
# cat /proc/loadavg
5.86 5.78 5.89 12/840 22261 |
命令输出前三项分别表示最近 1 分钟,最近 5 分钟及最近 15 分钟运行队列中的平均进程数,第四项包括由斜线隔开的两个数值,前者表示当前正由内核调度的进程和线程的数目,后者表示系统当前存活的进程数目;第五个值表示此文件被查看前,最近一个由内核创建的进程的 PID。
利用 ps 命令
系统处理器的工作是处理任务。而在现代操作系统中,任务是以进程的形式表现的。所以定位分析系统处理器的问题,也需要用到进程相关的接口或指令。ps 命令是分析 CPU 状态的重要工具,它读取 /proc 文件系统中各进程的信息,并且加以显示。用户首先需要定位感兴趣的进程,这可以通过 ps 命令来确定,也可以结合其他系统工具。很多情况下,用户仅需关注某个或某几个进程。
[root@bldlnx02 ~]# ps -e -o pcpu,pid,user,sgi_p,cmd |grep -v PID| sort -k 1| tail -5
0.0 8 root * [khelper]
0.0 9 root * [kacpid]
0.1 21949 root * sshd: csm@pts/0
0.2 21951 root * -bash
99.9 12392 root * /usr/bin/Xvnc -inetd -query 127.0.0.1 -once -depth 24
-desktop VNC Session Manager -rfbauth /root/.vnc/passwd -geometry 640x480 |
系统中通常会有多进程的程序运行。当这些进程占用了较多的 CPU 时间时,可以单独列出该程序的所有进程。多数情况下,用户也需要进程的启动时间,状态,父进程号 (PPID),占用的内存来分析系统和进程的状态。
# ps -C nfsd -o cmd,pCPU,start_time,user,ppid,size,stat
CMD %CPU START USER PPID SZ STAT
[nfsd] 0.0 May12 root 1 0 D
[nfsd] 0.0 May12 root 1 0 D
[nfsd] 0.0 May12 root 1 0 D
[nfsd] 0.0 May12 root 1 0 D
[nfsd] 0.0 May12 root 1 0 S
[nfsd] 0.0 May12 root 1 0 S
[nfsd] 0.0 May12 root 1 0 D
[nfsd] 0.0 May12 root 1 0 D |
在以上输出中各个字段分别表示如下含义:
- CMD 进程命令名
- %CPU 进程占用的 CPU 百分比
- START 进程启动的时间
- USER 进程的属主
- PPID 父进程号
- SZ 进程占用的内存空间
- STAT 进程状态
ps 命令的输出结果表示系统中共有八个 nfs 服务进程,是根用户在一天以前启动的。当前这些进程没有使用 CPU 时间或内存。并且分别处于不同的状态。ps 命令可以接受的状态种类共有七种,仅介绍清单 15 中的两个作为示例
- D 无法中断的休眠状态(通常 IO 的进程)
- S 处于休眠状态
可以看到有六个 nfs 服务进程处于无法中断的休眠状态,这表示 NFS 服务出现了问题。用户可以通过检查物理存储可用性,查看 /var/log/messages 文件或 sysrq-T 系统 trace 等手段来进一步定位和排查错误。
本段的最后给出一段例子代码,可以获得一段时间内的 CPU 利用率,并且找出系统中占用 CPU 较多的进程,已在 Redhat 企业服务器和 Suse 企业服务器上测试通过。代码首先选取两个相隔一分钟的两个时间段,分别读取文件 /proc/stat 的内容,获得 CPU 在两个时刻的进程占用时间等数值来计算 CPU 利用率。并且读取文件 /proc/loadavg 获得处理器上的集成负载。在负载较高的情况下,调用命令 ps 找出消耗 CPU 最多的几个进程,及其所在的处理器(processor)。
# 两次读取 /proc/stat 文件的内容
my $file = '/proc/stat';
open FILE, "<$file" or die "Failed to open file $file.\n";
my @fir_contents = ;
close FILE;
sleep 60;
open FILE, "<$file" or die "Failed to open file $file.\n";
my @sec_contents = ;
close FILE;
# 把 /proc/stat 文件的内容存入一个散列
my (%cpuinfo);
my $cpu_num = 0;
my $line_num = 0;
foreach my $line (@fir_contents, @sec_contents){
$line_num ++;
if ($line =~ m/^cpu/) {
# 判断当前数据来自第一次读取还是第二次读取
my $info_time;
if ($line_num <= scalar(@fir_contents)) {
$info_time = "first";
}else {
$info_time = "second";
}
# 向散列写入数据
chomp $line;
my ($cpu, $user, $nice, $sys, $idle, $io, $irq, $softirq, $steal, $guest) =
split /\s+/, $line;
$cpuinfo{$info_time}{$cpu}{'user'} = $user;
$cpuinfo{$info_time}{$cpu}{'nice'} = $nice;
$cpuinfo{$info_time}{$cpu}{'sys'} = $sys;
$cpuinfo{$info_time}{$cpu}{'idle'} = $idle;
$cpuinfo{$info_time}{$cpu}{'io'} = $io;
$cpuinfo{$info_time}{$cpu}{'irq'} = $irq;
$cpuinfo{$info_time}{$cpu}{'softirq'} = $softirq;
$cpuinfo{$info_time}{$cpu}{'$steal'} = $steal;
$cpuinfo{$info_time}{$cpu}{'guest'} = $guest;
$cpu_num ++;
}
}
# 读取散列中的数据以计算 CPU 利用率
my (%cpu_data, %cpu_usage);
foreach my $info_time (keys %cpuinfo)
{
foreach my $cpu (keys %{$cpuinfo{$info_time}}) {
$cpu_data{$cpu}{$info_time}{'total'} = 0;
$cpu_data{$cpu}{$info_time}{'idle'} = $cpuinfo{$info_time}{$cpu}{'idle'};
foreach my $key (keys %{$cpuinfo{$info_time}{$cpu}}) {
$cpu_data{$cpu}{$info_time}{'total'} += $cpuinfo{$info_time}{$cpu}{$key};
}
}
}
my $check_proc;
foreach my $cpu (keys %cpu_data){
my $idle_interval = $cpu_data{$cpu}{'second'}{'idle'} -
$cpu_data{$cpu}{'fisrt'}{'idle'};
my $total_interval = $cpu_data{$cpu}{'second'}{'total'} -
$cpu_data{$cpu}{'fisrt'}{'total'};
$cpu_usage{$cpu} = (1 - $idle_interval/$total_interval);
if ($cpu_usage{$cpu} > 0.5) {$check_proc = 1;}
}
# 从 /proc/loadavg 读取处理器上的进程负载
$file = '/proc/loadavg';
open FILE, "<$file" or die "Failed to open file $file.\n";
my @contents = ;
close FILE;
my ($load1, $load5, $load15, $proc_num, $pid);
foreach my $line (@contents) {($load1, $load5, $load15, $proc_num, $pid) = \n
split /\s+/, $line;}
if ($load1 > 5) {$check_proc = 1;}
# 如果处理器空闲,退出
if (!$check_proc){
print "CPU load on this machine is normal.\n";
exit 0;
}
# 如果处理器繁忙,找出占用 CPU 时间的进程
my @output = `ps -e -o pcpu,pid,user,sgi_p,cmd |grep -v PID| sort -k 1| tail -10`;
foreach my $line (@output){
$line =~ s/^\s+//g;
my ($cpu, $pid, $user, $proc, $cmd) = split /\s+/, $line;
if ($cpu < 5) {next;}
# 输出进程号和进程占用的 CPU 百分比
print "Process $pid of user $user takes $cpu% CPU time on processor $proc.\n";
}
exit 0; |