目次
プロセス
process
メモリ上に展開(ロード)され、実行されている状態のプログラム。
実行中のプログラムの基本単位 であり、それぞれのプロセスは生成時にプロセスID(PID)が割り当てられる。
- カーネルは PID によってプロセスを識別 し、プロセスに対して PID を使用してメモリ、CPU、その他のリソースの割り当てを行う
- 各プロセスは独立して動作し、基本的に他のプロセスと メモリ空間を共有しない
- 「ユーザが起動するプロセス」と、「システム(システムユーザ)が起動するプロセス(プロセスが起動する別のプロセス)」が存在する
- プロセスは起動したユーザの ユーザID(UID)と グループID(GID)を保持する
シェル もプロセスの一つであり、ビルトインシェル変数 $$
を確認することで、現在実行されているシェルのプロセスIDを知ることができる。
$ echo $$
親プロセス / 子プロセス
プロセスには 親プロセス と 子プロセス というお互いの関係性に基づく概念がある。
プロセスは fork()
を呼び出すことで新しいプロセスを作成することができる。このとき、プロセスを生成したプロセスを 親プロセス 、生成されたプロセスを 子プロセス という。
親プロセスから子プロセスを生成することを「fork する」と表現する。
ターミナルで実行したコマンドは、シェルによって起動されるため、シェルの子プロセスとして扱われる。
ゾンビプロセス
終了しているにも関わらず、プロセステーブルと呼ばれるカーネルのプロセス管理領域に情報が残ったままのプロセス。
名前の通り死んでいるけど完全には消えていない状態を指す。
通常、子プロセスが終了すると 終了ステータス やリソース情報(CPU、メモリ、ファイルディスクリプタ など)がカーネルに保存される。親プロセスは システムコール wait()
や waitpid()
を呼び出し、プロセステーブルから子プロセスの情報を削除する必要がある。
親プロセスが子プロセスの終了を認識できず、wait()
や waitpid()
を呼び出さない場合、カーネルはプロセステーブルの子プロセスの情報を消去することができず、ゾンビプロセスが発生する。
親プロセスが終了すると、ゾンビプロセスは init
(または systemd
) に引き取られ、自動的に wait()
されて消える。そのため、ゾンビプロセスが残るのは親プロセスが生きている間だけとなる。
$ ps
コマンドで確認すると、プロセスの状態(STAT
)が Z
、COMMAND
が defunct
で表示される。
$ 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 になることが多い。
$ ps -o pid,pgid,comm
$ ps
process status
現在実行中のプロセスを表示する。
オプションなしの場合、現在ログイン中のユーザが起動中のプロセス一覧が表示される。
$ ps
項目 | 意味 |
---|---|
PID |
プロセスID |
PPID |
親プロセスのプロセス ID |
TTY |
制御端末(標準入出力 が接続された 端末) |
TIME |
プロセスが CPU を使用した時間 |
CMD |
実行中のシェルもしくはコマンド |
PGID |
プロセスグループ |
PRI |
プロセスの優先度($ nice 、$ renice ) |
NO |
プロセスの nice 値($ nice 、$ renice ) |
%CPU |
CPU 使用率 |
%MEM |
メモリ使用率 |
$ ps ax
$ ps -ef
$ ps r
$ ps u
$ ps U ユーザ名
$ ps -u UID
$ ps -C コマンド名
$ ps -p PID
$ ps -pid PID
$ ps -l
$ ps e
$ ps x
$ pstree
$ pstree
$ pgrep
$ pgrep 検索ワード
$ grep
は 標準入力 やファイルから検索ワードを探すのに対して、$ pgrep
は実行中のプロセスから検索ワードを探す。
正規表現 を使った検索が可能。
$ pgrep -u ユーザ名 検索ワード
$ pgrep -g グループ名 検索ワード
$ nice
プロセスのスケジューリング優先度(priority)を変更してコマンドを実行する。
優先度は $ ps -l
で確認することができる。
ただし、直接、優先度を変更するのではなく nice 値(ナイス値)を変更することにより相対的に変更する。nice値は -20
~ 19
までの範囲で設定することができる。
- nice 値が高い → 優先度が低い
- nice 値が低い → 優先度が高い
nice 値のデフォルト値は 0
だが、$ nice
を使用した場合のデフォルト値は 10
である。
$ nice コマンド
-n
オプションにより、変更する nice 値を指定できる。
$ nice -n 10 コマンド
マイナス値は root
ユーザのみ設定できる。
$ sudo nice -n -5 コマンド
$ renice
実行中のプロセスの nice 値を変更する。
$ renice -n nice値 -p PID
nice 値を下げることができるのは root
ユーザのみ。
$ ulimit
user limit
プロセスが使用できるリソース量を制限するためのコマンド。
$ ulimit -a
$ ulimit -f
$ ulimit -f サイズ
$ ulimit -n
$ ulimit -n 数
シグナル
- プロセス → プロセス
- カーネル → プロセス
という方向の非同期通信の一つで、特定のイベントをプロセスに対して通知するための仕組み。
シグナルが送られると、プロセスは処理の途中であっても事前に定義された特定の動作を実行する。C 言語でこのシグナル受信時の処理をカスタマイズ実装することができるが、通常はデフォルトの動作が実行される。
またショートカットキー(Ctrl
+ C
など)で送信されるシグナルは フォアグラウンドジョブ にのみ送信される。
シグナルの種類は $ kill -l
で確認することができる。
$ 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) | シェルが起動している 端末(TTY)が閉じられると、シェルが HUP を受け取り、そのシェル内で実行中のプロセスにも HUP が送られ、デフォルト動作としてプロセスが終了する。設定ファイルの再読み込みに利用される。 |
2 |
INT |
終了(Interrupt) |
Ctrl + C による割り込みプロセスの終了 |
3 |
QUIT |
コアダンプ |
Ctrl + \ によるプロセスの終了 |
9 |
KILL |
強制的な終了 | リソースの解放を省略してプロセスが即時終了する ※ 無視できない |
15 |
TERM |
正規の終了(Terminate) | プロセス終了前にリソースの解放が行われる |
18 |
CONT |
再開(Continue) | 一時停止中プロセスの再開 |
19 |
STOP |
強制的な一時停止 | 再開時は CONT シグナルが必要※ 無視できない |
20 |
TSTP |
停止(Terminal Stop) |
Ctrl + Z による割り込みシェルがプロセスをバックグラウンドへ移動させて一時停止させるが、 $ fg や $ bg で再開できる |
※無視できない=プロセスがこのシグナルをキャッチして処理することはできない
キー操作 | 意味 |
---|---|
Ctrl + C
|
割り込みによるプロセス終了 |
Ctrl + Z
|
割り込みによるプロセス一時停止 |
シグナル名はプレフィックス SIG
付きで定義されているが、省略形を使用できる。
$ kill -TERM プロセスID
$ kill -SIGTERM プロセスID
内部コマンド $ kill
と 外部コマンドの $ kill
$ kill
コマンドには以下の2種類が存在する。
- ビルトインコマンド(内部コマンド)としての
$ kill
- 外部コマンドとしての
$ kill
それぞれのコマンドは以下の違いを持つ。
-
ビルトインコマンド
-
bash
やzsh
に代表される、シェルに組み込まれたコマンド - シグナル送信先のジョブID(
%ジョブID
)を指定することができる
-
-
外部コマンド
- 実行可能ファイル
/bin/kill
として存在するコマンド - シェルが持つジョブ管理機能とは連携しないため、ジョブIDを利用できない(ジョブはシェルが管理するプロセス)
- 実行可能ファイル
$ type
コマンドを使用すると、明示的に指定しない場合にどちらが利用されるのかがわかる。
$ type kill
kill is a shell builtin
外部コマンドを使用したい場合、絶対パスで実行する方法と $ command
を利用する方法がある。
$ /bin/kill プロセスID # 絶対パスで指定
$ command kill プロセスID # command を利用
$ kill
$ kill プロセスID
$ kill プロセスID1 プロセスID2 ... # 複数指定可
$ kill %ジョブID
$ kill -プロセスグループID
$ kill -シグナル プロセスID
$ kill -シグナル番号 プロセスID
$ kill -s シグナル プロセスID
親プロセスを終了させた場合、子プロセスについても終了される。
コマンドの内部ではプロセスに対してシグナルが送信されている。以下のように直接シグナルを指定することもできる。
$ kill -STOP プロセスID
$ kill -s STOP プロセスID
シグナルには数字が紐づいているため、数字を指定しても良い。
$ kill -19 プロセスID # 19:STOP
一般的に、プロセスの強制終了はリソースの解放よりも優先されるため、システムが異常を起こす可能性がある。そのため、先に $ kill -TERM プロセスID
を実行して、それでも終了できない等、やむを得ない場合にのみ $ kill -KILL プロセスID
を実行した方が良い。
シグナルを省略した場合は TERM
シグナルを指定したことになる。
$ kill プロセスID
$ kill -TERM プロセスID # kill プロセスID と同じ
-
を PGID の前につけることで、プロセスグループ に対してシグナルを送信することもできる。
$ kill -TERM -プロセスグループID
$ killall
$ kill
コマンドでは、シグナル送信先をプロセスID(PID)で指定できたのに対して、$ killall
コマンドでは、プロセス名を使って指定することができる。
同じ名前のプロセスが複数存在する場合に便利(PID は重複しない)。
$ killall -シグナル プロセス名
$ killall -s シグナル プロセス名
シグナルの指定については $ kill
と同様で、省略した場合は TERM
シグナルを指定したことになる。
$ killall プロセス名
$ killall -TERM プロセス名 # killall プロセス名と同じ
$ killall -u ユーザ名
$ pkill
シグナルを送信するプロセスを「$ pgrep
でヒットしたプロセス」に絞り込んだ上で kill することができる。
$ pkill -シグナル 検索ワード
シグナルの指定については $ kill
と同様で、省略した場合は TERM
シグナルを指定したことになる。
$ pkill 検索ワード
$ pkill -TERM 検索ワード # pkill 検索ワード と同じ
$ pkill -u ユーザ名
ジョブ
単一または複数のプロセスで構成される、一連の処理単位。
複数のコマンドを パイプ で実行した場合も、一連の処理が一つのジョブとされる。
$ コマンド1 | コマンド2 # コマンド1と2を合わせたものがジョブ
シェル によって管理されていて、それぞれのジョブはジョブID(JID)で識別される。
JID はセッション内で 1
から順に割り振られるため、異なるシェルセッション同士で JID が重複することもある。
JID を使用する際は、プロセスID(PID)と区別するために %
が接頭語として使用される。
$ kill %ジョブID # ジョブを kill する
$ kill プロセスID # プロセスを kill する
ジョブにはフォアグラウンドで実行されるものとバックグラウンドで実行されるものがある。
ジョブはシェルによって管理される
ジョブの実態
ジョブの実態は プロセスグループ である。
プロセスグループは OS によって管理され、ジョブはシェルによって管理される。
複数のプロセスで構成されるジョブの場合、最初に実行されるプロセスの PID がそのジョブを代表する PID として扱われ、PGID にはこの PID が使用される。
この最初に実行されるプロセスをジョブの リーダー と表現することがあり、ジョブのリーダーの PID は、そのジョブの実態であるプロセスグループの PGID と一致する。
ジョブの実態はプロセスグループ
ジョブID(JID)がシェル内部で使用される識別子であることを理解すると、外部コマンドの $ kill
(/bin/kill
) が JID を指定して実行できないことも自然と理解できる(外部コマンドは JID を直接取得することができない。外部コマンドが取得できる JID は、システム全体で共有される、例えば ビルトインシェル変数 などの情報に限られるため)。
フォアグラウンドジョブ
フォアグラウンドで実行中のジョブがある場合、「シェルの 標準入力」が「実行中のジョブ」に接続された状態となり、シェルの標準入力が占有されるため、シェルはキーボード入力を受け付けない(ターミナルアプリ上で入力した文字がシェルに渡されない)。
フォアグランド中で実行中のジョブは Ctrl
+ C
で終了させたり、Ctrl
+ Z
でバックグラウンドに送り、一時停止させることができる。
バックグラウンドジョブ
バックグラウンドで実行中のジョブがあっても、シェルの標準入力は端末に接続された状態となるため、ターミナルアプリ上のコマンド入力は、通常通りシェルに渡すことができる。
バックグラウンド = 停止
ではなく、バックグラウンドで実行中のジョブもあれば、バックグランドで一時停止中のジョブも存在する。
バックグランドで一時停止中のジョブについては $ fg
もしくは $ bg
で再開することができる。
バックグラウンドジョブも 標準出力 が 端末 に接続されている場合があるため、この場合、フォアグラウンド実行中のジョブの出力内容とバックグランドで実行中のジョブの出力内容が混ざることがある。
過去にバックグラウンドで実行されたコマンドのうち、最後に生成されたプロセスのプロセスIDはビルトインシェル変数の $!
に格納されている。
$ echo $!
&
コマンドの末尾に $
をつけることで、ジョブをバックグラウンドで実行させることができる。
$ コマンド &
$ jobs
実行中、もしくは一時停止中のジョブID(JID)を表示する。
$ jobs
[JID]+ 状態(Running/Stopped) [ジョブの内容] # 実行中のジョブ
[JID]- 状態(Running/Stopped) [ジョブの内容] # 直前のジョブ
[JID] 状態(Running/Stopped) [ジョブの内容]
-l
でプロセスID(PID)も併せて表示する。
$ jobs -l
$ bg
background
一時停止中のジョブをバックグラウンドで再開する。
$ bg %ジョブID
ジョブIDを省略した場合、最後に一時停止したジョブが対象になる。
$ fg
foreground
一時停止中のジョブをフォアグラウンドで再開させる。
または、バックグラウンドで実行中のジョブをフォアグラウンドで実行させる。
$ fg %ジョブID
ジョブIDを省略した場合、最後に操作したジョブ(バクグラウンドのものを含む)が対象になる。
$ disown
own=所有する
現在のジョブリスト($ jobs
)から特定のジョブを削除し、特定のジョブをシェルの管理下から切り離す。
管理対象外とすることで HUP
シグナルを受信しなくなるため、ユーザがログアウトしてもプロセスが終了しなくなる。
$ diswon %ジョブID
タスク
カーネル のスケジューラによって管理される、処理のスケジューリング単位。
(ただしプロセスやスレッドを含む広い概念であり、文脈によって意味が異なる場合がある)
CPU の処理単位としてスケジューリングされ、以下の状態間を遷移する。
- 実行可能状態(
READY
) - 実行状態(
RUN
) - 待機状態(
WAIT
)
Linux では プロセス とほぼ同義で使用される。
スレッド
一つのプロセス内で実行される処理の実行単位。
一つのプロセス内のスレッド同士は、プロセス内でヒープ領域と呼ばれるメモリ空間を共有するが、スタックと呼ばれるメモリ空間については各スレッドが独立して保持する。ただし正確には、スタックはスレッドごとに確保されるものの、他のスレッドのスタックにアクセスすることも技術的には可能である。
(Java のスレッドについては こちら)
サービス / デーモン
メモリ上に常駐することで、バックグラウンド上で特定の機能を提供するプロセス。
通常は SysVinit や systemd によって管理され、自動的に起動されるが、$ systemctl
や $ service
コマンドによって起動、停止、再起動などの制御を行うことも可能。
ジョブやタスクはコマンド実行時に起動し、処理終了と共に終了するため、継続的に動作し続けるサービス、デーモンとは異なる。
サービスとデーモンの違い
Linux では デーモン はバックグラウンドで動作し、制御端末を持たない プロセスを指す。名前の末尾に d
の付くものが多い。
サービス は特に init
システムによって管理されるプロセスを指す。デーモンはサービスに含まれる概念であるが、単発のジョブ(Type=oneshot
)もサービスとして管理されることがある。
特に systemd
では ユニット という概念があり、その中の「サービスユニット(*.service
)」が狭義の「サービス」にあたる(ただし、ソケットユニット(*.socket
)やタイマーユニット(*.timer
)もサービスの一部として機能する場合がある)。
主なデーモン
- Webサーバ
httpd
apache2
nginx
- DBサーバ
mysqld
postgres
mongod
- メールサーバ
postfix
exim
sendmail
- SSHサーバ
sshd
- ジョブスケジューラ
デーモンは通常 init
(または systemd
)を直接、または間接的な親プロセスとするため、ログインユーザとは独立したプロセスツリーを持つ(プロセスツリーは $ pstree
で確認できる)。
プロセスには、「親プロセスが終了したら子プロセスも連動して終了する」特徴があるが、システムの起動時に自動起動したデーモンは ユーザが起動するプロセスとは親子関係にない ため、ユーザがログアウトしても連動して終了することがない。
標準入出力の扱い
デーモンのプロセスは 標準入出力 を /dev/null
に接続しているため、制御端末(TTY)を持たない。
ただし、systemd
によって管理されるサービスは journald
にログを記録するため、標準出力が /dev/null
にリダイレクトされない場合もある(systemctl status
で確認可能)。
デーモンのように標準入出力に接続されておらず、制御端末を持たないプロセスは、$ ps aux
コマンドで確認すると TTY
の項目が ?
で表示される。
スーパーサーバ
複数のデーモンを管理する役割を持つ特別なデーモン。
デーモンは通常、常時メモリ上にいる ため、その分リソースを消費する。
スーパーサーバは必要に応じて適切なデーモンを起動することでリソースを節約することができる。一方で、要求を受けてからデーモンが起動するため、応答が遅くなるデメリットがある。
代表的なスーパーサーバには 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/
配下の設定ファイルでサービス固有の設定を行う - アクセス制御が可能
- リソースの使用量制限が可能