systemdとcgroup
systemdではcgroupを活用しているようだ。サービスごとにcgroupでアイソレーションされているので、関係するプロセスを一網打尽で殺せるし、サービスごとの資源利用量を観測できる。確かにpidをファイルに記録しておいて、殺すってのはダサいよね。流れとしてはユニットファイルでExecStopで指定されているコマンドを実行し、それでも当該グループのプロセスが残っている場合は、SIGTERM、SIGKILLの順でシグナルを投げるそうな。詳細はレッドハット中井さんの「Linux女子部 systemd徹底入門」とか。
次はsystemd-cgtopの実行例。
Path Tasks %CPU Memory Input/s Output/s / 86 0.3 197.3M - - /system.slice/NetworkManager.service 2 - - - - /system.slice/auditd.service 1 - - - - /system.slice/avahi-daemon.service 2 - - - - /system.slice/crond.service 1 - - - - /system.slice/dbus.service 1 - - - - /system.slice/firewalld.service 1 - - - - /system.slice/iprdump.service 1 - - - - /system.slice/iprinit.service 1 - - - - /system.slice/iprupdate.service 1 - - - - /system.slice/lvm2-lvmetad.service 1 - - - - /system.slice/polkit.service 1 - - - - /system.slice/postfix.service 3 - - - - /system.slice/rsyslog.service 1 - - - - /system.slice/sshd.service 1 - - - - /system.slice/system-getty.slice/getty@tty1.service 1 - - - - /system.slice/systemd-journald.service 1 - - - - /system.slice/systemd-logind.service 1 - - - - /system.slice/systemd-udevd.service 1 - - - - /system.slice/tuned.service 1 - - - - /system.slice/vboxadd-service.service 1 - - - - /user.slice/user-1000.slice/session-4.scope 4 - - - -
大きく分けるとsystem.sliceとuser.sliceってのがある。/sys/fs/cgroup/systemdがルートね。
systemd-cglsでcgroupの使われ方を見ることができる。
[vagrant@localhost ~]$ systemd-cgls ├─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 23 ├─user.slice │ └─user-1000.slice │ └─session-4.scope │ ├─2407 sshd: vagrant [priv] │ ├─2410 sshd: vagrant@pts/0 │ ├─2411 -bash │ ├─2471 systemd-cgls │ └─2472 systemd-cgls └─system.slice ├─polkit.service │ └─747 /usr/lib/polkit-1/polkitd --no-debug ├─auditd.service │ └─501 /sbin/auditd -n ├─systemd-udevd.service │ └─455 /usr/lib/systemd/systemd-udevd ├─lvm2-lvmetad.service │ └─440 /usr/sbin/lvmetad -f ├─systemd-journald.service │ └─426 /usr/lib/systemd/systemd-journald ├─dbus.service │ └─543 /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation ├─systemd-logind.service │ └─542 /usr/lib/systemd/systemd-logind ├─sshd.service │ └─1109 /usr/sbin/sshd -D :
冒頭で触れた挙動を確認するため、ちょっとソースコードを眺める。systemd本体はsrc/core以下なのかな。サービスユニットの上げ下げはsrc/core/service.cとかsrc/core/unit.cあたりかな。確かにSysVinitでシェルスクリプトで実装されていた分が全部Cで書かれているわけね。たしかにこれはビッグチェンジだなぁ。
service_stop()がsystemctl stopで呼ばれる関数。ユニットごとに状態遷移マシンになっていて、service_enter_stop()でExecStop、service_enter_stop_post()でExecStopPostに登録されたコマンドを順に実行する。コマンドの実行に失敗した場合は、service_enter_signal()を経由してunit_kill_context()でプロセスにSIGTERMを投げる。それでも死なないプロセスはwatchdogで回収され、同様にunit_kill_context()でSIGKILLを投げる。
unit_kill_context()が参照するKillContextにはkill_modeというメンバ変数があり、ユニットはKILL_CONTROL_GROUPに初期化されている(SysVinit互換のケースはKILL_PROCESSになっている)。unit_kill_context()ではcg_kill_recursive()を呼んで、cgroupのプロセスにシグナルを投げる。
cg_*関数の定義はsrc/shared/cgroup-util.c。cgroupの実装がファイルシステムというのがよくわかる。