スケジュールポリシー
概要
概要とここで書いたことの途中くらいまでを丁寧に解説した記事を見つけたので紹介がてらぺたぺた...
Linux スケジューラーのコア実装とシステムコール
スケジューラとはなんや?からリアルタイムスケジューラあたりまでの話は
このへんでも見ながら予習しておいてもらった方が、下記の話についていけると思われる。
スケジュールポリシーの種類
Linuxでは、POSIX標準の
- SCHED_FIFO
- SCHED_RR
- SCHED_OTHER
に加えて、下記3つに拡張対応している。
- SCHED_DEADLINE
- SCHED_BATCH
- SCHED_IDLE
SCHED_DEADLINE, SCHED_FIFO, SCHED_RR の3つはいわゆるリアルタイムスケジュール、SCHED_OTHER, SCHED_BATCH, SCHED_IDLEの3つはタイムシェアスケジュールとなる。
スケジューリングポリシーは
sched_setscheduler(2),
pthread_setschedparam(3) で設定する。
priority
リアルタイムスケジュールでは、SCHED_DEADLINEをのぞき、priorityを設定できる。priorityは優先度の高い(数値の小さい)ものからスケジューリングされる。
priorityは
sched_setparam(2),
pthread_setschedprio(3) で設定する。
nice
タイムシェアスケジュールでは、SCHED_IDLEをのぞき、nice値を設定できる。priorityは優先度の高い(数値の小さい)ものに多くCPU時間が割り当たるように(CFSの場合は)スケジューリングされる。
nice値は
setpriority(2) で設定する。Linuxではsetpriority()が拡張されていて、tidを指定するとスレッドのnice値を設定できる。ただ、
gettid(2) は syscall しないといけなかったりするので注意。
テーブルにまとめる
SCHED | priority | nice | sched | ordered |
SCHED_DEADLINE | (none) | (none) | 1st | at first |
SCHED_FIFO | avail | (none) | 2nd | by priority |
SCHED_RR | avail | (none) | 2nd | by priority, but timeout |
SCHED_OTHER | (none) | avail | 3rd | timeshare with nice weight |
SCHED_BATCH | (none) | avail | 3rd | timeshare with nice weight |
SCHED_IDLE | (none) | (none) | 4th | timeshare |
確認方法
# ps alxHww
top して
H でスレッド表示する
など。
/proc/[pid]/statで確認する方法もあるが、statの文字列をパースするのが面倒とかなんとか。
動的に変更する
util-linux の chrt(1) が使える。あまりに古いバージョンだと SCHED_IDLE や SCHED_DEADLINE に対応していないので注意。busy box の chrt は機能が限定される。
- # chrt -a -p 29624 (PID が 29624 のプロセスの全スレッドをチェックする)
- # chrt -f -p 29624 (TID が 29624 のスレッドを SCHED_FIFO にする)
- # chrt -p 10 -p 29624 (TID が 29624 のスレッドの priority を 10 にする)
util-linux の renice が使える。
- # renice -n +10 -p 29624 (TID が 29624 のスレッドの nice値を +10 する)
nice値の操作は相対的なので注意。
Capability
POSIX的には、「上げる」方向の変更にはスーパユーザ権限が必要になる。「下げる」方向には特に権限は必要ない。
Linuxでは、スーパユーザ権限以外に、CAP_SYS_NICE の Capability の権限でも同様のことが行える。Capability のチェックは libcap の getpcaps で行える。
- # getpcaps 29624 (PID が 29624 のプロセスの Capability を表示する)
CFS (Completely Fair Scheduler)
概要
Linux-2.6.23 (9th October, 2007)から、CFS(Completely Fair Scheduler)というスケジューラが入っており、これが現在までのデファクトとなっている。
- すべてのタスクはCPU時間に対してフェアに扱われる。
- nice値が1異なると、1.25倍(0.8倍)のCPU時間割り当てとなる。
- session IDが同じプロセスの単位で CPU時間の合計が計算される。つまりsession ID大事!
- 実行可能なタスクの数が多いと、1タスク当たりのCPU時間は短くなる。つまり実行可能なタスクの数が大事!
パラメータ
/proc/sys/kernel/sched/* にいくつかのパラメータが存在する。レイテンシにも関わるため要注意。
- sched_autogroup_enabled (CPU時間を session ID でグループ化するかどうか)
- sched_latency_ns (リスケジュールされるまでのレイテンシの目標値。ただし実行可能なタスクが増えるとこれよりも伸びてしまう。)
- sched_min_granularity_ns (各タスクが最低限獲得できるCPU時間)
- sched_rr_timeslice_ms (SCHED_RRでスケジューリングされるタスクのタイムアウト時間。この時間だけ走り続けると実行可能になり後回しにされる。)
- sched_rt_runtime_us (SCHED_FIFO, SCHED_RR, SCHED_DEADLINE のリアルタイムスケジュールのタスクが占有できる最大時間。(sched_rt_runtime_us/sched_rt_period_us))
cgroups
cgroupsを設定することにより、cgroups単位で CPU, memory, network, filesystem namespace などの制限を掛けることができる。今興味があるのはスケジューラなので、特にCPU回りについて書くと、
- # mount -t tmpfs cgroup /sys/fs/cgroup
- # mkdir /sys/fs/cgroup/cpu
- # mount -t cgroup -o cpu cgroup /sys/fs/cgroup/cpu
- # cd /sys/fs/cgroup/cpu
- # mkdir test01 ('test01'というcgroupを新規作成)
- # cd test01
- # echo 500000 > cpu.rt_runtime_us (1000000がデフォルトなので、これで50%のCPU時間割り当てとなるようなcgroupを作った)
- # echo 208 > tasks (TID 208 のスレッドを 'test01'というcgroupに加入させた。これで、リアルタイムスケジュールのTID 208のタスクも最大50%しか動けなくなる。)
他にも cgroups に対応した CFS のパラメータがあるため、柔軟に CPU時間の制限を記述できる。