天天看點

為什麼ps中CPU占用率會有超出%100的現象?

前面的關于ps中的%CPU的含義一文已經介紹了CPU占用率的含義,那麼為什麼有時會在ps的輸出中看到CPU占用率超出%100的現象呢?我們知道在/proc目錄下每個程序都會有一個以它的PID以名字的目錄,這個目錄中有一個stat檔案,它包含了和這個程序狀态相關的各種資訊,它的各個數值對應的含義在核心文檔的Documentation/filesystems/proc.txt檔案中有明确的定義:

Table 1-3: Contents of the stat files (as of 2.6.22-rc3)

..............................................................................

 Field          Content

  pid           process id

  tcomm         filename of the executable

  state         state (R is running, S is sleeping, D is sleeping in an

                uninterruptible wait, Z is zombie, T is traced or stopped)

  ppid          process id of the parent process

  pgrp          pgrp of the process

  sid           session id

  tty_nr        tty the process uses

  tty_pgrp      pgrp of the tty

  flags         task flags

  min_flt       number of minor faults

  cmin_flt      number of minor faults with child's

  maj_flt       number of major faults

  cmaj_flt      number of major faults with child's

  utime         user mode jiffies

  stime         kernel mode jiffies

  cutime        user mode jiffies with child's

  cstime        kernel mode jiffies with child's

  priority      priority level

  nice          nice level

  num_threads   number of threads

  start_time    time the process started after system boot

  vsize         virtual memory size

  rss           resident set memory size

  rsslim        current limit in bytes on the rss

  start_code    address above which program text can run

  end_code      address below which program text can run

  start_stack   address of the start of the stack

  esp           current value of ESP

  eip           current value of EIP

  pending       bitmap of pending signals (obsolete)

  blocked       bitmap of blocked signals (obsolete)

  sigign        bitmap of ignored signals (obsolete)

  sigcatch      bitmap of catched signals (obsolete)

  wchan         address where process went to sleep

  0             (place holder)

  exit_signal   signal to send to parent thread on exit

  task_cpu      which CPU the task is scheduled on

  rt_priority   realtime priority

  policy        scheduling policy (man sched_setscheduler)

  blkio_ticks   time spent waiting for block IO

這其中就包括這個程序的stime和utime,而ps就是檢視這個檔案來獲得程序運作的時間,進而計算出%CPU,那麼stat這個檔案中的stime和utime是怎樣得到的呢?在fs/proc/array.c中定義了下面兩個函數

int proc_tgid_stat(struct task_struct *task, char *buffer)

{

    return do_task_stat(task, buffer, 1);

}

int proc_tid_stat(struct task_struct *task, char *buffer)

    return do_task_stat(task, buffer, 0);

在每次讀取程序狀态資訊時,proc檔案系統就是調用這兩個函數來填充資料的,它們的差別隻有調用do_task_stat時傳遞的最後一個參數不同,看一下do_task_stat的代碼就知道這個參數的含義了:

static int do_task_stat(struct task_struct *task, char *buffer, int whole)

       ...

/* add up live thread stats at the group level */

        if (whole) {

            struct task_struct *t = task;

            do {

                min_flt += t->min_flt;

                maj_flt += t->maj_flt;

                utime = cputime_add(utime, task_utime(t));

                stime = cputime_add(stime, task_stime(t));

                t = next_thread(t);

            } while (t != task);

            min_flt += sig->min_flt;

            maj_flt += sig->maj_flt;

            utime = cputime_add(utime, sig->utime);

            stime = cputime_add(stime, sig->stime);

        }

...

如果whole的值為1, 那麼proc檔案系統會把這個程序中各個線程的運作時間累加起來,其中next_thread這個函數就是擷取這個程序中的下一個線程。在fork的時候,如果指定了CLONE_THREAD标志,也就是新建立的線程和它的父程序在同一個線程組,那麼fork會它加入到這個線程中:

if (clone_flags & CLONE_THREAD) {

        p->group_leader = current->group_leader;

        list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);

而next_thread就是沒着它的thread_group所在的連結清單進行周遊,擷取線程組中的每個線程。這樣就可以解釋為什麼%CPU字段有超過100%了,因為分子是這個程序(線程組)中所有線程運作的時間,而在同一時刻,同一線程組中的兩個不同線程可能在兩個不同的CPU上運作,這樣總的運作時間就有可能超過實體上真正過去的時間(分母)可見,這種情況隻會在SMP的系統上發生。

    執行ps aux時是按程序輸出的,但是如果這個程序中還有其他線程,它的stat字段有一個l, 比如firefox

[root@localhost 3013]# ps aux|grep firefox-bin

root      3091 15.6 26.6 374644 137048 ?       Sl   10:05  47:49 /usr/lib/firefox-2.0.0.12/firefox-bin -UILocale zh-CN

[root@localhost 3013]# ps aux -L|grep firefox-bin

root      3091  3091 11.3   12 26.6 374644 137056 ?     Sl   10:05  34:40 /usr/lib/firefox-2.0.0.12/firefox-bin -UILocale zh-CN

root      3091  3130  0.0   12 26.6 374644 137056 ?       Sl   10:05   0:01 /usr/lib/firefox-2.0.0.12/firefox-bin -UILocale zh-CN

root      3091  3131  0.1   12 26.6 374644 137056 ?       Sl   10:05   0:25 /usr/lib/firefox-2.0.0.12/firefox-bin -UILocale zh-CN

root      3091  3140  0.0   12 26.6 374644 137056 ?       Sl   10:05   0:00 /usr/lib/firefox-2.0.0.12/firefox-bin -UILocale zh-CN

root      3091  3141  0.0   12 26.6 374644 137056 ?       Sl   10:05   0:00 /usr/lib/firefox-2.0.0.12/firefox-bin -UILocale zh-CN

上面的L參數面顯示其他的線程及其TID,程序号和線程号相同的線程就是它的第一個線程,即3091,進入這個目錄可以看到:

[root@localhost proc]# cd 3091

[root@localhost 3091]# ls

attr    clear_refs       cpuset   exe     io        maps    mountstats  root       smaps  status

auxv    cmdline          cwd      fd      limits    mem     oom_adj     sched      stat   task

cgroup  coredump_filter  environ  fdinfo  loginuid  mounts  oom_score   schedstat  statm  wchan

[root@localhost 3091]# cd task/

[root@localhost task]# ls

11850  11851  11853  11854  11855  3091  3130  3131  3140  3141  3142  3155  3158

[root@localhost task]# cd 3130

[root@localhost 3130]# ls

attr    clear_refs  cwd      fd      loginuid  mounts     root       smaps  status

auxv    cmdline     environ  fdinfo  maps      oom_adj    sched      stat   wchan

cgroup  cpuset      exe      limits  mem       oom_score  schedstat  statm

在一個程序的目錄中的task目錄下會包含其他的線程的資訊。實際上, 在核心中程序和線程并沒有什麼本質的差別,隻不過如果fork的時候共享位址空間那就是線程,否則就是程序。

Email:[email protected]

繼續閱讀