Linux 监控之 CPU

2013-04-14 Sunday     linux

Linux 中的系统监控,包括了常见的计算、内存、网络等监控指标,以及相应的工具。

CPU Usage VS. LOAD

Linux 系统的 CPU 使用率就是 CPU 的使用状况,也就是一段时间之中,CPU 用于执行任务占用的时间与总的时间的比率。

相关文件

CPU 的全局性能指标保存在 /proc/stat 文件中,大部分监控都是读取该文件,如 dstat 。对于文件的解读可以参考 man 5 proc 中的 /proc/stat 部分,也可以参考 online man 5 proc

进程和线程的统计可参考 /proc/<pid>/stat/proc/<pid>/task/<tid>/stat;需要注意的是,对于不同的内核版本, /proc/stat 内容会有所区别:steal (since Linux 2.6.11)guest (since Linux 2.6.24)guest_nice (since Linux 2.6.33)

cpu  3816877 11951 424208 1254811 21564 0 2153 0 0 0
cpu0 958728 2517 108301 895882 15136 0 1048 0 0 0
cpu1 953763 3080 108605 117901 2376 0 431 0 0 0
cpu2 955060 3789 102803 119840 2140 0 588 0 0 0
cpu3 949325 2563 104497 121187 1910 0 85 0 0 0
intr 88396062 67 62144 0 0 0 0 0 0 1 126484 0 0 2126483 ... ...
ctxt 156849121
btime 1488368299
processes 126703
procs_running 2
procs_blocked 0
softirq 76359224 43 47319594 111 58877 726684 9 63206 16513537 0 11677163

对于上述文件的内容,简单介绍如下。

cpu cpuN
    CPU使用情况,都是从系统启动到当前的值,单位是节拍 (Jiffies),总计分为10列;
  **user
    普通进程处于用户态的运行时间,不包括nice为负的进程消耗时间。
  **nice
    nice值为负的进程所占用的用户态时间。
  **system
    处于核心态的运行时间,也包括 IRQ 和 softirq 时间。
    如果系统CPU占用率高,表明系统某部分存在瓶颈,通常值越低越好。
  **idle
    空闲时间,不包含 IO 等待时间。
  **iowait
    等待 IO 响应的时间(since 2.5.41)。
    如果系统花费大量时间等待 IO ,说明存在 IO 瓶颈。
  **irq
    处理硬中断的时间(since 2.6.0-test4)。
  **softirq
    处理软中断的时间(since 2.6.0-test4)。
  **steal
    当采用虚拟环境时,运行在其它操作系统上的时间,此时hypervisor在为另一个虚拟处理器服务,具体监控指标详见后面。
  **guest
    内核运行在虚拟机上的时间。
  **guest_nice
    同上。
intr
    中断信息,首列为系统启动以来,发生的所有的中断的次数;接着的每个数对应特定中断自系统启动以来所发生的次数。
ctxt
    系统启动以来CPU发生的上下文交换的次数。
btime
    给出了从系统启动到现在为止的时间,单位为秒。
processes
    自系统启动以来所创建的任务的个数目。
procs_running
    当前运行队列的任务的数目。
procs_blocked
    当前被阻塞的任务的数目。

/proc/stat 文件中定义了 CPU 相关的指标,需要注意其单位,如果单纯计算比例 (例如使用率) 可以忽略。

各个字段的计算规则如下:

Total CPU time since boot = user+nice+system+idle+iowait+irq+softirq+steal
Total CPU Idle time since boot = idle + iowait
Total CPU usage time since boot = Total CPU time since boot - Total CPU Idle time since boot
Total CPU percentage = Total CPU usage time since boot/Total CPU time since boot X 100

注意:guestguest_nice 已经统计到了 usernice 中,所以在计算总量的时候应该忽略。

PS: 如果 iowait 的值过高,表示硬盘存在 I/O 瓶颈。如果 idle 值高但系统响应慢时,有可能是 CPU 等待分配内存,此时应加大内存容量。

内核实现

/proc/stat 文件在 proc_stat_init()@fs/proc/stat.c 函数中实现,会在内核初始化时调用;对上述文件的读写,会调用 proc_stat_operations 所定义的函数。

static const struct file_operations proc_stat_operations = {
    .open       = stat_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

打开文件会调用函数 stat_open() ,该函数首先申请大小为 size 的内存,来存放临时数据,也是我们看到的 stat 里的最终数据;文件中的数据由 show_stat() 函数填充。

static int show_stat(struct seq_file *p, void *v)
{
    int i, j;
    unsigned long jif;
    u64 user, nice, system, idle, iowait, irq, softirq, steal;
    u64 guest, guest_nice;
    u64 sum = 0;
    u64 sum_softirq = 0;
    unsigned int per_softirq_sums[NR_SOFTIRQS] = {0};
    struct timespec boottime;

    user = nice = system = idle = iowait =
        irq = softirq = steal = 0;
    guest = guest_nice = 0;
    getboottime(&boottime);
    jif = boottime.tv_sec;

    for_each_possible_cpu(i) {
        user += kcpustat_cpu(i).cpustat[CPUTIME_USER];
        nice += kcpustat_cpu(i).cpustat[CPUTIME_NICE];
        system += kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM];
        idle += get_idle_time(i);
        iowait += get_iowait_time(i);
        irq += kcpustat_cpu(i).cpustat[CPUTIME_IRQ];
        softirq += kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ];
        steal += kcpustat_cpu(i).cpustat[CPUTIME_STEAL];
        guest += kcpustat_cpu(i).cpustat[CPUTIME_GUEST];
        guest_nice += kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE];
        sum += kstat_cpu_irqs_sum(i);
        sum += arch_irq_stat_cpu(i);

        for (j = 0; j < NR_SOFTIRQS; j++) {
            unsigned int softirq_stat = kstat_softirqs_cpu(j, i);

            per_softirq_sums[j] += softirq_stat;
            sum_softirq += softirq_stat;
        }
    }
    sum += arch_irq_stat();

    seq_puts(p, "cpu ");
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(user));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(nice));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(system));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(idle));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(iowait));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(irq));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(softirq));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(steal));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest));
    seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest_nice));
    seq_putc(p, '\n');

    for_each_online_cpu(i) {
        /* Copy values here to work around gcc-2.95.3, gcc-2.96 */
        user = kcpustat_cpu(i).cpustat[CPUTIME_USER];
        nice = kcpustat_cpu(i).cpustat[CPUTIME_NICE];
        system = kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM];
        idle = get_idle_time(i);
        iowait = get_iowait_time(i);
        irq = kcpustat_cpu(i).cpustat[CPUTIME_IRQ];
        softirq = kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ];
        steal = kcpustat_cpu(i).cpustat[CPUTIME_STEAL];
        guest = kcpustat_cpu(i).cpustat[CPUTIME_GUEST];
        guest_nice = kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE];
        seq_printf(p, "cpu%d", i);
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(user));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(nice));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(system));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(idle));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(iowait));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(irq));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(softirq));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(steal));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest));
        seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest_nice));
        seq_putc(p, '\n');
    }
    seq_printf(p, "intr %llu", (unsigned long long)sum);

    /* sum again ? it could be updated? */
    for_each_irq_nr(j)
        seq_put_decimal_ull(p, ' ', kstat_irqs_usr(j));

    seq_printf(p,
        "\nctxt %llu\n"
        "btime %lu\n"
        "processes %lu\n"
        "procs_running %lu\n"
        "procs_blocked %lu\n",
        nr_context_switches(),
        (unsigned long)jif,
        total_forks,
        nr_running(),
        nr_iowait());

    seq_printf(p, "softirq %llu", (unsigned long long)sum_softirq);

    for (i = 0; i < NR_SOFTIRQS; i++)
        seq_put_decimal_ull(p, ' ', per_softirq_sums[i]);
    seq_putc(p, '\n');

    return 0;
}

在上述的函数中,会依次统计 cpu 的各个参数,下面以 user 为例。

参考



如果喜欢这里的文章,而且又不差钱的话,欢迎打赏个早餐 ^_^


About This Blog

Recent Posts

Categories

Related Links

  • RTEMS
    RTEMS
  • GNU
  • Linux Kernel
  • Arduino

Search


This Site was built by Jin Yang, generated with Jekyll, and hosted on GitHub Pages
©2013-2019 – Jin Yang