hiroの長い冒険日記

主にコンピュータ周辺の興味を持った内容を綴ります

systemd ... SysVinit との違いと動作について その1

Linux を使い出した頃から環境が大きく変化するのはよくある事で、binary の elf 化 、kernel module の始まり、その度に混乱しつつも適応してきた。今回の init の SysVinit から systemd への移行も、最終的には慣れるしかない事。これまでもそうだった様に、試行錯誤しながら理解する。

参考になったサイト

改めて書く事がない程に、以下のサイトにまとめられていた。
qiita.com
equj65.net
www.slideshare.net
分かりやすくまとめられている文章を読んでも、私は実際に手を動かさないと理解できない。基本的な考え方のまとめと実際に debian 9.6 で試した記録を記す。

SysVinit とは

ja.wikipedia.org

initは、UNIXおよびUnix系システムのプログラムのひとつであり、他の全てのプロセスを起動する役目を持つ。デーモンとして動作し、一般にPID 1 を付与される。ブートローダカーネルを起動し、カーネルがinitを起動する。

init の機能はBSD系とSystem V系では大きく異なるため、ユーザーは自分のシステムがどちらのバージョンを使っているかをマニュアルで調べる必要がある。多くのLinuxディストリビューションで使われていたinitはSystem Vと互換性がある。
...略...
他にもいくつかinitの設計上の限界に対処した代替として、systemdやUpstartがあり、Ubuntu[3][4]や他のLinuxディストリビューションで採用している[5][6]。

とある。元々は SystemV 系の UN*X で使用されていたものと同機能の init が linux で長い間使用されてきた。

/etc/inittab に runlevel が規定されていて、runlevel 毎の /etc/rcN.d フォルダ以下の script file を特定の引数 (start, stop, restart, status, ...) で動作させる事により、必要な初期設定や daemon を起動させていた。起動順、停止順は file name の sort 順となり、alphabet の "S (Start)" か "E (End)" + 数字2桁を file name の先頭に付けた形となっていた。実体は/etc/rc.d 以下にあり、/etc/rcN.d フォルダには symbolic link を置く形になっていた。

Linux でのやり方を覚えておくと他の商用 UN*X でも同様の管理が出来るので、分かりにくい方法ながら覚えた記憶がある。結局、他の商用 UN*X には触れずじまいだったが。Linux 以外の環境に触れる可能性がなければ、今から学ぶ必要はないと思う。

systemd とは

ja.wikipedia.org

systemdは、システム管理デーモン、ライブラリおよびユーティリティの一式であり、管理および設定における中心的プラットフォームとしてLinuxコンピュータオペレーティングシステム用に設計されている。
...略...
UNIX System VやBSDから継承された(Linuxスタートアッププロセス中のユーザー空間で最初に実行されるプロセスである)Linuxのinitシステムを置き換えることを第一の目標としている。

私が linux から暫く離れていた間に、SysVinit から Upstart、そして systemd への移行が進んでいた。現在は systemd が主流の模様。

SysVinit から systemd へ移行するメリットとして、上記3番目のリンク先に簡潔にまとめられていた。
Linux女子部 systemd徹底入門
Slide 10 of 50:

  • システム起動時間の短縮
  • システム構成の動的変更に対応
  • プロセス停止処理を標準機能として提供
  • デーモンの実行環境を制御

他に、Wikipedia には

彼らはシステムのブート中にたくさんの処理を並行または並列に行えるようにし、さらにシェルのオーバーヘッドを減少させるために、依存関係を表現できるようにそのソフトウェアフレームワークを改良したいと思っていた。

とある。

  • 並列化による効率向上、shell のオーバーヘッド減少、この2点の改善が systemd の主目的だろう。確かに SysVinit だと一つ一つの daemon が series で起動するので時間を要していた。
  • システム構成の動的な変更への対応は、USB の普及が主因だろう。udev とも協調して動作している。
  • rc.d script は混沌地帯で、start/stop 以外の引数がバラバラ、詳細は script 内を読んで理解する事も度々で、stop に使用する pid の扱いも個々に違っていた。/etc 以下に script があるという状況も不思議なものだった。
  • 起動、停止も symbolic link の名前の sort 順だったので、稀に起動に失敗する事もあった。

systemd の場合は設定ファイルのみを作成する事になり、依存関係も定義出来るので、ちゃんと理解した上で使用すれば install の際に気にする事が少なくなると思う。また、依存関係をしっかりしておけば、trouble の際の問題切り分けも楽になると予想する。

とはいえ、どの設定ファイルを読み込むかは Symbolic link を使っている。手作業でやろうとは思わないが、仕組みを理解しておく事は重要だと思う。

最初に読む設定ファイル

systemd が最初に読み込む設定ファイルは default.target である。debian 9.6 の場合は以下の通り:

$ find /lib/systemd/ | grep default.target
/lib/systemd/system/default.target

systemd が設定ファイルを読み込む場所は決まっていて、man systemd の DIRECTORY section を見ると pkg-config で調べられる事が分かる。

$ pkg-config systemd --variable=systemdsystemunitdir
/lib/systemd/system
$ pkg-config systemd --variable=systemdsystemconfdir
/etc/systemd/system
$ pkg-config systemd --variable=systemduserunitdir
/usr/lib/systemd/user
$ pkg-config systemd --variable=systemduserconfdir
/etc/systemd/user

通常は、/lib/systemd と /etc/systemd より下の directory を調べれば良い。

読み込む場所の詳細は man systemd.unit の UNIT FILE LOAD PATH section に優先順位付きで記載されている。特別な設定をしない場合には、

  1. /etc/systemd
  2. /lib/systemd

の順で探せば良い。

default.target の選択

では、default.target の実体は何かというと、

$ ls -l /lib/systemd/system/default.target 
lrwxrwxrwx 1 root root 16 Feb 17 17:22 /lib/systemd/system/default.target -> graphical.target

同じ directory の graphical.target への symbolic link となっている。

SysVinit の場合は /etc/inittab で runlevel を指定していたが、systemd では、どの target を default.target へ symbolic link するかで起動する状態が決まる。

特別な事がない限り、default.target は、

  • graphical.target
  • multi-user.target

のどちらか一方が選択されている。GUI が使える環境では graphical.target になっている。組込システムや server 等の GUI 不要な 環境では multi-user.target を選択する。(cf. man systemd.special)

他の target については、最初に挙げたリンク先の2番目のリンクの「ランレベルをUnitとして表現する」に、runlevel と target の対応表が書かれている。

target の切替 (runlevel 変更)

SysVinit では起動してからの runlevel の切替は init コマンドで行う。後方互換の為に init コマンドも使用できるが、同じ事を systemd では systemctl isolate で実施する。

$ sudo systemctl isolate multi-user.target

CLI の login 画面が出る。GUI 画面に戻すには次の通り。

$ sudo systemctl isolate graphical.target

再起動した時の target を切り替える方法:

$ systemctl get-default
graphical.target
$ sudo systemctl set-default multi-user.target
Created symlink /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target.
$ ls -l /etc/systemd/system/default.target 
lrwxrwxrwx 1 root root 37 Mar  8 22:37 /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target

/etc/systemd 以下の方が優先されるので、/lib/systemd 以下の default.target の symbolic link はそのままで、/etc/systemd/system/default.target が作成された。

【寄り道】Linux 仮想 console の使い方

ちなみに、CLI 画面では複数の仮想 console が使用できる(昔はこれを切り替えて使っていた)。
qiita.com
上のリンク先のショートカットは、debian (LXDE?) では違っていた。

  • Alt + F[1-6] で tty[1-6] に切り替わる。
  • GUI 画面で一時的に仮想 console に切り替えるには、Ctrl + Alt + F[1-6]
  • 仮想 console から GUI に戻るには、Alt + F7

間違って仮想 console に切り替えてしまった場合には、慌てず Alt + F7 で戻すことが出来る。

そもそも target って何?

systemd の設定ファイルの単位は unit と呼ばれている。target は unit の集合体。その中でも、graphical.target 等の default.target にリンクされるような target は特殊な存在だが、それ以外にも target のファイルは存在している。
unit にどのような種類が存在するかは別の記事にするとして、graphical.target がどんな unit から構成されているか調べてみた。

$ systemctl list-dependencies graphical.target --no-pager
graphical.target
* |-lightdm.service
* |-rtkit-daemon.service
* |-systemd-update-utmp-runlevel.service
* `-multi-user.target
*   |-binfmt-support.service
*   |-console-setup.service
*   |-cron.service
*   |-dbus.service
*   |-networking.service
*   |-rsync.service
*   |-rsyslog.service
*   |-ssh.service
*   |-systemd-ask-password-wall.path
*   |-systemd-logind.service
*   |-systemd-networkd.service
... 略 ...

multi-user.target に上乗せする形で lightdm.service 等が加わり、graphical.target が出来ている。

結果

  • systemd が最初に読む設定ファイルは default.target
  • 通常は、default.target は graphical.target の symbolic link。CLI の場合は multi-user.target。
  • systemctl isolate が init の代わり。起動した状態で target を変更する(runlevel の変更と等価)。
  • systemctl set-default で起動時の target を変更する(inittab の編集と等価)。実際は synbolic link するだけ。systemctl get-default で現在の target を表示。
  • SysVinit 世代への配慮が様々な所で散見される:-)

試してみると色々と分かってくる。もう少し詳細に調べてみる。