Linux プロセス デーモン initシステム

# 目次

- [プロセス](#プロセス)
- [ジョブ](#ジョブ)
- [タスク](#タスク)
- [スレッド](#スレッド)
- [サービス / デーモン](#サービス--デーモン)

# プロセス
process

メモリ上に展開（ロード）され、実行されている状態のプログラム。

**実行中のプログラムの基本単位** であり、それぞれのプロセスは生成時にプロセスID（**PID**）が割り当てられる。

- カーネルは **PID によってプロセスを識別** し、プロセスに対して PID を使用してメモリ、CPU、その他のリソースの割り当てを行う
- 各プロセスは独立して動作し、基本的に他のプロセスと **メモリ空間を共有しない**
- 「ユーザが起動するプロセス」と、「システム（[システムユーザ](https://qiita.com/AsahinaKei/items/59c380a98c65d0aef887#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%A6%E3%83%BC%E3%82%B6)）が起動するプロセス（プロセスが起動する別のプロセス）」が存在する
- プロセスは起動したユーザの ユーザID（**UID**）と グループID（**GID**）を保持する

[シェル](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%82%B7%E3%82%A7%E3%83%AB) もプロセスの一つであり、ビルトインシェル変数 [`$$`](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%83%93%E3%83%AB%E3%83%88%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB%E5%A4%89%E6%95%B0) を確認することで、現在実行されているシェルのプロセスIDを知ることができる。

```console:実行中のシェルのプロセス ID を確認する
$ echo $$
```

## 親プロセス / 子プロセス

プロセスには 親プロセス と 子プロセス というお互いの関係性に基づく概念がある。

プロセスは `fork()` を呼び出すことで新しいプロセスを作成することができる。このとき、プロセスを生成したプロセスを **親プロセス** 、生成されたプロセスを **子プロセス** という。

親プロセスから子プロセスを生成することを「fork する」と表現する。

ターミナルで実行したコマンドは、シェルによって起動されるため、シェルの子プロセスとして扱われる。

## ゾンビプロセス

終了しているにも関わらず、プロセステーブルと呼ばれるカーネルのプロセス管理領域に情報が残ったままのプロセス。

名前の通り死んでいるけど完全には消えていない状態を指す。

通常、子プロセスが終了すると [終了ステータス](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E7%B5%82%E4%BA%86%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9--) やリソース情報（CPU、メモリ、[ファイルディスクリプタ](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%82%BF) など）がカーネルに保存される。親プロセスは [システムコール](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%B3%E3%83%BC%E3%83%AB) `wait()` や `waitpid()` を呼び出し、プロセステーブルから子プロセスの情報を削除する必要がある。

親プロセスが子プロセスの終了を認識できず、`wait()` や `waitpid()` を呼び出さない場合、カーネルはプロセステーブルの子プロセスの情報を消去することができず、ゾンビプロセスが発生する。

親プロセスが終了すると、ゾンビプロセスは [`init`](#sysvinit) (または [`systemd`](#systemd)) に引き取られ、自動的に `wait()` されて消える。そのため、ゾンビプロセスが残るのは親プロセスが生きている間だけとなる。

`$ ps` コマンドで確認すると、プロセスの状態（`STAT`）が `Z`、`COMMAND` が `defunct` で表示される。

```console:ゾンビプロセス
$ ps aux
USER PID  %CPU %MEM VSZ    RSS  TTY STAT START   TIME COMMAND
user 1234 1.0  0.1  123456 7890 ?   Z    10:15   0:00 [my_program] <defunct>

$ ps aux | grep Z
user 1234 1.0  0.1  123456 7890 ?   Z    10:15   0:00 [my_program] <defunct>
```

ゾンビプロセスは、通常のプロセスとは異なりメモリやCPUを消費しないが、プロセステーブルのエントリを占有する。 そのため、ゾンビプロセスが大量に発生するとプロセステーブルの枯渇により新しいプロセスを作成できなくなる可能性がある。

## プロセスグループ

複数のプロセスを OS がまとめて管理するための単位。

プロセスグループID（**PGID**）によって識別される。すべてのプロセスは PGID を保持している。

[ジョブ](#ジョブ) の最初のプロセスの PID が PGID になることが多い。

```console:プロセスグループを確認する（output format）
$ ps -o pid,pgid,comm 
```

## シグナル

- プロセス → **プロセス**
- カーネル → **プロセス** 

という方向の非同期通信の一つで、特定のイベントをプロセスに対して通知するための仕組み。

シグナルが送られると、プロセスは処理の途中であっても事前に定義された特定の動作を実行する。C 言語でこのシグナル受信時の処理をカスタマイズ実装することができるが、通常はデフォルトの動作が実行される。

またショートカットキー（`Ctrl` + `C` など）で送信されるシグナルは [フォアグラウンドジョブ](#フォアグラウンドジョブ) にのみ送信される。

シグナルの種類は `$ kill -l` で確認することができる。

```console:シグナルを一覧表示する
$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX
```

| シグナル番号 | シグナル名 | 動作概要 | 説明 |
|:-:|:-:|:-|:-|
| `1` | `HUP` | 終了 / 再起動（*Hang Up*） | シェルが起動している [端末](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E7%AB%AF%E6%9C%AB)（TTY）が閉じられると、シェルが `HUP` を受け取り、そのシェル内で実行中のプロセスにも `HUP` が送られ、デフォルト動作としてプロセスが終了する。設定ファイルの再読み込みに利用される。 |
| `2` | `INT` | 終了（*Interrupt*） | **`Ctrl` + `C`** による割り込み |
| `3` | `QUIT` | コアダンプ | **`Ctrl` + `\`** によるプロセスの終了 |
| `9` | `KILL` | 強制的な終了 | リソースの解放を省略してプロセスが即時終了する<br>※ 無視できない |
| **`15`** | **`TERM`**| 正規の終了（*Terminate*）  | プロセス終了前にリソースの解放が行われる |
| `18` | `CONT` | 再開（*Continue*） | 一時停止中プロセスの再開 |
| `19` | `STOP` | 強制的な一時停止 | 再開時は `CONT` シグナルが必要<br>※ 無視できない |
| `20` | `TSTP` | 停止（*Terminal Stop*） | **`Ctrl` + `Z`** による割り込み<br>シェルがプロセスをバックグラウンドへ移動させて停止させるが、`$ fg` や `$ bg` で再開できる |

※無視できない=プロセスがこのシグナルをキャッチして処理することはできない


シグナル名はプレフィックス `SIG` 付きで定義されているが、省略形を使用できる。

```console:
$ kill -TERM プロセスID
$ kill -SIGTERM プロセスID
```

## `$ ps`
process status

現在実行中のプロセスを表示する。

オプションなしの場合、現在ログイン中のユーザが起動中のプロセス一覧が表示される。

```console:ログインユーザが起動したプロセスを一覧表示する
$ ps
```

| 項目 | 意味 |
|:-|:-|
| `PID` | プロセスID |
| `TTY` | プロセスが紐づく端末（[TTY](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#-tty)） |
| `TIME` | プロセスが CPU を使用した時間 |
| `CMD` | 実行中のシェルもしくはコマンド |
| `PGID` | プロセスグループ |
| `PRI` | プロセスの優先度（`$ nice`、`$ renice`） |


`-u` オプションで特定のユーザが起動したプロセスを表示することができる。

```console:特定のユーザが起動したプロセスを表示する
$ ps -u ユーザ名
```

実行中のすべてのプロセスを表示するには `ax` もしくは `-e` オプションを使用する。

```console:すべての実行中プロセスを表示（all:すべて、extra:端末に接続されていないプロセスを含む、every:すべて）
$ ps ax
$ ps -e
```

```console:プロセスの詳細情報を確認する
$ ps -l
```

## `$ top`

```console:プロセス情報、リソースの使用状況をリアルタイム表示する
$ top 
```

`Q` キーで終了。

それ以外のキーでインタラクティブモードに切り替えができる。

## `$ pstree`

```console:親子関係を含めて起動中のプロセスを表示する
$ pstree
```

## `$ pgrep`

```console:条件に合致したプロセスIDを表示する
$ pgrep 検索ワード
```

[`$ grep`](https://qiita.com/AsahinaKei/items/161674f16b6c849fbf17#-grep) は [標準入力](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#%E6%A8%99%E6%BA%96%E5%85%A5%E5%8A%9B--stdin) やファイルから検索ワードを探すのに対して、`$ pgrep` は実行中のプロセスから検索ワードを探す。

[正規表現](https://qiita.com/AsahinaKei/items/161674f16b6c849fbf17#%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE) を使った検索が可能。

```console:プロセスを起動したユーザを指定して検索を行う
$ pgrep -u ユーザ名 検索ワード
```

```console:プロセスを起動したグループを指定した検索を行う
$ pgrep -g グループ名 検索ワード
```

## `$ nice`

プロセスのスケジューリング優先度（priority）を変更してコマンドを実行する。

優先度は `$ ps -l` で確認することができる。

ただし、直接、優先度を変更するのではなく **nice 値**（ナイス値）を変更することにより相対的に変更する。nice値は `-20` ~ `19` までの範囲で設定することができる（デフォルト値は `0`）。

- nice 値が高い → 優先度が低い
- nice 値が低い → 優先度が高い

```console:nice 値を +10 してコマンド実行する（デフォルトが +10）
$ nice コマンド
```

`-n` オプションにより、変更する nice 値を指定できる。

```console:通常より低い優先度 (+10)で指定したコマンドを実行する
$ nice -n 10 コマンド
```

マイナス値は `root` ユーザのみ設定できる。

```console:通常より高い優先度 (-5)で指定したコマンドを実行する
$ sudo nice -n -5 コマンド
```

## `$ renice`

実行中のプロセスの nice 値を変更する。

```console:指定したプロセスID（PID）の優先度を変更する
$ renice -n nice値 -p PID
```

nice 値を下げることができるのは `root` ユーザのみ。

## `$ kill`

```console:指定したプロセスを強制終了させる
$ kill プロセスID
$ kill プロセスID1 プロセスID2 ... # 複数指定可
$ kill %ジョブID
$ kill -プロセスグループID
$ kill -シグナル プロセスID
$ kill -シグナル番号 プロセスID
$ kill -s シグナル プロセスID
```

親プロセスを終了させた場合、子プロセスについても終了される。

コマンドの内部ではプロセスに対してシグナルが送信されている。以下のように直接シグナルを指定することもできる。

```console:シグナルを明示的に指定する
$ kill -STOP プロセスID
$ kill -s STOP プロセスID
```

シグナルには数字が紐づいているため、数字を指定しても良い。

```console:シグナルを数値で指定して実行
$ kill -19 プロセスID # 19:STOP
```

一般的に、プロセスの強制終了はリソースの解放よりも優先されるため、システムが異常を起こす可能性がある。そのため、先に `$ kill -TERM プロセスID` を実行して、それでも終了できない等、やむを得ない場合にのみ `$ kill -KILL プロセスID` を実行した方が良い。

**シグナルを省略した場合は `TERM` シグナルを指定したことになる**。

```console:シグナルを省略した場合は TERM シグナルを指定したことになる
$ kill プロセスID
$ kill -TERM プロセスID # kill プロセスID と同じ
```

`-` を [PGID](#プロセスグループ) の前につけることで、[プロセスグループ](#プロセスグループ) に対してシグナルを送信することもできる。

```console:プロセスグループにシグナルを送信する
$ kill -TERM -プロセスグループID
```

## `$ killall`

`$ kill` コマンドでは、シグナル送信先をプロセスID（PID）で指定できたのに対して、`$ killall` コマンドでは、プロセス名を使って指定することができる。

同じ名前のプロセスが複数存在する場合に便利（PID は重複しない）。

```console:プロセスを kill する
$ killall -シグナル プロセス名
$ killall -s シグナル プロセス名
```

シグナルの指定については `$ kill` と同様で、**省略した場合は `TERM` シグナルを指定したことになる**。

```console:シグナルを明示的に指定する
$ killall プロセス名
$ killall -TERM プロセス名 # killall プロセス名と同じ
```

```console:プロセスを起動したユーザを指定する
$ killall -u ユーザ名
```

## `$ pkill`

シグナルを送信するプロセスを「`$ pgrep` でヒットしたプロセス」に絞り込んだ上で kill することができる。

```console:検索ワードにヒットするプロセスにシグナルを送信する
$ pkill -シグナル 検索ワード
```

シグナルの指定については `$ kill` と同様で、**省略した場合は `TERM` シグナルを指定したことになる**。

```console:シグナルを明示的に指定する
$ pkill 検索ワード
$ pkill -TERM 検索ワード # pkill 検索ワード と同じ
```

```console:プロセスを起動したユーザを指定する
$ pkill -u ユーザ名
```

## `$ ulimit`
user limit

プロセスが使用できるリソース量を制限するためのコマンド。

```console:制限値を一覧表示する
$ ulimit -a
```

```console:シェルが作成できるファイルの最大サイズを確認する
$ ulimit -f
```

```console:シェルが作成できるファイルの最大サイズを設定する
$ ulimit -f サイズ
```

```console:同時に開くことができるファイルディスクリプタ数を確認する
$ ulimit -n
```
※ [ファイルディスクリプタ](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%82%BF)

```console:同時に開くことができるファイルディスクリプタ数を設定する
$ ulimit -n 数
```

## 内部コマンド `$ kill` と 外部コマンドの `$ kill`

`$ kill` コマンドには以下の2種類が存在する。 

- ビルトインコマンド（内部コマンド）としての `$ kill`
- 外部コマンドとしての `$ kill`

それぞれのコマンドは以下の違いを持つ。

- [ビルトインコマンド](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%83%93%E3%83%AB%E3%83%88%E3%82%A4%E3%83%B3%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)
    - `bash` や `zsh` に代表される、シェルに組み込まれたコマンド
    - シグナル送信先のジョブID（`%ジョブID`）を指定することができる
- [外部コマンド](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E5%AE%9F%E8%A1%8C%E5%8F%AF%E8%83%BD%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E5%BD%A2%E5%BC%8F)
    - 実行可能ファイル `/bin/kill` として存在するコマンド
    - シェルが持つジョブ管理機能とは連携しないため、ジョブIDを利用できない（[ジョブ](#ジョブ)はシェルが管理するプロセス）

[`$ type`](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#-type) コマンドを使用すると、明示的に指定しない場合にどちらが利用されるのかがわかる。

```console:$ kill で使用されるのが内部コマンドか外部コマンドか調べる
$ type kill
kill is a shell builtin
```

外部コマンドを使用したい場合、絶対パスで実行する方法と [`$ command`](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#-command) を利用する方法がある。

```console:外部コマンドの $ kill を実行する
$ /bin/kill プロセスID   # 絶対パスで指定
$ command kill プロセスID   # command を利用
```

# ジョブ

単一または複数のプロセスで構成される、「一連の処理」の単位。

複数のコマンドを [パイプ](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#%E3%83%91%E3%82%A4%E3%83%97--) で実行した場合も、一連の処理が一つのジョブとされる。

[シェル](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%82%B7%E3%82%A7%E3%83%AB)  によって管理され、それぞれのジョブはジョブID（**JID**）で識別される。

JID はログイン中のシェルセッション内で `1` から順に割り振られるため、異なるシェルセッションでは同じ JID が使用されることもある。

JID を使用する際は、プロセスID（PID）と区別するために **`%`** が接頭語として使用される。

```console:%ジョブID
$ kill %ジョブID
$ kill プロセスID
```

ジョブにはフォアグラウンドで実行されるものとバックグラウンドで実行されるものがある。

:::note info
ジョブはシェルによって管理される
:::


## ジョブの実態

ジョブの実態は [プロセスグループ](#プロセスグループ) であり、内部的にはプロセスグループとして扱われている。

**プロセスグループは OS によって管理され、ジョブはシェルによって管理される**。

ジョブID（JID）がシェル内部で使用される識別子であることを理解すると、外部コマンドの `$ kill`（`/bin/kill`） が JID を指定して実行できないことも自然と理解できる（外部コマンドは JID を直接取得することができない。外部コマンドが取得できる JID は、システム全体で共有される、例えば [ビルトインシェル変数](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%83%93%E3%83%AB%E3%83%88%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB%E5%A4%89%E6%95%B0) などの情報に限られるため）。

複数のプロセスで構成されるジョブの場合、最初に実行されるプロセスの PID がそのジョブを代表する PID として扱われ、PGID にはこの PID が使用される。

この最初に実行されるプロセスをジョブの **リーダー** と表現することがあり、ジョブのリーダーの PID は、そのジョブの実態であるプロセスグループの PGID と一致する。

:::note info
ジョブの実態はプロセスグループ
:::

## フォアグラウンドジョブ

フォアグラウンドで実行中のジョブがある場合、そのジョブがシェルの [標準入力](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#%E6%A8%99%E6%BA%96%E5%85%A5%E5%8A%9B--stdin)（TTY入力）を占有するため、シェルがキーボード入力を受け付けないように見える。

[シグナル](#シグナル) `Ctrl` + `C` で終了させたり、`Ctrl` + `Z` でバックグラウンドに送り、一時停止させることができる。

## バックグラウンドジョブ

バックグラウンドで実行中のジョブがあっても、シェルはコマンド入力を受け付ける。

バックグラウンド = 停止 ではなく、バックグラウンドで実行中のジョブもあれば、バックグランドで一時停止中のジョブも存在する。バックグランドで一時停止中のジョブは [`$ fg`](#-fg) もしくは [`$ bg`](#-bg) で再開することができる。

バックグラウンドジョブもデフォルトでは [標準出力](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#%E6%A8%99%E6%BA%96%E5%87%BA%E5%8A%9B--stdout) が [TTY](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E6%93%AC%E4%BC%BC%E7%AB%AF%E6%9C%AB--pty) に接続されているため、フォアグラウンドジョブの出力内容と混ざることがある。

過去にバックグラウンドで実行されたコマンドのうち、最後に生成されたプロセスのプロセスIDはビルトインシェル変数の [`$!`](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%83%93%E3%83%AB%E3%83%88%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB%E5%A4%89%E6%95%B0) に格納されている。

```console:最後に実行されたバックグラウンドジョブを確認する
$ echo $!
```

## `&`

コマンドの末尾に `$` をつけることで、ジョブをバックグラウンドで実行させることができる。

```console:バックグラウンドで実行
$ コマンド &
```

## `$ jobs`

実行中、もしくは一時停止中のジョブID（JID）を表示する。

```console:実行中、一時停止中のジョブを確認
$ jobs
```

```:出力形式
[JID]+ 状態(Running/Stopped) [ジョブの内容]  #  実行中のジョブ
[JID]- 状態(Running/Stopped) [ジョブの内容]  # 直前のジョブ
[JID] 状態(Running/Stopped) [ジョブの内容]
```

`-l` でプロセスID（PID）も併せて表示する。

```console:プロセスID も表示
$ jobs -l
```

## `$ bg`
background

一時停止中のジョブをバックグラウンドで再開する。

```console:バックグラウンドで再開
$ bg %ジョブID
```

ジョブIDを省略した場合、最後に一時停止したジョブが対象になる。

## `$ fg`
foreground

一時停止中のジョブをフォアグラウンドで再開させる。

または、バックグラウンドで実行中のジョブをフォアグラウンドで実行させる。

```console:フォアグランドで実行させる
$ fg %ジョブID
```

ジョブIDを省略した場合、最後に操作したジョブ（バクグラウンドのものを含む）が対象になる。


## `$ disown`
own=所有する

現在のジョブリスト（[`$ jobs`](#-jobs)）から特定のジョブを削除し、特定のジョブをシェルの管理下から切り離す。

管理対象外とすることで `HUP` シグナルを受信しなくなるため、ユーザがログアウトしてもプロセスが終了しなくなる。

```console:ジョブをシェルの管理下から外す
$ diswon %ジョブID
```

# タスク

[カーネル](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB) のスケジューラによって管理される、処理のスケジューリング単位。

（ただしプロセスやスレッドを含む広い概念であり、文脈によって意味が異なる場合がある）

CPU の処理単位としてスケジューリングされ、以下の状態間を遷移する。

- 実行可能状態（`READY`）
- 実行状態（`RUN`）
- 待機状態（`WAIT`）

Linux では [プロセス](#プロセス) とほぼ同義で使用される。

# スレッド

一つのプロセス内で実行される処理の実行単位。

一つのプロセス内のスレッド同士は、プロセス内でヒープ領域と呼ばれるメモリ空間を共有するが、スタックと呼ばれるメモリ空間については各スレッドが独立して保持する。ただし正確には、スタックはスレッドごとに確保されるものの、他のスレッドのスタックにアクセスすることも技術的には可能である。

（Java のスレッドについては [こちら](https://qiita.com/AsahinaKei/items/b05b5b59e94840cbbaa1#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%A8%E3%81%AF)）

# サービス / デーモン

メモリ上に常駐することで、バックグラウンド上で特定の機能を提供するプロセス。

通常は [SysVinit](#sysvinit) や [systemd](#systemd) によって管理され、自動的に起動されるが、[`$ systemctl`](#-systemctl) や [`$ service`](#service) コマンドによって起動、停止、再起動などの制御を行うことも可能。

ジョブやタスクはコマンド実行時に起動し、処理終了と共に終了するため、継続的に動作し続けるサービス、デーモンとは異なる。

## サービスとデーモンの違い

Linux では **デーモン** はバックグラウンドで動作し、[制御端末を持たない](#標準入出力の扱い) プロセスを指す。名前の末尾に `d` の付くものが多い。

**サービス** は特に `init` システムによって管理されるプロセスを指す。デーモンはサービスに含まれる概念であるが、単発のジョブ（`Type=oneshot`）もサービスとして管理されることがある。


特に `systemd` では **ユニット** という概念があり、その中の「サービスユニット（`*.service`）」が狭義の「サービス」にあたる（ただし、ソケットユニット（`*.socket`）やタイマーユニット（`*.timer`）もサービスの一部として機能する場合がある）。

## 主なデーモン

- Webサーバ
    - `httpd`
    - `apache2`
    - `nginx`
- DBサーバ
    - `mysqld`
    - `postgres`
    - `mongod`
- メールサーバ
    - `postfix`
    - `exim`
    - `sendmail`
- SSHサーバ
    - `sshd`
- ジョブスケジューラ
    - [`$ crond`](#-)

デーモンは通常 `init`（または `systemd`）を直接、または間接的な親プロセスとするため、ログインユーザとは独立したプロセスツリーを持つ（プロセスツリーは `$ pstree` で確認できる）。

プロセスには、「親プロセスが終了したら子プロセスも連動して終了する」特徴があるが、システムの起動時に自動起動したデーモンは **ユーザが起動するプロセスとは親子関係にない** ため、ユーザがログアウトしても連動して終了することがない。

## 標準入出力の扱い

デーモンのプロセスは [標準入出力](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e) を [`/dev/null`](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#devnull) に接続しているため、制御端末（[TTY](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#-tty)）を持たない。

ただし、`systemd` によって管理されるサービスは `journald` にログを記録するため、標準出力が `/dev/null` にリダイレクトされない場合もある（`systemctl status` で確認可能）。

デーモンのように標準入出力に接続されておらず、制御端末を持たないプロセスは、[`$ ps aux`](#-ps) コマンドで確認すると `TTY` の項目が `?` で表示される。

## SysVinit
System V(Five) Init

長年 Linux の標準的な **init** システムとして使用されてきた、システムの起動やプロセス管理の仕組み。

起動の遅さや並列処理ができないなどの課題があり、Red Hat 系（RHEL、CentOS、Fedora）や Debian 系（Debian、Ubuntu）では、既に後継版である [systemd](#systemd) に移行されている。

ただし、一部の軽量ディストリビューション（Alpine Linux など）では、現在も SysVinit が採用されている。

以下の特徴を持つ。

- 「ランレベル」でシステムの状態を管理する
- サービスの起動を **直列処理** で1つずつ順番に起動するため時間がかかる
- サービスが異常終了した場合、自動で復旧しない

SysVinit では [カーネルが起動](#システム起動の流れ) すると、まず `/sbin/init` が最初に実行される。

`init` は PID（プロセスID）が `1` の特別なプロセスであり、システム起動後の最初のプロセスとして動作する。PID `0` はカーネル用の特別な番号として使われており、 `1` はユーザプロセスが使用できる最初の PID である。 

SysVinit の主な役割は、システム起動時に必要なプロセスを適切に立ち上げ、シャットダウン時に適切に停止することだが、「デーモンを起動する」、「親プロセスが終了した孤立プロセスを引き取る」役割なども併せ持つ。

ユーザが手動で起動したプロセスは `init` ではなく、シェルなどが親プロセスになる。

`init` が起動すると、SysVinit は次に、設定ファイル **`/etc/inittab`** を読み込んで、どの **ランレベル** で起動するかを決定する。

### ランレベル

システムの状態を管理するための概念のこと。

`/etc/inittab` に記載されたランレベルに応じて `/etc/rcX.d/` （X はランレベルの番号）にあるスクリプトがサービスとして起動される。

`/etc/rcX.d/` ディレクトリ内の `S` や `K` で始まるファイルは、 `/etc/init.d/` ディレクトリに配置されたスクリプトへの [シンボリックリンク](https://qiita.com/AsahinaKei/items/8d43c366f061a27ad670#%E3%82%B7%E3%83%B3%E3%83%9C%E3%83%AA%E3%83%83%E3%82%AF%E3%83%AA%E3%83%B3%E3%82%AF) になっている。

`S` は `start` を意味し、`K` は `kill` を意味する。

| ランレベル | 意味 |
|:-:|:-|
| `0` | システムの停止（シャットダウン） |
| `1` | シングルユーザモード<br>`root` ユーザのみがログインできる |
| `2` | マルチユーザモード<br>NFSなし(※) |
| `3` | マルチユーザモード<br>NFSあり(※)、GUIなし |
| `4` | 未使用 |
| `5` | マルチユーザモード + GUI（X Window） |
| `6` | システムの再起動 |

※ NFS（Network File System）
リモートのサーバにあるファイルをローカルのディレクトリのように扱える仕組み。

例えばサーバー上の `/home/shared/` をクライアントの `/mnt/shared/` に [マウント](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88) すれば、まるでローカルファイルのようにアクセスできるようになる。ランレベル `2` では NFS クライアントが無効、`3` では有効。


### `$ runlevel`

```console:現在のランレベルを表示する
$ runlevel
```

### `$ init` / `$ telinit`

```console:ランレベルを変更する
$ init ランレベル
$ telinit ランレベル
```

### `$ service`

SystemVinit で使用される [サービス](#サービス--デーモン) 管理用コマンド。

```console:サービスを起動する
$ service サービス start
```

```console:指定したサービスを停止する
$ service サービス stop
```

```console:サービスを再起動する
$ service サービス restart
```

```console:サービスを停止させずに、設定のみ再読み込みする
$ service サービス reload
```

```console:サービスの状態を確認する
$ service サービス status
```

### `$ shutdown`

```console:シャットダウンする（halt）
$ shutdown -h now
```

```console:再起動する（reboot）
$ shutdown -r now
```

```console:21時にシャットダウンする
$ shutdown -h 21:00
```

```console:予定したシャットダウンをキャンセルする（cancel）
$ shutdown -c
```

## systemd

従来の [SysVinit](#sysvinit) の後継版として開発され、現在 Linux で主流になっている init システム。

以下の特徴を持つ。

- 並列処理により高速に起動できる
- すべての管理対象（サービス、デバイス、マウントポイントなど）を **ユニット** という概念により統一的に管理する
- SysVinit のランレベルに相当する **ターゲット** によりシステム状態を管理する
- 異常終了したサービスを自動で再起動できる
- `journald` というログ管理システムが組み込まれている

ただし、以下の批判をされることがある。

- SysVinit はシンプルなシェルスクリプトで構成されていたのに対して、systemd はバイナリの実行可能ファイルで構成されており、内部動作が隠蔽されているのに加えて複雑すぎる
- UNIX の「小さなツールを組み合わせる」という設計思想に反して、多機能すぎる
- あまりにも多くを管理しすぎているため、 systemd に対する依存が強くなる

このため、一部のディストリビューション（Alpine Linux, Devuan, Artix Linux など）は systemd を採用せず、SysVinit や runit、OpenRC などを使い続けている。

### ユニット
Unit

systemd では、すべてのサービスやシステムリソースを **ユニット** という概念で管理する。

ユニットが定義されたファイルを **ユニットファイル** と呼ぶ。ユニットファイルは systemd がサービスやデバイスの管理を行う際に参照される。

| 拡張子 | 説明 |
|:-|:-|
| `.service` | デーモンやサービスを管理するユニット<br>Webサーバ：`httpd.service`、`apache2.service`、`nginx.service`<br>DBサーバ：`mysqld.service`、`postgresql.service`<br>メールサーバ：`postfix.service`<br>SSHサーバ：`sshd.servic`<br>ジョブスケジューラ：`crond.service` |
| `.target` | システムの状態（SysVinitのランレベルに相当）を管理するユニット<br>`default.target`、`graphical.target` など |
| `.mount` | ファイルシステムの [マウント](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88) 設定を管理するユニット |
| `.device` | デバイスを管理するユニット |
| `.timer` | ジョブのスケジュールを管理するユニット（`cron` に相当する） |
| `.socket` | ソケットを管理するユニット |

ユニットファイルは下記のディレクトリに格納されている。

| ディレクトリ | 説明 |
|:-|:-|
| `/etc/systemd/system/` | ユーザがカスタマイズしたユニットファイルが格納される<br>優先順位が最も高い |
| `/run/systemd/system/` | 一時的な Unit ファイルが格納される<br>再起動によって消える |
| `/usr/lib/systemd/system/` | Linux デフォルトのユニットファイルが格納される<br>ユーザが直接編集しない |
| `/lib/systemd/system/` | ディストリビューションによって `/usr/lib/systemd/system/` の代わりに使用される |
| `~/.config/systemd/user/` | ユーザ単位のユニットファイルが格納される |

ユニットファイルは `[セクション]` 単位で記述され、以下のような構成となっている。

```text:ユニットファイル
[Unit]
Description=説明
After=起動していることが前提とされる別ユニット（依存しているユニット。起動順の制御に関係するが、試みるだけで強制力を持たない。つまり、ここに記載されたユニットが起動していなくても、起動処理が走る）
Wants=推奨の依存関係（なくても起動可能）
Requires=必須の依存関係（これが無いと起動しないという別のユニット。強制的な依存関係となるため、一方が停止されるともう一方も停止する。起動も同様。）

[Service] （.service の場合）
ExecStart=ユニットが内部で実行するコマンドの絶対パス
Restart=再起動の設定（always、on-failure で指定）
User=実行ユーザ名
Group=実行グループ名

[Install]
WantedBy=実行時にどのターゲットに関連付けるか
```

### ターゲット

SysVinit のランレベルに相当するもの。

| SysVinit のランレベル | 対応するターゲット |
|:-|:-|
| `0` | `poweroff.target` |
| `1` | `rescue.target` |
| `2`、`3`、`4` | `multi-user.target` |
| `5` | `graphical.target` |
| `6` | `reboot.target` |

### `$ systemctl`
System Control

systemd で使用されるユニット管理用コマンド。

指定するユニットが `***.service` の場合、`.service` を省略できる。

```console:ユニットを起動する
$ systemctl start ユニット
```

```console:ユニットを停止する
$ systemctl stop ユニット
```

```console:システムをシャットダウンする
$ systemctl poweroff
```

```console:ユニットを再起動する
$ systemctl restart ユニット
```

```console:システムを再起動する
$ systemctl reboot
```

```console:ユニット（サービス）を停止させずに、設定のみ再読み込みする
$ systemctl reload ユニット # サービスごとに設定ファイルは異なる
```

```console:ユニット（サービス）を停止させずに、ユニットファイルの変更を反映させる
$ systemctl daemon-reload
```

`$ systemctl reload サービス` は特定のサービスの設定を再読み込みするためのコマンド。

`$ systemctl daemon-reload` は `systemd` に [ユニットファイル](#ユニット) の変更を反映させるためのコマンド。

```console:システム起動時にユニットを自動起動する
$ systemctl enable ユニット
```

```console:システム起動時にユニットを自動起動させない
$ systemctl disable ユニット
```

```console:ユニットの状態を確認する
$ systemctl status ユニット
```

```console:ユニットの依存関係を表示する
$ systemctl list-dependencies
```

```console:ユニットをマスクすることで起動禁止状態にする
$ systemctl mask ユニット
```

ユニットをマスクすると、ユニットに対する [シンボリックリンク](https://qiita.com/AsahinaKei/items/8d43c366f061a27ad670#%E3%82%B7%E3%83%B3%E3%83%9C%E3%83%AA%E3%83%83%E3%82%AF%E3%83%AA%E3%83%B3%E3%82%AF) が [`/dev/null`](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#devnull) に作成される。 

```console:マスクを解除する
$ systemctl unmask ユニット
```

```console:デフォルトのターゲット（起動時に適用される systemd のターゲット）を確認する
$ systemctl get-default
```

```console:システムのデフォルトのターゲットを変更する
$ systemctl set-default ターゲット
```


## システム起動の流れ

1. **電源ON**
   - ハードウェアが起動
2. **ファームウェア（BIOS / UEFI）が実行される**
   - ハードウェアを初期化し、ブートローダを読み込む
3. **ブートローダ（GRUB など）が実行される**
   - [カーネル](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB) をメモリにロード
4. **カーネルが起動する**
   - ハードウェアや [ファイルシステム](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0) を認識し、`init`（または `systemd`）を起動
5. **`init`（または `systemd`）によるプロセス管理が開始される**
   - ランレベル（SysVinit）またはターゲット（systemd）に応じたサービスが起動

システム起動時にカーネルが画面に出力する情報は、`/var/log/dmesg` に格納され、`$ dmesg` にて確認することができる（Ubuntu では `/var/log/dmesg` は作成されない）。

### `$ dmesg`

`/var/log/dmesg` に保存された、起動時のカーネルのログを確認する。

```console:起動時のカーネルのログを確認する
$ dmesg
```

`/var/log/dmesg` の情報は `$ dmesg --clear` にて明示的に削除することができる（Ubuntu では `/var/log/dmesg` は作成されない）。

### `getty`

`/dev/tty1` ~ `/dev/tty6` までの [仮想コンソール](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E7%8F%BE%E4%BB%A3%E3%81%AE%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB%E4%BB%AE%E6%83%B3%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%ABtty) のデバイスファイルは、起動時にカーネルによって作成される。

OS が起動すると、`SysVinit` もしくは `systemd`  が `getty` （get tty）プロセスを起動する。

`getty` の役割は以下を行うことにある。

- デバイスとしての端末を `open()` する
    - `open()` は [システムコール](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%B3%E3%83%BC%E3%83%AB) の一つ
- シェルの標準入出力を `open()` した端末へ接続する
- ユーザ名の入力を促すログイン用のプロンプトを表示する
- `login` プロセスを起動し、ユーザが入力した情報を渡すことでログインを実行する

`getty` は `/dev/tty1`、`/dev/tty2`、 ... のデバイスファイルを  `open()` によってオープンし、戻り値として取得した [ファイルディスクリプタ](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%82%BF) の接続先を次のように割り当てる。

| ファイルディスクリプタ | 接続先 |
|:-|:-|
| `0`（`0`） | `/dev/tty1`（キーボード） |
| `1`（標準出力） | `/dev/tty1`（画面出力） |
| `2`（標準エラー） | `/dev/tty1`（画面出力） |

これにより、シェルは [標準入力](https://qiita.com/AsahinaKei/items/301b88e464c23aba231e) を `/dev/tty1`（キーボード）から受け取り、標準出力を `/dev/tty1`（ディスプレイ）へと流せるようになる。

`getty` はその後、下記のようなログインプロンプトを表示し、ユーザー名から入力された「ユーザ名」と「パスワード」を `login` プロセスに渡す。`login` プロセスは `getty` によって起動される。

![Screenshot 2025-02-19 at 21.42.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/c629c08b-be99-4dc9-ade4-06d88e39a1b0.png)

`login` プロセスは、入力された情報による認証が完了すると、ユーザのデフォルトシェル（`bash` など）起動する。


## ファームウェア
Firmware

**BIOS** （Basic Inpput / Output System）や **UEFI** （Unified Extensible Fireware Interface）に代表される、コンピュータの基本的な動作制御のためのソフトウェア。

**ブートローダ** を起動する役割を持つ。

BIOS はマザーボード上の [ROM](https://qiita.com/AsahinaKei/items/8fede81f102f848cb6ba#rom) に、UEFI はマザーボード上の [フラッシュメモリ](https://qiita.com/AsahinaKei/items/8fede81f102f848cb6ba#%E3%83%95%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5%E3%83%A1%E3%83%A2%E3%83%AA)（NVRAM）に格納されている。

実体はソフトウェアでありながらハードウェアとソフトウェアの中間的な存在として位置づけられる（firm = 固い、堅牢）。

従来の BIOS は書き換えが難しかったが、最近の UEFI ではアップデートが容易になっている。

UEFI には、デジタル署名済みの信頼できるソフトウェアだけを起動するセキュリティ機能（セキュアブート）もある。

## ブートローダ
Boot Loader

**GRUB** 、Windows Boot Manager に代表される、カーネルをメモリにロードして起動するプログラム。

複数の OS がインストールされている PC などで、どの OS を起動するかを選択できる機能を **デュアルブート** や、**マルチブート** という。

`/boot/grub/menu.lst` や `/boot/grub/grub.conf` が設定ファイルとして使用される。

### `$ grub-install`

```console:GRUBをシステムにインストールする
$ sudo grub-install インストール先ディスクのデバイスファイル
```

```console:例
$ sudo grub-install /dev/sda
```

## スーパーサーバ

複数のデーモンを管理する役割を持つ特別なデーモン。

デーモンは通常、**常時メモリ上にいる** ため、その分リソースを消費する。
スーパーサーバは必要に応じて適切なデーモンを起動することでリソースを節約することができる。一方で、要求を受けてからデーモンが起動するため、応答が遅くなるデメリットがある。

代表的なスーパーサーバには `inetd`（internet service daemon） や `xinetd`（extended inetd） がある。

スーパーサーバを経由せず、`init` や `systemd` によって起動されるデーモンを **スタンドアロンデーモン** と言う。

### `inetd`
internet service daemon

ネットワークサービス（`telnet`、`ftp`、`rsh`、`pop3` など）へのリクエストを一括で待ち受け、それぞれのサービス用のプロセスを、必要なときだけ起動するスーパーサーバ。

`/etc/inetd.conf` にサービスの定義が記述されている。

### `xinetd`
extended internet daemon

`inetd` の後継版。

- `/etc/xinetd` でシステム全体の設定を行う
- `/etc/xinetd.d/` 配下の設定ファイルでサービス固有の設定を行う
- アクセス制御が可能
- リソースの使用量制限が可能