「linuxのしくみ」を読む 第2章
第2章 プロセス管理(基礎編)
プロセスの生成
同一プログラムの処理を複数のプロセスに分けて処理する(例: webサーバの複数リクエスト受付)パターンと別のプログラムを生成する(例: bashから各種プログラムの新規生成)パターンがある。
fork()関数
fork()関数を発行すると、プロセスのコピーが作られ、どちらもfork関数から復帰させる。親プロセスと子プロセスになる。
fork()関数の返り値は、親プロセスなら子プロセスのpid(必ず0より大きい)が、子プロセスなら0が返るので、返り値のハンドリングで親プロセスと子プロセスで処理を分岐できる。
execve()関数
execve()関数では、現在のプロセスのメモリを新たなプロセスのメモリに置き換える。(メモリマップという)
実行ファイルは複数の領域に分けられていて、セクションと呼ぶ。コードセクション、データセクションがある
ASLR(Address Space Layout Randomization)
プログラムを実行するたびに、違うメモリアドレスにマップする。これによって攻撃対象が特定のアドレスに存在することを前提とした攻撃を防げる。
Ubuntu 20.04ではデフォルトで有効。 ASLR対応のプログラムをPIE(Position Independent Executable)という。-no-pie
オプションでビルドすればPIEを無効化できる。
プロセスツリー
pstree
コマンドで見れる。-p
オプション付けるとPIDも表示できる。
pid=1のinitプロセスが根になっている。
プロセスの状態
ps aux
のSTATフィールドで状態がわかる。
- S: スリープ状態
- R: 実行可能状態/実行状態
- Z: ゾンビ状態(プロセス終了後になる)
プロセスの終了
exit_group()
というシステムコールを呼び出している。pythonのexit()関数は内部でこのシステムコールを呼び出している。exit_group()
の中でカーネルはメモリなどのプロセスのリソースを回収する。
子プロセスが終了すると、親プロセスがwait()
やwaitpid()
を呼び出すことで、子プロセスのプロセスの戻り値、シグナルによる終了かどうか、終了までのCPU時間を得られる。(親プロセスがハンドリングできる)
ゾンビプロセスと孤児プロセス
子プロセスが終了したが、親プロセスがその終了状態を得ていないとき、この子プロセスはゾンビプロセスという。親プロセスは子プロセスの終了状態を適宜チェックしていて、ゾンビプロセスで溢れないようになっている。ゾンビプロセスが大量に存在している時は親プロセスのバグかもしれない。
親プロセスがwait()系システムコールを呼ぶ前に終了した場合、子プロセスは親がいないので孤児プロセスになる。カーネルはinitを孤児プロセスの親にする。
シグナル
シグナルとはプロセスが別のプロセスの実行の流れを強制的に変えるためのしくみ。
- SIGINT:
ctrl + c
.これを受け取ったプロセスはそのまま終了する - SIGCHLD: 子プロセス終了時に親に送られる。このシグナルが送られてきてwait()系システムコールを呼ぶようにハンドリングするのが一般的。
- SIGSTOP: プロセスの実行を止める。
ctrl + z
- SIGCONT: SIGSTOPなどで一時停止中のプロセスを再開する
- SIGKILL: このシグナルを受け取ったプロセスは必ず終了させられる。シグナルハンドラでも挙動は変えられない
シグナルハンドラというシグナルを受け取ってからの処理を登録できる。
ジョブ
ジョブはシェルがバックグラウンドで実行したプロセスを制御するしくみ。&でバックグラウンド処理させる時の[1] xxxx
の1がjob番号。
セッション
セッションはログインセッションに対応する。セッションにはセッションを制御するための端末(ターミナルみたいな)が紐づいている。セッションIDはSID。セッションリーダーというプロセスがいて、それは通常bashなどのシェルになる。セッションリーダーのPIDはSIDに等しい。
ps ajx
でセッションについての情報を見れる。
Discussion