Open18

topコマンドで得られるCPU使用率

akym03akym03

やりたいことは、 top(1) の %CPU の求め方を知りたいので、ソースを追いかける。
環境は Amazon Linux 2。

akym03akym03

まずは、ソースファイルを探す所からなので、rpm -f を使って topコマンドをインストールしたパッケージ情報を参照すると、 https://sourceforge.net/projects/procps-ng/ であることが分かった。

$ rpm -qfi `which top`
Name        : procps-ng
Version     : 3.3.10
Release     : 26.amzn2
Architecture: x86_64
Install Date: Thu 28 Apr 2022 07:54:28 PM UTC
Group       : Applications/System
Size        : 758091
License     : GPL+ and GPLv2 and GPLv2+ and GPLv3+ and LGPLv2+
Signature   : RSA/SHA256, Tue 22 Oct 2019 08:43:34 PM UTC, Key ID 11cf1f95c87f5b1a
Source RPM  : procps-ng-3.3.10-26.amzn2.src.rpm
Build Date  : Wed 14 Aug 2019 12:01:21 AM UTC
Build Host  : build.amazon.com
Relocations : (not relocatable)
Packager    : Amazon Linux
Vendor      : Amazon Linux
URL         : https://sourceforge.net/projects/procps-ng/
Summary     : System and process monitoring utilities
Description :
The procps package contains a set of system utilities that provide
system information. Procps includes ps, free, skill, pkill, pgrep,
snice, tload, top, uptime, vmstat, w, watch and pwdx. The ps command
displays a snapshot of running processes. The top command provides
a repetitive update of the statuses of running processes. The free
command displays the amounts of free and used memory on your
system. The skill command sends a terminate command (or another
specified signal) to a specified set of processes. The snice
command is used to change the scheduling priority of specified
processes. The tload command prints a graph of the current system
load average to a specified tty. The uptime command displays the
current time, how long the system has been running, how many users
are logged on, and system load averages for the past one, five,
and fifteen minutes. The w command displays a list of the users
who are currently logged on and what they are running. The watch
program watches a running program. The vmstat command displays
virtual memory statistics about processes, memory, paging, block
I/O, traps, and CPU activity. The pwdx command reports the current
working directory of a process or processes.

SourceForge のサイトを訪れると、gitリポジトリは https://gitlab.com/procps-ng/procps に移動したとあるので、GitLabのリポジトリから バージョン 3.3.10 のソース を取得する。

akym03akym03

topコマンドのソースファイルは https://gitlab.com/procps-ng/procps/-/blob/v3.3.10/top/top.c で、
main関数は下の内容。 Loops変数 は -n オプションの値が設定され、指定された回数ループしたら終了する模様。初期値は -1 。

int main (int dont_care_argc, char **argv) {
   (void)dont_care_argc;
   before(*argv);
                                        //                 +-------------+
   wins_stage_1();                      //                 top (sic) slice
   configs_reads();                     //                 > spread etc, <
   parse_args(&argv[1]);                //                 > lean stuff, <
   whack_terminal();                    //                 > onions etc. <
   wins_stage_2();                      //                 as bottom slice
                                        //                 +-------------+

   for (;;) {
      struct timespec ts;

      frame_make();

      if (0 < Loops) --Loops;
      if (!Loops) bye_bye(NULL);

      ts.tv_sec = Rc.delay_time;
      ts.tv_nsec = (Rc.delay_time - (int)Rc.delay_time) * 1000000000;

      if (Batch)
         pselect(0, NULL, NULL, NULL, &ts, NULL);
      else {
         if (ioa(&ts))
            do_key(iokey(1));
      }
           /* note: that above ioa routine exists to consolidate all logic
                    which is susceptible to signal interrupt and must then
                    produce a screen refresh. in this main loop frame_make
                    assumes responsibility for such refreshes. other logic
                    in contact with users must deal more obliquely with an
                    interrupt/refresh (hint: Frames_signal + return code)!

                    (everything is perfectly justified plus right margins)
                    (are completely filled, but of course it must be luck)
            */
   }
   return 0;
} // end: main
akym03akym03

topコマンドの -d オプションで指定できる更新間隔は pselect(2) のタイムアウトで実現している。インタラクティブモードの場合は、 ioa関数 の中で使っていた。

akym03akym03

プロセスの情報を読み取り出力を行うのは frame_make 関数なので、この関数を見てみると、procs_refresh() を呼び出している。いかにも、プロセスの情報を読み取っていそう。

akym03akym03

procs_refresh 関数は libprocps のお作法に則って、 /proc/[pid]/stat からプロセスの情報を読んでいる。 https://qiita.com/rarul/items/e02b4f8faaf69890bc18 が参考になった。

主な変数

  • proc_t 構造体 は プロセスの情報を管理し、 proc(5) に記載されている /proc/[pid]/stat を読み取った情報
  • private_ppt 変数 は 複数の proc_t を格納する領域
  • PROCTAB 構造体 は openprocの戻り値。 readprocで使う。
  • read_something 変数 は topコマンドの -H オプションで切り替わる。 -H オプションが指定されていればスレッドの情報も読み込む readeither 関数を指し、-H オプションが指定されていなければ readproc 関数を指す。

読み込んだ結果は グローバル変数 Winstk に保存されている。ところで GROUPSMAX のグループって何だろう。

        /* /////////////////////////////////////////////////////////////// */
        /* Special Section: multiple windows/field groups  --------------- */
        /* ( kind of a header within a header: constants, types & macros ) */

#define CAPTABMAX  9             /* max entries in each win's caps table   */
#define GROUPSMAX  4             /* the max number of simultaneous windows */
#define WINNAMSIZ  4             /* size of RCW_t winname buf (incl '\0')  */
#define GRPNAMSIZ  WINNAMSIZ+2   /* window's name + number as in: '#:...'  */

マルチウィンドウってあるけど、よくわからない。自分が知らないtopの機能かな。

akym03akym03

procs_refresh 関数は大体わかったので、 frame_make関数に戻ろう。

akym03akym03

procs_refresh 関数の次は summary_show 関数。その名前の通りサマリー情報、具体的にはターミナルの上部に表示される下の部分を表示している。

top - 00:08:11 up 0 min,  0 users,  load average: 0.82, 0.18, 0.06
Tasks: 113 total,   3 running,  67 sleeping,   0 stopped,   0 zombie
%Cpu(s):100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2001104 total,   841628 free,   683812 used,   475664 buff/cache
KiB Swap:   499996 total,   499996 free,        0 used.  1170004 avail Mem 

frame_make関数で 表示されるプロセス数 に関わっていそうなのは下の3つ。

  1. Screen_rows は ターミナルの縦方向の行数。
  2. Msg_row は表示済みの行数。 summary_show 関数で表示した行数をカウントしていた。これからは1件のプロセスを表示したら、カウントアップされるはず。
  3. Max_lines は 表示できる上限なので、表示されるプロセス数の条件に相当する。
akym03akym03

プロセスの表示を行っていそうなのは、この部分。

   if (!Rc.mode_altscr) {
      // only 1 window to show so, piece o' cake
      w->winlines = w->rc.maxtasks ? w->rc.maxtasks : Max_lines;
      scrlins = window_show(w, Max_lines);
   } else {
      // maybe NO window is visible but assume, pieces o' cakes
      for (i = 0 ; i < GROUPSMAX; i++) {
         if (CHKw(&Winstk[i], Show_TASKON)) {
            frame_hlp(i, Max_lines - scrlins);
            scrlins += window_show(&Winstk[i], Max_lines - scrlins);
         }
         if (Max_lines <= scrlins) break;
      }
   }

Rc.mode_altscr という変数 が出てくるので、定義を参照してみると

'A' - Alt display mode (multi task windows)

とあった。

topコマンドを実行中に (大文字の)A を入力すると4分割された状態に切り替わる。man(1)の和訳には「 別形式の表示モード」と書かれていた。 マルチウィンドウが分かってすっきり。

1:Def - 00:45:03 up 37 min,  0 users,  load average: 0.02, 0.03, 0.00
Tasks: 106 total,   1 running,  64 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.5 us,  0.7 sy,  0.0 ni, 97.3 id,  0.0 wa,  0.0 hi,  0.0 si,  1.5 st
KiB Mem :  2001104 total,  1067372 free,   372336 used,   561396 buff/cache
KiB Swap:   499996 total,   499996 free,        0 used.  1445736 avail Mem 

1  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                    
  4995 ec2-user  20   0  997004 140928  32220 S   0.0  7.0   0:03.44 node                                                                                       
  3541 root      20   0 1370424  75024  49344 S   0.0  3.7   0:00.48 dockerd                                                                                    
  4255 ec2-user  20   0  935204  64160  31148 S   0.0  3.2   0:04.28 node                                                                                       
  3121 root      20   0 1369496  39924  28280 S   0.0  2.0   0:00.99 containerd                                                                                 
  4961 ec2-user  20   0  684764  35556  28636 S   0.0  1.8   0:00.14 node                                                                                       
  5342 ec2-user  20   0  582440  35132  25992 S   0.0  1.8   0:00.27 node                                                                                       
  5328 ec2-user  20   0  583668  35040  25976 S   0.0  1.8   0:00.23 node                                                                                       
  3855 root      20   0  723844  20528  13324 S   0.0  1.0   0:00.12 ssm-agent-worke                                                                            
  1150 root      20   0   55484  17780  17424 S   0.0  0.9   0:00.22 systemd-journal                                                                            
  3316 root      20   0  714416  13384   8572 S   0.0  0.7   0:00.08 amazon-ssm-agen                                                                            
  3306 root      20   0  257484   9024   7476 S   0.0  0.5   0:00.20 rsyslogd                                                                                   
  4673 root      20   0  148504   8856   7540 S   0.0  0.4   0:00.02 sshd                                                                                       
  4206 root      20   0  148496   8772   7460 S   0.0  0.4   0:00.01 sshd                                                                                       
  3459 root      20   0  110828   7668   6632 S   0.0  0.4   0:00.01 sshd                                                                                       
2  PID  PPID     TIME+  %CPU %MEM  PR  NI S    VIRT    RES   UID COMMAND                                                                                        
 20055  4596   0:00.05   0.4  0.2  20   0 R  168956   4468  1000 top                                                                                            
     1     0   0:01.59   0.2  0.3  20   0 S   41608   5320     0 systemd                                                                                        
  4594     1   0:00.12   0.2  0.1  20   0 S  126132   2816  1000 tmux                                                                                           
     2     0   0:00.00   0.0  0.0  20   0 S       0      0     0 kthreadd                                                                                       
     4     2   0:00.00   0.0  0.0   0 -20 I       0      0     0 kworker/0:0H                                                                                   
     6     2   0:00.01   0.0  0.0   0 -20 I       0      0     0 kworker/0:1H                                                                                   
     7     2   0:00.00   0.0  0.0   0 -20 I       0      0     0 mm_percpu_wq                                                                                   
     8     2   0:00.07   0.0  0.0  20   0 S       0      0     0 ksoftirqd/0                                                                                    
     9     2   0:00.31   0.0  0.0  20   0 I       0      0     0 rcu_sched                                                                                      
    10     2   0:00.00   0.0  0.0  20   0 I       0      0     0 rcu_bh                                                                                         
    11     2   0:00.01   0.0  0.0  rt   0 S       0      0     0 migration/0                                                                                    
    12     2   0:00.00   0.0  0.0  rt   0 S       0      0     0 watchdog/0                                                                                     
    13     2   0:00.00   0.0  0.0  20   0 S       0      0     0 cpuhp/0                                                                                        
    14     2   0:00.00   0.0  0.0  20   0 S       0      0     0 cpuhp/1                                                                                        
3  PID %MEM    VIRT    RES   CODE    DATA    SHR nMaj nDRT  %CPU COMMAND                                                                                        
  4995  7.0  997004 140928  39632  203412  32220   22    0   0.0 node                                                                                           
  3541  3.7 1370424  75024  55612  179852  49344  374    0   0.0 dockerd                                                                                        
  4255  3.2  935204  64160  39632  136088  31148  305    0   0.0 node                                                                                           
  3121  2.0 1369496  39924  33360  132200  28280  274    0   0.0 containerd                                                                                     
  4961  1.8  684764  35556  39632  101144  28636    0    0   0.0 node                                                                                           
  5342  1.8  582440  35132  39632   62180  25992    0    0   0.0 node                                                                                           
  5328  1.8  583668  35040  39632   62384  25976    0    0   0.0 node                                                                                           
  3855  1.0  723844  20528   8516   47320  13324  129    0   0.0 ssm-agent-worke                                                                                
  1150  0.9   55484  17780    304     580  17424  127    0   0.0 systemd-journal                                                                                
  3316  0.7  714416  13384   4960   46736   8572   67    0   0.0 amazon-ssm-agen                                                                                
  3306  0.5  257484   9024    584   22056   7476   22    0   0.0 rsyslogd                                                                                       
  4673  0.4  148504   8856    780    1288   7540    0    0   0.0 sshd                                                                                           
  4206  0.4  148496   8772    780    1280   7460    0    0   0.0 sshd                                                                                           
  3459  0.4  110828   7668    780    1040   6632    8    0   0.0 sshd                                                                                           
4  PID  PPID   UID USER     RUSER    TTY          TIME+  %CPU %MEM S COMMAND                                                                                    
  2385     1    32 rpc      rpc      ?          0:00.01   0.0  0.2 S rpcbind                                                                                    
     1     0     0 root     root     ?          0:01.59   0.2  0.3 S systemd                                                                                    
     2     0     0 root     root     ?          0:00.00   0.0  0.0 S kthreadd                                                                                   
     4     2     0 root     root     ?          0:00.00   0.0  0.0 I kworker/0:0H                                                                               
     6     2     0 root     root     ?          0:00.01   0.0  0.0 I kworker/0:1H                                                                               
     7     2     0 root     root     ?          0:00.00   0.0  0.0 I mm_percpu_wq                                                                               
     8     2     0 root     root     ?          0:00.07   0.0  0.0 S ksoftirqd/0                                                                                
     9     2     0 root     root     ?          0:00.31   0.0  0.0 I rcu_sched                                                                                  
    10     2     0 root     root     ?          0:00.00   0.0  0.0 I rcu_bh                                                                                     
    11     2     0 root     root     ?          0:00.01   0.0  0.0 S migration/0                                                                                
    12     2     0 root     root     ?          0:00.00   0.0  0.0 S watchdog/0                                                                                 
    13     2     0 root     root     ?          0:00.00   0.0  0.0 S cpuhp/0                                                                                    
    14     2     0 root     root     ?          0:00.00   0.0  0.0 S cpuhp/1                                                                                    
    15     2     0 root     root     ?          0:00.00   0.0  0.0 S watchdog/1                                                                                 
    16     2     0 root     root     ?          0:00.11   0.0  0.0 S migration/1                                                                                
akym03akym03

シングルウィンドウで表示を行っているのは下の部分。

      scrlins = window_show(w, Max_lines);

window_show 関数 に渡している w変数 は

   WIN_t *w = Curwin;             // avoid gcc bloat with a local copy

って書かれている。procs_refresh 関数で取得できたプロセスの情報は グローバルな Winstk 変数に渡している。違いは何?と思い、宣言部分を見てみる。

        /* Our four WIN_t's, and which of those is considered the 'current'
           window (ie. which window is associated with any summ info displayed
           and to which window commands are directed) */
static WIN_t  Winstk [GROUPSMAX];
static WIN_t *Curwin;

雰囲気だけど、 CurwinWinstk の先頭?表示したいグループ?を指していそう

akym03akym03

if (CHKw(q, Show_FOREST))Show_FOREST が気になったので確認すると、 プロセスをツリー表示する V キーのフラグだった。何かの時に使えそう。

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                     
    1 root      20   0   41608   5344   3832 S   0.0  0.3   0:01.77 systemd                                                                                     
 1150 root      20   0   55484  18760  18404 S   0.0  0.9   0:00.30  `- systemd-journal                                                                         
 1390 root      20   0  116744   2148   1888 S   0.0  0.1   0:00.00  `- lvmetad                                                                                 
 1803 root      20   0   46016   3832   3004 S   0.0  0.2   0:00.17  `- systemd-udevd                                                                           
 2289 root      16  -4   59736   2060   1652 S   0.0  0.1   0:00.03  `- auditd                                                                                  
 2367 dbus      20   0   58272   4096   3636 S   0.0  0.2   0:00.41  `- dbus-daemon                                                                             
 2385 rpc       20   0   67268   3292   2756 S   0.0  0.2   0:00.01  `- rpcbind                                                                                 
 2417 libstor+  20   0   12620   1848   1680 S   0.0  0.1   0:00.01  `- lsmd                                                                                    
 2419 root      20   0   28468   2996   2652 S   0.0  0.1   0:00.21  `- systemd-logind                                                                          
 2434 root      20   0  101848   1664   1496 S   0.0  0.1   0:00.08  `- irqbalance                                                                              
 2575 rngd      20   0   94076   4592   3740 S   0.0  0.2   0:00.00  `- rngd                                                                                    
 2636 chrony    20   0  120316   3636   3136 S   0.0  0.2   0:00.06  `- chronyd                                                                                 
 2649 root      20   0  211992   3260   2784 S   0.0  0.2   0:00.00  `- gssproxy                                                                                
 2989 root      20   0   98660   4508   2456 S   0.0  0.2   0:00.00  `- dhclient                                                                                
 3034 root      20   0   98660   4008   1988 S   0.0  0.2   0:00.00  `- dhclient                                                                                
 3121 root      20   0 1369496  39924  28280 S   0.0  2.0   0:01.83  `- containerd                                                                              
 3244 root      20   0   81752   4340   3480 S   0.0  0.2   0:00.01  `- master                                                                                  
 3252 postfix   20   0   81856   5720   4860 S   0.0  0.3   0:00.01      `- pickup                                                                              
 3253 postfix   20   0   82036   5780   4888 S   0.0  0.3   0:00.00      `- qmgr                                                                                
 3306 root      20   0  257484   9024   7476 S   0.0  0.5   0:00.32  `- rsyslogd                                                                                
 3316 root      20   0  714416  13256   8572 S   0.0  0.7   0:00.16  `- amazon-ssm-agen                                                                         
 3855 root      20   0  723844  20588  13388 S   0.0  1.0   0:00.17      `- ssm-agent-worke                                                                     
 3459 root      20   0  110828   7668   6632 S   0.0  0.4   0:00.01  `- sshd                                                                                    
 4206 root      20   0  148496   8772   7460 S   0.0  0.4   0:00.01      `- sshd                                                                                
 4253 ec2-user  20   0  149412   5264   3044 S   0.0  0.3   0:00.44          `- sshd                                                                            
 4254 ec2-user  20   0    9424   2324   2120 S   0.0  0.1   0:00.00              `- bash                                                                        
 4255 ec2-user  20   0  937252  66520  31148 S   0.0  3.3   0:07.44                  `- node                                                                    
 4586 ec2-user  20   0  119488   2520   2324 S   0.0  0.1   0:00.00                      `- tmux                                                                
 4995 ec2-user  20   0  997004 141036  32220 S   0.0  7.0   0:03.65                      `- node                                                                
 5328 ec2-user  20   0  583668  38568  28392 S   0.0  1.9   0:00.31                          `- node                                                            
 5342 ec2-user  20   0  582440  38396  28408 S   0.0  1.9   0:00.35                          `- node                                                            
akym03akym03

やっと、プロセスごとの表示にたどり着いた。

      while (i < Frame_maxtask && lwin < wmax) {
         if (*task_show(q, i++))
            ++lwin;
      }
  • q 変数の型は WIN_t なので、表示されるプロセスの情報
  • lwin 変数は表示されたプロセスの数
  • wmax 変数は表示できるプロセス数の上限

task_show関数には

Build the information for a single task row and display the results or return them to the caller.

とあるので、これでよいはず。

akym03akym03

task_show 関数は %CPU%MEM などの表示する列を単位に switch case が書かれていた。
%CPU の部分 はこうなっていた。

         case EU_CPU:
         {  float u = (float)p->pcpu;
#ifndef TREE_VCPUOFF
 #ifndef TREE_VWINALL
            if (q == Curwin)   // note: the following is NOT indented
 #endif
            if (CHKw(q, Show_FOREST)) u += Hide_cpu[idx];
            u *= Frame_etscale;
            if (p->pad_2 != 'x' && u > 100.0 * p->nlwp) u = 100.0 * p->nlwp;
#else
            u *= Frame_etscale;
            /* process can't use more %cpu than number of threads it has
             ( thanks Jaromir Capik <jcapik@redhat.com> ) */
            if (u > 100.0 * p->nlwp) u = 100.0 * p->nlwp;
#endif
            if (u > Cpu_pmax) u = Cpu_pmax;
            cp = scale_pcnt(u, W, Jn);
         }
            break;

CPU使用率の元になる値は proc_tpcpu だった。 /proc/[pid]/state で得られるのはCPUが割り当てられた時間なので、割合ではない。スルーした部分で pcpu を求めていそう。

(14) utime %lu
このプロセスがユーザーモードでスケジューリングされた時間の合計。 clock tick 単位で計測される (sysconf(_SC_CLK_TCK) で割った値が表示される)。 この値にはゲスト時間 guest_time (仮想 CPU の実行に消費された時間) も含まれる。これは、ゲスト時間のフィールドを認識しないアプリケーションにおいて、ゲスト時間分を計算に入れ損ねないようにするためである。
(15) stime %lu
プロセスのカーネルモードでの実行時間 (単位 jiffies)。 このプロセスがカーネルモードでスケジューリングされた時間の合計。 clock tick 単位で計測される (sysconf(_SC_CLK_TCK) で割った値が表示される)。

http://linuxjm.osdn.jp/html/LDP_man-pages/man5/proc.5.html より。

akym03akym03

proc_tpcpu を求めている場所のあたりをつけるとしたら、プロセスの情報を取得するprocs_refresh 関数で readproc 関数を実装した後に実行している procs_hlp 関数。 コメントにそう書いてある。それに加えて、プロセス情報を読み込むループの前にも実行している。

   procs_hlp(NULL);                              // prep for a new frame
   if (NULL == (PT = openproc(Frames_libflags, Monpids)))
      error_exit(fmtmk(N_fmt(FAIL_openlib_fmt), strerror(errno)));
   read_something = Thread_mode ? readeither : readproc;

   for (;;) {
      if (n_used == n_alloc) {
         /* we're subject to integer overflow if total linux tasks ever approach |
            400+ million (but, do you think memory might be the bigger problem?) | */
         n_alloc = 10 + ((n_alloc * 5) / 4);     // grow by over 25%
         private_ppt = alloc_r(private_ppt, sizeof(proc_t *) * n_alloc);
         // ensure NULL pointers for the additional memory just acquired
         memset(private_ppt + n_used, 0, sizeof(proc_t *) * (n_alloc - n_used));
      }
      // on the way to n_alloc, the library will allocate the underlying
      // proc_t storage whenever our private_ppt[] pointer is NULL...
      if (!(ptask = read_something(PT, private_ppt[n_used]))) break;
      procs_hlp((private_ppt[n_used] = ptask));  // tally this proc_t
   }
akym03akym03

なので、 procs_hlp を見ていく。

static void procs_hlp (proc_t *this) {
#ifdef OFF_HST_HASH
   static unsigned maxt_sav = 0;        // prior frame's max tasks
#endif
   TIC_t tics;
   HST_t *h;

   if (!this) {
      static double uptime_sav;
      double uptime_cur;
      float et;
      void *v;

      uptime(&uptime_cur, NULL);
      et = uptime_cur - uptime_sav;
      if (et < 0.01) et = 0.005;
      uptime_sav = uptime_cur;

      // if in Solaris mode, adjust our scaling for all cpus
      Frame_etscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : smp_num_cpus));
#ifdef OFF_HST_HASH
      maxt_sav = Frame_maxtask;
#endif
      Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0;

      // prep for saving this frame's HST_t's (and reuse mem each time around)
      v = PHist_sav;
      PHist_sav = PHist_new;
      PHist_new = v;
#ifdef OFF_HST_HASH
      // prep for binary search by sorting the last frame's HST_t's
      qsort(PHist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t);
#else
      v = PHash_sav;
      PHash_sav = PHash_new;
      PHash_new = v;
      memcpy(PHash_new, HHash_nul, sizeof(HHash_nul));
#endif
      return;
   }

   switch (this->state) {
      case 'R':
         Frame_running++;
         break;
      case 't':     // 't' (tracing stop)
      case 'T':
         Frame_stopped++;
         break;
      case 'Z':
         Frame_zombied++;
         break;
      default:
         /* currently: 'D' (disk sleep),
                       'I' (idle),
                       'P' (parked),
                       'S' (sleeping),
                       'X' (dead - actually 'dying' & probably never seen)
         */
         Frame_sleepin++;
         break;
   }

   if (Frame_maxtask+1 >= HHist_siz) {
      /* we're subject to integer overflow if total linux tasks ever approach |
         400+ million (but, do you think memory might be the bigger problem?) | */
      HHist_siz = HHist_siz * 5 / 4 + 100;
      PHist_sav = alloc_r(PHist_sav, sizeof(HST_t) * HHist_siz);
      PHist_new = alloc_r(PHist_new, sizeof(HST_t) * HHist_siz);
   }

   /* calculate time in this process; the sum of user time (utime) and
      system time (stime) -- but PLEASE dont waste time and effort on
      calcs and saves that go unused, like the old top! */
   PHist_new[Frame_maxtask].pid  = this->tid;
   PHist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
   // finally, save major/minor fault counts in case the deltas are displayable
   PHist_new[Frame_maxtask].maj = this->maj_flt;
   PHist_new[Frame_maxtask].min = this->min_flt;

#ifdef OFF_HST_HASH
   // find matching entry from previous frame and make stuff elapsed
   if ((h = hstbsrch(PHist_sav, maxt_sav - 1, this->tid))) {
      tics -= h->tics;
      this->maj_delta = this->maj_flt - h->maj;
      this->min_delta = this->min_flt - h->min;
   }
#else
   // hash & save for the next frame
   hstput(Frame_maxtask);
   // find matching entry from previous frame and make stuff elapsed
   if ((h = hstget(this->tid))) {
      tics -= h->tics;
      this->maj_delta = this->maj_flt - h->maj;
      this->min_delta = this->min_flt - h->min;
   }
#endif

   /* we're just saving elapsed tics, to be converted into %cpu if
      this task wins it's displayable screen row lottery... */
   this->pcpu = tics;

   // shout this to the world with the final call (or us the next time in)
   Frame_maxtask++;
} // end: procs_hlp
akym03akym03

procs_hlp 関数は 大きく2つに分かれていて

前処理として、 procs_hlp(NULL); で呼び出された場合と、 集計処理として procs_hlp((private_ppt[n_used] = ptask)); で呼び出される場合がある。

前処理は if (!this) { の部分。集計処理は switch (this->state) { 以降の部分。

akym03akym03

前処理のうち、CPU使用率に関係がありそうなのは、下の2つ。

  1. Frame_etscale は topコマンドの計測間隔は -d オプション でされる。前回の計測から今回の計測までの経過時間( et 変数に入っている) を調べ、その経過時間内に発生しうる tick 数。
  2. PHist_savPHist_new の入れ替え、および、 PHash_savPHash_new の入れ替え。

Frame_etscale は使われる時に書く予定。

PHist_xxx は プロセスの履歴を記録している HST_t 構造体の動的な配列。

typedef struct HST_t {
   TIC_t tics;                  // last frame's tics count
   unsigned long maj, min;      // last frame's maj/min_flt counts
   int pid;                     // record 'key'
   int lnk;                     // next on hash chain
} HST_t;

CPU利用率でいえば必要なのは ティック数を管理している ticsmajminはページフォルトの メジャーとマイナーだった。これらを pid に紐づけて管理している。

_sav と _new の2つあるのは、履歴といっても前回の値だけで足りるので、前回分と今回分の2つだけがある。メモリ管理としては、前回と今回が交互に入れ替わっている。

もう一つの、PHash_xxx は 前回の値を知るための情報。ハッシュマップになっていて キーは pid、値は PHash_sav の n番目。