Linux Kernel: TASK_IDLE を調べる

本エントリの要約

Linux kenrel v4.14-rc3 から /proc/$pid/status/proc/$pid/stat /proc/sched_debug で表示されるタスクの状態に I (idle) が追加されている

これに対応するカーネル内の定数は TASK_IDLE (注: 定数自体は v4.2-rc1 に追加された )

#define TASK_IDLE           (TASK_UNINTERRUPTIBLE | TASK_NOLOAD)
  • TASK_IDLE は TASK_UNINTERRUPTIBLE の状態を取りつつも、 TASK_NOLOAD = ロードアベレージには寄与しない状態のタスクを示す
  • TASK_IDLE は カーネルスレッドのみが取りうる状態のようだ ( kthreadd から生えた workqueue のスレッドや oom_reaper 等 )

ps や top で見るとカーネルスレッドの一部が I と表示されるのが確認できる

f:id:hiboma:20171101163029p:plain

I のタスクを探してね

f:id:hiboma:20171101163035p:plain

以降、調べた経緯を記す。興味ない人はすっ飛ばしてね

イントロダクション

kernel 4.14-rc7 を触っていたところ、 ps auxf で出力される STAT 欄に I で標示されるタスクがあるのに気がついた

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         2  0.0  0.0      0     0 ?        S    10月30   0:00 [kthreadd]
root         4  0.0  0.0      0     0 ?        I< 👈  10月30   0:00  \_ [kworker/0:0H]
root         6  0.0  0.0      0     0 ?        I< 👈 10月30   0:00  \_ [mm_percpu_wq]
root         7  0.0  0.0      0     0 ?        S    10月30   0:00  \_ [ksoftirqd/0]
root         8  0.0  0.0      0     0 ?        R    10月30   0:08  \_ [rcu_sched]
root         9  0.0  0.0      0     0 ?        I  👈 10月30   0:00  \_ [rcu_bh]
root        10  0.0  0.0      0     0 ?        S    10月30   0:00  \_ [migration/0]
root        11  0.0  0.0      0     0 ?        S    10月30   0:00  \_ [watchdog/0]

...

man で調べる

man 1 ps http://man7.org/linux/man-pages/man1/ps.1.html には載っていない

PROCESS STATE CODES         top

       Here are the different values that the s, stat and state output
       specifiers (header "STAT" or "S") will display to describe the state
       of a process:

               D    uninterruptible sleep (usually IO)
               R    running or runnable (on run queue)
               S    interruptible sleep (waiting for an event to complete)
               T    stopped by job control signal
               t    stopped by debugger during the tracing
               W    paging (not valid since the 2.6.xx kernel)
               X    dead (should never be seen)
               Z    defunct ("zombie") process, terminated but not reaped by
                    its parent

       For BSD formats and when the stat keyword is used, additional
       characters may be displayed:

               <    high-priority (not nice to other users)
               N    low-priority (nice to other users)
               L    has pages locked into memory (for real-time and custom
                    IO)
               s    is a session leader
               l    is multi-threaded (using CLONE_THREAD, like NPTL
                    pthreads do)
               +    is in the foreground process group

man 1 top http://man7.org/linux/man-pages/man1/top.1.html も同様で載っていない

       29. S  --  Process Status
           The status of the task which can be one of:
               D = uninterruptible sleep
               R = running
               S = sleeping
               T = stopped by job control signal
               t = stopped by debugger during trace
               Z = zombie

最近追加されたものだと推測できる。もしくは man 作ってる人達が見落としているってのもあるだろう (よくあるみたいだよ)

man の upstream を調べる

man 1 ps man 1 top の upstream は procps-ng だが、ここをみても (まだ) 説明は無かった

gitlab.com

gitlab.com

📝 rc 無しバージョンのカーネルがリリースされたら issue をあげたらいいかな?

strace で調べる

ps は /proc のデータを整形して出力するなのを経験的に知っているので、strace を取ってアタリをつけた

$ strace -yy -s100 -- ps ax -ostat 

...

stat("/proc/4", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/4/stat", O_RDONLY)          = 6</proc/4/stat>
read(6</proc/4/stat>, "4 (kworker/0:0H) I 👈2 0 0 0 -1 69238880 0 0 0 0 0 0 0 0 0 -20 1 0 0 0 0 18446744073709551615 0 0 0 0 "..., 2048) = 
156
close(6</proc/4/stat>)                  = 0
open("/proc/4/status", O_RDONLY)        = 6</proc/4/status>
read(6</proc/4/status>, "Name:\tkworker/0:0H\nUmask:\t0000\nState:\tI (idle)\nTgid:\t4\nNgid:\t0\nPid:\t4\nPPid:\t2\nTracerPid:\t0\nUid:\t0\t0\t"..., 2048) = 893
close(6</proc/4/status>)                = 0
write(1</dev/pts/0>, "I<\n", 3I< 👈
)         = 3

/proc/$pid/stat もしくは /proc/$pid/status から取り出した文字列なのが分かる

[vagrant@localhost ~]$ cat /proc/4/stat
4 (kworker/0:0H) I 👈 2 0 0 0 -1 69238880 0 0 0 0 0 0 0 0 0 -20 1 0 0 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0

カーネルのソースを探す

The Linux Kernel Archives から最新のソースコードを取ってこよう。git blame で追いかけるならリポジトリを clone しておく方がよいか

github.com

/proc/$pid/stat や /proc/$pid/status のソースコードfs/proc/*.c 以下に転がっている。適当に grep をかけて回って ITASK_IDLE の省略形であることを調べだした。以下のコミットを探し出す

 $ grep idle fs/proc/* | head -1
fs/proc/array.c:    "I (idle)",     /* 0x80 */

ここをエントリポイントとして git blame でコミットを調べだして過去を遡っていく

関連のコミット

TASK_IDLE は下記のコミットで /proc/$pid/status /proc/$pid/stat に表示されるよになった ( v4.14-rc3 )

github.com

TASK_IDLE は下記のコミットで追加された ( v4.2-rc1 )

github.com

どういう用途なのかはコミットメッセージを読むと分かる

Currently people use TASK_INTERRUPTIBLE to idle kthreads and wait for 
'work' because TASK_UNINTERRUPTIBLE contributes to the loadavg. Having 
all idle kthreads contribute to the loadavg is somewhat silly. 

Now mostly this works OK, because kthreads have all their signals 
masked. However there's a few sites where this is causing problems and 
TASK_UNINTERRUPTIBLE should be used, except for that loadavg issue. 

This patch adds TASK_NOLOAD which, when combined with 
TASK_UNINTERRUPTIBLE avoids the loadavg accounting. 

As most of imagined usage sites are loops where a thread wants to 
idle, waiting for work, a helper TASK_IDLE is introduced. 

なるほどなー

感想

  • 新しいステートが増えることで ps/proc/pid/status , /proc/pid/stat をパースしてごそごそやっているツールがあれば副作用がでるだろうか?
    • とりあえずカーネルスレッドだけなので あまり影響はなさそうだが
  • 些細な変更にみえるが、ユーザランドで確認できるステートが増えるというのは滅多にないことだよな
  • TASK_UNINTERRUPTIBLE + TASKNO_LOAD にするのは興味深いが、そもそもロードアベレージの計算が TASK_RUNNING と TASK_UNINTERRUPTIBLE とで混ざっているのが厄介だよなぁ。Brendan Gregg は何と言ってたかな

www.brendangregg.com