Cent7での起動プロセスの違いについてまとめてみた

# はじめに
前回[CentOS6からの変更 ブートローダGrubについて](https://qiita.com/hot_study_man/items/c7e0951d51149f9f76a4)にてCentOS7での変更点であるブートローダGrub2についてまとめてみました。今回はCentOS7での起動プロセスの違いについてまとめてみた。ここでいう起動プロセスとは、電源ONしてからログイン画面が表示されるまでのことです。

## CentOSでの起動プロセス方式の違い
## CentOS5まで
SysVinit

## CentOS6
Upstart

## CentOS7
Systemd

# SysVinit、Upstart、Systemdについて
**SysVinit**、**Upstart**、**Systemd**についてそれぞれの特徴をまとめてみました。

## SysVinitのサービス起動方法
/etc/inittabに従ってプロセス起動。/etc/inittabの各行を上から順番に実行する。**ただこれだけ**。並列実行はできずにCentOS6やCentOS7の起動方式より遅い。


### SysVinitディレクトリ
SysVinitについて調べてみるとSysVinit起動方式に関連するスクリプトやディレクトリは多数存在していました。**/etc/rc.d/以下**が本体で、/etc/rc.d/以下へ対して様々な場所からシンボリックリンクで参照されていました。

* /etc/init.d/ -> /etc/rc.d/init.d         **(/etc/rc.d/以下へのリンク)**
  - httpd,sshd など各シェルスクリプト
* /etc/rc.[0-6].d/ -> /etc/rc.d/rc[0-6].d　**(/etc/rc.d/以下へのリンク)**
* /etc/rc.sysinit -> /etc/rc.d/rc.sysinit  **(/etc/rc.d/以下へのリンク)**
* /etc/rc -> /etc/rc.d/rc                  **(/etc/rc.d/以下へのリンク)**
* /etc/rc.local -> /etc/rc.d/rc.local     **(/etc/rc.d/以下へのリンク)**


### /etc/rc.d/の本体
* /etc/rc.d/ **(上記は全部/etc/rc.d/以下の下記スクリプトへのリンク)**
  - init.d 起動スクリプトの本体がある
  - rc 　　　　 起動スクリプト
  - rc0-6.d　　 各runlevelのスクリプトは/etc/rc.d/init.dみてる
  - rc.local　　全てのrunlevelの共通処理
  - rc.sysinit 下記参照

#### /etc/rc.d/rc.sysinit
/etc/rc.sysinit はシステム起動時にすべてのランレベルで一度だけ実行されるスクリプトで、環境変数 PATH の初期設定、ネットワークの初期化、ホスト名の設定、システムクロックの設定、keymap の読み込み、システムフォントの読み込みなど、システムの基本的な環境設定を行います。また、/proc ファイルシステムのマウント、スワップの有効化、ファイルシステムのチェックとマウントなど、ファイルシステムに関する初期化処理等も行います。さらに、クォータ、RAID 、LVM の設定等が行われている場合には、これらの機能も有効化します。

#### /etc/rc.d/init.d/
* **各サービスのスクリプト本体**。/etc/rc[0-6].d/以下の各ランレベルに配置されたスクリプトは/etc/rc.d/init.d/以下の本体へのシンボリックリンク


### /etc/rc.d/rc
* 起動時のランレベルに応じたサービスを起動

### サービス起動・停止コマンド
* serviceコマンド

```sh

$ which service
/sbin/service
```

/sbin/serviceの中身抜粋

```sh
SERVICEDIR="/etc/init.d"
```

* /etc/init.d以下の各サービスを実行。上記で見たように`/etc/init.d -> /etc/rc.d/init.d `なのでつまりは`/etc/rc.d/init.d`以下の各シェルスクリプトが実行される

### SysVinitまとめ
* **/etc/init/inittabの上から順次実行していく**
* /etc/rc.d/以下へリンクがたくさん貼られている


# Upstart
* イベントベースのジョブ管理システム
* **並列**で起動プロセスを実行できる
* upstatでも/etc/rc.d/rc1-6.d/各シェルスクリプト -> /etc/rc.d/init.d/各シェルスクリプトのランレベルのシェルスクリプトが実行される。

### initctl
initctlでジョブを管理する

* サービス起動。

```
initctl start ジョブ名
```

* イベント発生

```
initctl emit "任意のイベント名"
```


### /etc/inittab

```
id:3:initdefault:
```

これだけの記載。ランレベルを参照されるだけのものになった。


## ジョブ定義ファイル
* `/etc/init/定義ファイル`。/etc/init以下に用意された.confがジョブと呼ばれる。ジョブ同士でイベントを送り/受け て起動や停止の処理をする
* **今まで/etc/inittabに記載されていた内容が/etc/init/以下の個別のジョブ定義ファイルに分割**。**各ジョブは並列に実行される**


# Upstart起動シーケンス
ランレベル3(CUI)の場合

* **/etc/init/rcS.confが一番最初に起動するジョブ定義でUpstartのエントリポイント**
* /etc/init/rcS.confの中で`exec telinit $runlevel`を実行
  - runlevelというイベントを発動。ここでは`runlevel 3` イベント
  - `exec teilinit 3`だと start up runlevel [012345]のイベントが []に3と書かれているので /etc/init/各ジョブ定義でstart up runlevel []の[]に3が含まれているジョブが起動する


カーネル起動
↓

/sbin/initが**startupイベント**発動　

↓
/etc/init/rcS.confジョブが実行
(startupイベントを受信。start on startup の記載があるので)
↓
/etc/init/rcS.confの中で以下を実行

/etc/rc.d/rc.sysinit実行 (sysvinitと同じ)
exec telinit $runlevel　実行 (runlevelのイベントを発生)　今回だとレベル３


↓
/etc/init/@@@.confで各ランレベルに対応したジョブが実行
ランレベル３の場合だと/etc/rc.confがだけが該当 (中身は下記参照)

↓

/etc/init/rc.confの中で

/etc/rc.d/rc ＄runlevel 実行

↓
/etc/rc.d/rcの中で、
/etc/rc[ランレベル].d/Sを順次実行するような処理がある。(sysvinitと同じ)

↓

各デーモンが起動して終わり。


/etc/init/rcS.conf

```sh
# rcS - runlevel compatibility
#
# This task runs the old sysv-rc startup scripts.
#
# Do not edit this file directly. If you want to change the behaviour,
# please create a file rcS.override and put your changes there.

start on startup

stop on runlevel

task

# Note: there can be no previous runlevel here, if we have one it's bad
# information (we enter rc1 not rcS for maintenance).  Run /etc/rc.d/rc
# without information so that it defaults to previous=N runlevel=S.
console output
pre-start script
        for t in $(cat /proc/cmdline); do
                case $t in
                        emergency)
                                start rcS-emergency
                                break
                        ;;
                esac
        done
end script
exec /etc/rc.d/rc.sysinit
post-stop script
        if [ "$UPSTART_EVENTS" = "startup" ]; then
                [ -f /etc/inittab ] && runlevel=$(/bin/awk -F ':' '$3 == "initdefault" && $1 !~ "^#" { print $2 }' /etc/inittab)
                [ -z "$runlevel" ] && runlevel="3"
                for t in $(cat /proc/cmdline); do
                        case $t in
                                -s|single|S|s) runlevel="S" ;;
                                [1-9])       runlevel="$t" ;;
                        esac
                done
                exec telinit $runlevel
        fi
end script

```

## 「runlevel」のイベント契機で発動するジョブ
```sh
$ grep 'start on runlevel' /etc/init/*
/etc/init/ck-log-system-restart.conf:start on runlevel 6
/etc/init/ck-log-system-stop.conf:start on runlevel 0
/etc/init/kexec-disable.conf:start on runlevel [!S016]
/etc/init/quit-plymouth.conf:start on runlevel S or stopping rc RUNLEVEL=[234]
/etc/init/rc.conf:start on runlevel [0123456]
/etc/init/rcS-sulogin.conf:start on runlevel S
/etc/init/serial.conf:# start on runlevel [23]
```
**この中でランレベル3で発動するのは/etc/init/rc.conf**　のみ！！！


## /etc/init/rc.conf 見てみる

```sh
$ cat /etc/init/rc.conf 
# rc - System V runlevel compatibility
#
# This task runs the old sysv-rc runlevel scripts.  It
# is usually started by the telinit compatibility wrapper.
#
# Do not edit this file directly. If you want to change the behaviour,
# please create a file rc.override and put your changes there.

start on runlevel [0123456]

stop on runlevel [!$RUNLEVEL]

task

export RUNLEVEL
console output
exec /etc/rc.d/rc $RUNLEVEL
```

/etc/rc.d/rc[0-6].d/各ファイルが実行される。

このジョブが終わるとジョブ完了イベント発生。



## Upstartまとめ
* Upstartでも前からあるinittabとほぼ同じ起動順番。ただ、ジョブ化することで**並列化できて高速化**できるみたい。
* upstatでも`/etc/rc.d/rc1-6.d/各シェルスクリプト -> /etc/rc.d/init.d/各シェルスクリプト`のランレベルのシェルスクリプトが実行される。sysvinitと同じもの。









# Systemd
* PID=1として/usr/lib/systemd/systemd(systemdの本体)が起動
* Systemdにおける起動の基点となるUnitはdefault.target
* SysVinitにおいてシェルスクリプトの中で実施されていたそれぞれの処理を全て個別の**Unit**として定義
* Systemdでは、Unitに定義されたUnit間の関係を元にSystemd側で起動順を決定する。

### Unit種類
Unit名の拡張子部分でUnitの種類が区別される

Unitの定義ファイル
/usr/lib/systemd/system(デフォルト)
/etc/systemd/system(デフォルトから変更した内容)


targetと呼ばれる種類のUnitを利用することで複数のUnitをグルーピングすることができる。
targetは他Unitとの関係を保持し、複数のUnitをとりまとめる。例えばネットワーク関連のUnitをとりまとめたnetwork.targetや、従来のrc.sysinitに相当する処理をとりまとめたsysinit.target等が存在する。targetを利用することで、Unit同士の依存や起動順定義が楽に行うことができる。

### Systemd参考
http://equj65.net/tech/systemd-boot/
http://yukiyan.hatenablog.jp/entry/2015/10/18/190639
http://enakai00.hatenablog.com/entry/20130916/1379295816
https://www.slideshare.net/enakai/linux-27872553
https://thinkit.co.jp/story/2014/12/11/5388?page=0%2C1

#### 依存関係
* [Unit]セクションでWants=Requires=に対して、当該Unitと一緒に有効化すべきUnitを指定する
* 「＜Unit名＞.wants」または「＜Unit名＞.requires」という名のディレクトリを作成

#### 順序関係
[Unit]セクションでAfrer=やBefore=に対して、当該Unitを指定したUnitより後に起動するか前に起動するかを定


プロセス管理の挙動に関するパラメータを詳細に設定することが可能
## Cgroupによる制御
systemdではserviceごとにプロセスをグループ化している
systemdは、serviceごとにcgroupsによるリソースの割り当てを細かに制御することができるよう
cgroupsの設定を直接に行うのではなく、serviceの設定ファイルにリソースの割り当て条件を記載することで、systemdが該当serviceのグループに対して必要な設定を行う形になります

## ログ管理
Unitとして起動したデーモンプロセスの標準出力、およびrsyslogdへの出力内容を独自のロギングサービス(journald)に送る


# まとめ
システム起動がいかなる仕組みによって実現されようが、”システムを起動するためにやらなければいけないこと”（ネットワークの初期化や必要に応じたプロセス起動など）は変わらない。システム起動を実現する仕組みの違いは、これらの処理を”どうやって管理し、どのように実行するか”という点にある。