unixカーネルの設計 7 プロセスの制御

61
UNIXカーネルの設計】 7. プロセスの制御

Upload: norito-agetsuma

Post on 10-Jun-2015

1.153 views

Category:

Technology


0 download

DESCRIPTION

Unixカーネルの設計の社内勉強会資料です。 http://www.amazon.co.jp/UNIXカーネルの設計-Maurice-J-Bach/dp/4320025512

TRANSCRIPT

Page 1: Unixカーネルの設計 7 プロセスの制御

【UNIXカーネルの設計】

7. プロセスの制御

Page 2: Unixカーネルの設計 7 プロセスの制御

● プロセスの生成 pid = fork();

● シグナル

● プロセスの終了 exit(status);

● プロセス終了の待合せ pid = wait(stat_addr)

● 他のプログラムの起動 execve(filename,argv,envp)

● プロセスの利用者ID

● プロセスの大きさの変更

● システムの起動とinitプロセス

7章 主な内容

Page 3: Unixカーネルの設計 7 プロセスの制御

プロセスの生成

pid = fork();

Page 4: Unixカーネルの設計 7 プロセスの制御

fork システムコール

● UNIXにおいてユーザがプロセスを作る唯一の方法

● 親 → 子にコンテキストがコピーされるのが特徴

fork()

親プロセス 子プロセス

u区域

ユーザのFD表

タイマ(実行時間等)

カレントディレクトリ

エラーフィールド

プロセス表へのポインタ

カーネルスタック

u区域

ユーザのFD表

タイマ(実行時間等)

カレントディレクトリ

エラーフィールド

プロセス表へのポインタ

カーネルスタック

【復習】※1 u区域は、プロセス表とともにプロセスの状態について保持している。p129 を参照。※2 プロセス固有の領域表は、プロセス内の各領域(テキスト、データ、スタックなど)の開始アドレスを保持する。p131 を参照。

p領域(プロセス固有の領域表)

text data stack

親プロセスデータ

親プロセススタック

共有テキスト

p領域(プロセス固有の領域表)

text data stack

子プロセスデータ

子プロセススタック

fork時に各領域をコピー

テキストのように、共有できるものはする

Page 5: Unixカーネルの設計 7 プロセスの制御

fork の処理順序1: 領域の割り当て

kernel

親プロセス(pid1000)

他プロセス 子プロセス(pid1001)

仮想メモリ

1. 領域を割り当て、プロセスIDを付ける。

Page 6: Unixカーネルの設計 7 プロセスの制御

fork の処理順序2: プロセス表の割り当て

kernel

親プロセス(pid1000)

他プロセス 子プロセス(pid1001)

仮想メモリ

2. プロセス表の空きを確認し、確保して初期化。 デッドロックを防ぐため、一般ユーザは最後の空き項目は 使えない。スーパーユーザだと使える。

state pid size ...

S 982 xxx

R 1000 xxx

initial 1001 xxx

プロセス表

【復習】※1 プロセス表の各項目については、p129 6.1 プロセスの状態とその遷移 を参照。

Page 7: Unixカーネルの設計 7 プロセスの制御

fork の処理順序3: コンテキスト複写

親プロセス(pid1000)

他プロセス 子プロセス(pid1001)

仮想メモリ

state pid size ...

R 1000 xxx

initial 1001 xxx

プロセス表

u区域

ユーザのFD表

プロセス表へのポインタ

カーネルスタック

p領域

text data stack

親プロセスデータ

親プロセススタック

...

u区域

ユーザのFD表

プロセス表へのポインタ

カーネルスタック

p領域

text data stack

子プロセスデータ

子プロセススタック

...

共有テキスト

利用者スタック 利用者スタック

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

kernel 3. 親から子へコンテキストをコピーする。 スタックのコピーは、fork()実行しているスタックから。

Page 8: Unixカーネルの設計 7 プロセスの制御

fork の処理順序4: iノードの更新

親プロセス(pid1000)

他プロセス 子プロセス(pid1001)

仮想メモリ

state pid size ...

R 1000 xxx

initial 1001 xxx

プロセス表

【復習】※1 ファイル表、iノード表については 5 ファイルシステム用のシステムコールを参照。

u区域

オープンされたファイル

現ディレクトリ

カーネルスタック

p領域

text data stack

親プロセスデータ

親プロセススタック

変更されたルート.

u区域

オープンされたファイル

現ディレクトリ

カーネルスタック

p領域

text data stack

子プロセスデータ

子プロセススタック

変更されたルート

共有テキストファイル表 iノード表

... ...xxx(参照数1+1)

yyy(参照数1+1)

... ...現ディレクトリ(参照数1+1)

chrootしたdir(参照数1+1)

4. ファイル表の参照値を+1する。親子は同じファイル表項目を参照する。 iノード表は、現ディレクトリとおよびchrootした場合はルートに+1する。

利用者スタック 利用者スタック

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

Page 9: Unixカーネルの設計 7 プロセスの制御

fork の処理順序5: 実行可にしてfork()終了

親プロセス(pid1000)

他プロセス 子プロセス(pid1001)

仮想メモリ

state pid size ...

R 1000 xxx

R 1001 xxx

プロセス表

u区域

オープンされたファイル

現ディレクトリ

カーネルスタック

p領域

text data stack

親プロセスデータ

親プロセススタック

変更されたルート.

u区域

オープンされたファイル

現ディレクトリ

カーネルスタック

p領域

text data stack

子プロセスデータ

子プロセススタック

変更されたルート

共有テキストファイル表 iノード表

... ...xxx(参照数2)

yyy(参照数2)

... ...現ディレクトリ(参照数2)

chrootしたdir(参照数2)

利用者スタック 利用者スタック

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

実行可

実行可

Page 10: Unixカーネルの設計 7 プロセスの制御

fork() を使ったプログラムの流れ

main () { int stat_addr;

if (fork() == 0) { printf(...); exit(0); } wait(&stat_addr);}

Page 11: Unixカーネルの設計 7 プロセスの制御

main () { int stat_addr;

if (fork() == 0) { printf(...); exit(0); } wait(&stat_addr);}

fork() を使ったプログラムの流れ

親プロセス

Page 12: Unixカーネルの設計 7 プロセスの制御

main () { int stat_addr;

if (fork() == 0) { printf(...); exit(0); } wait(&stat_addr);}

fork() を使ったプログラムの流れ

親プロセス

子プロセス

Page 13: Unixカーネルの設計 7 プロセスの制御

main () { int stat_addr;

if (fork() == 0) { printf(...); exit(0); } wait(&stat_addr);}

fork() を使ったプログラムの流れ

子プロセス

親プロセス(待ち)

fork()は親プロセスには、子プロセスのpidを返す。子プロセスには 【0】 を返す。

Page 14: Unixカーネルの設計 7 プロセスの制御

main () { int stat_addr;

if (fork() == 0) { printf(...); exit(0); } wait(&stat_addr);}

fork() を使ったプログラムの流れ

子プロセス

親プロセス(待ち)

子プロセスが exit() を実行して終了すると

Page 15: Unixカーネルの設計 7 プロセスの制御

main () { int stat_addr;

if (fork() == 0) { printf(...); exit(0); } wait(&stat_addr);}

fork() を使ったプログラムの流れ

親プロセス(終了)

waitの引数アドレスに子プロセスの終了コードを設定してwaitから復帰します。

Page 16: Unixカーネルの設計 7 プロセスの制御

シグナル

Page 17: Unixカーネルの設計 7 プロセスの制御

シグナル

● 非同期に発生した事象をプロセスに伝える手段。

● プロセス間の通信や、カーネルからの異常通知に使う。

プロセス

SIGHUP (1)(ハングアップ検出)

SIGINT (2)(キーボード割り込み)

SIGQUIT (3)(キーボードによる中止)

SIGABRT (6)(アボート)

SIGKILL (9)(kill シグナル)

SIGSEGV (11)(メモリ不正参照)

SIGUSR1 (30,10,16)(ユーザ定義シグナル)

SIGCHLD (20,17,18)(子プロセスの一時停止または終了) signal

参考: POSIX標準シグナルhttp://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/signal.7.html

シグナル処理

Page 18: Unixカーネルの設計 7 プロセスの制御

シグナルをチェックするタイミング

● 主にカーネルモード実行中から遷移する場合にチェック

● 利用者モード実行中の場合は、割込みを発生させてからチェック

2

1

3

7

4

9

CPU割り当て保留状態

ここから下はスワップ関連の状態

利用者モード実行中

カーネルモード実行中

ゾンビ状態

メモリ上休眠状態

メモリ上実行可能状態

終了 (exit)

割込み・割込み復帰

利用者モード復帰

休眠プロセスの再スケジュール

CPU横取り

復帰

システムコール割込み

目覚め

Page 19: Unixカーネルの設計 7 プロセスの制御

シグナルフィールド

● 受信したシグナルを示すフィールド

● シグナルチェックのタイミングでの確認対象

● 同じシグナルを複数回受信したかは保持できない

u区域

カーネルスタック

p領域

text data stack

プロセスデータ

プロセススタック

利用者スタック

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

ユーザのFD表

プロセス表へのポインタ

state pid size シグナルフィールド

S 982 xxx

S 1000 xxx

R 1001 xxx

プロセス表

xxx

シグナルを受信したかは、プロセス表のシグナルフィールドに格納

Page 20: Unixカーネルの設計 7 プロセスの制御

シグナルの操作

● exit を実行してプロセスが終了する (SIGABRT/SIGKILLなど)

● 無視する (何も設定していないSIGUSR1など?)

● ユーザ関数を実行する (システムコールsignalで設定)

シグナルの操作には大きく分けて3つの種類がある

システムコール signal

oldfunction = signal(signum, function);

元々登録されていた関数のアドレス

シグナル番号 実行したい関数

Page 21: Unixカーネルの設計 7 プロセスの制御

シグナル処理順序1: プロセス表の確認と初期化

kernelstate pid size シグナルフィールド

S 982 xxx

S 1000 xxx

R 1001 xxx

プロセス表

xxxxxx

Core

特定のシグナルではcoreを出力し、それ以外はプロセス終了する。

チェック

exit

シグナル関数に『0』が設定

exit または

『1』が設定

無視する

『関数』が設定

シグナル処理関数を実行する

次ページ以降に続く

シグナルを受け取っていた場合、主に3種類の対処がある。

Page 22: Unixカーネルの設計 7 プロセスの制御

シグナル処理順序2: 利用者スタックフレームの生成

kernel

利用者スタック

カーネルスタック

ローカル変数・関数の引数

呼出し後の戻り番地(レジスタコンテキスト)

プログラムカウンタ

PSレジスタ

スタックポインタ

1. レジスタコンテキストにアクセス

2. 新規フレームを生成(シグナルに対応した関数実行用)

プログラムカウンタ

PSレジスタ

スタックポインタ

コピーされたレジスタコンテキスト

func1()

signalfunc()

次に実行する関数をシグナルハンドラ関数に書き換えるために、スタックフレームを追加して、追加したフレームに元の戻り先をコピーする。

※この時点ではプログラムカウンタの実行先はfunc1のまま

Page 23: Unixカーネルの設計 7 プロセスの制御

シグナル処理順序3: PCとスタックポインタを変更

3. プログラムカウンタ → シグナル操作関数 スタックポインタ → 追加したフレーム

kernel

利用者スタック

カーネルスタック

ローカル変数・関数の引数

呼出し後の戻り番地(レジスタコンテキスト)

プログラムカウンタ

PSレジスタ

スタックポインタ

1. レジスタコンテキストにアクセス

プログラムカウンタ

PSレジスタ

スタックポインタ

コピーされたレジスタコンテキスト

func1()

signalfunc()

PCをシグナルハンドラ関数の命令番地に変更し、スタックポインタは先ほどコピーしたフレームを指すように変更する。

【割込み処理】のようにして、利用者スタックでシグナルに対応した関数を実行

Page 24: Unixカーネルの設計 7 プロセスの制御

プロセスグループとは

● 同じログインシェルの場合などにプロセスをグループ化する

● 同一のシグナルを、プロセスグループに対して投げるために使う

login

bash

./application_a

application_a1

application_a2

login

bash

プロセスグループ1

プロセスグループ2

端末回線のハング等によるシグナルはグループ内全てのプロセスに送る。

Page 25: Unixカーネルの設計 7 プロセスの制御

シグナルの送出

システムコール kill

kill (pid, signum);プロセス番号 シグナル番号

● pid が整数の場合は、pid のプロセスにシグナルを送る

● pid が0の場合は、シグナルを送り主と同じプロセスグループに送る

● pid が-1の場合は、シグナル送り主の実行利用者IDと同じ利用者IDを

持つプロセスに送る

● pid が-1以外の負数の場合は、絶対値と等しいプロセスグループに送る

Page 26: Unixカーネルの設計 7 プロセスの制御

プロセスの終了

Page 27: Unixカーネルの設計 7 プロセスの制御

exit の実行

システムコール exit

exit(status)親プロセスに返す終了コード

● プロセスはゾンビ状態に遷移し、資源を解放する。

● status の値は親プロセスに返される。

Page 28: Unixカーネルの設計 7 プロセスの制御

exit の処理順序1: シグナル処理の停止

1. プロセスのシグナル処理を禁止する → 終わるプロセスのシグナルを処理しても意味がない

Page 29: Unixカーネルの設計 7 プロセスの制御

exit の処理順序2: グループにSIGHUP送出

1. プロセスのシグナル処理を禁止する

2. プロセスグループの代表の場合は、グループのプロセスに

【ハングアップ】シグナルを送る。

代表 子

exit()

SIGHUP

例: loginなど

端末に対応付いた全てのプロセスを終了させる

Page 30: Unixカーネルの設計 7 プロセスの制御

exit の処理順序3: オープン中のファイルをclose()

1. プロセスのシグナル処理を禁止する

2. プロセスグループの代表の場合は、グループのプロセスに

【ハングアップ】シグナルを送る。

3. オープンされているファイル識別子を調べ、close()によりクローズする。

u区域

ファイル識別子(FD)

カーネルスタック

p領域

text data stack

親プロセスデータ

親プロセススタック

利用者スタック

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

FD表

0

1

ファイル表 iノード表

参照数1 → 0

参照数1 → 0

/etc/...参照数2→1

/etc/...参照数3→1

プロセス

Page 31: Unixカーネルの設計 7 プロセスの制御

exit の処理順序4: カレントディレクトリファイルのiノード解放

1. プロセスのシグナル処理を禁止する

2. プロセスグループの代表の場合は、グループのプロセスに

【ハングアップ】シグナルを送る。

3. オープンされているファイル識別子を調べ、close()によりクローズする。

4. iputアルゴリズムによって、プロセスの現ディレクトリとchrootしたiノードを解放する

u区域

プロセスの現ディレクトリ

カーネルスタック

p領域

text data stack

親プロセスデータ

親プロセススタック

利用者スタック

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

iノード表

/home/test参照数2→1

変更したルートディレクトリ

プロセス

u区域の現ディレクトリを元に、カレントディレクトリを示すファイルのiノード参照数を-1する

Page 32: Unixカーネルの設計 7 プロセスの制御

exit の処理順序5: メモリ解放をゾンビ状態への遷移

1. プロセスのシグナル処理を禁止する

2. プロセスグループの代表の場合は、グループのプロセスに

【ハングアップ】シグナルを送る。

3. オープンされているファイル識別子を調べ、close()によりクローズする。

4. iputアルゴリズムによって、プロセスの現ディレクトリとchrootしたiノードを解放する

5. detachreg アルゴリズムによって、利用者メモリを全て解放し、プロセスをゾンビ状態にする。

2

1

79

利用者モード実行中

カーネルモード実行中

ゾンビ状態

終了 (exit)

割込み・割込み復帰

利用者モード復帰

CPU横取り

復帰

システムコール割込み

CPU割り当て保留状態

Page 33: Unixカーネルの設計 7 プロセスの制御

exit の処理順序6: 統計情報の記録

1. プロセスのシグナル処理を禁止する

2. プロセスグループの代表の場合は、グループのプロセスに

【ハングアップ】シグナルを送る。

3. オープンされているファイル識別子を調べ、close()によりクローズする。

4. iputアルゴリズムによって、プロセスの現ディレクトリとchrootしたiノードを解放する

5. detachreg アルゴリズムによって、利用者メモリを全て解放し、プロセスをゾンビ状態にする。

6. 統計情報を書き出す

● プロセスおよび子孫の利用者モード・カーネルモードでの実行時間

● ユーザIDとCPU、メモリの使用状況、I/Oの実行回数等など

かつてのUNIXはコンピュータをみんなで使って、後で請求するために必要だった。

Page 34: Unixカーネルの設計 7 プロセスの制御

exit の処理順序7: プロセスツリーから除去

1. プロセスのシグナル処理を禁止する

2. プロセスグループの代表の場合は、グループのプロセスに

【ハングアップ】シグナルを送る。

3. オープンされているファイル識別子を調べ、close()によりクローズする。

4. iputアルゴリズムによって、プロセスの現ディレクトリとchrootしたiノードを解放する

5. detachreg アルゴリズムによって、利用者メモリを全て解放し、プロセスをゾンビ状態にする。

6. 統計情報を書き出す

7. 対象のプロセスをプロセスツリーから取り除く。

R

R

Z

exit()したプロセスinit (pid 1)

R

R

実行中の子プロセス群

● 実行中の子プロセスは、initの子になる。

● 既にゾンビ状態であった子プロセスは削除される。

ゾンビ状態

Page 35: Unixカーネルの設計 7 プロセスの制御

プロセス終了の待合せ

Page 36: Unixカーネルの設計 7 プロセスの制御

wait の実行

システムコール wait

pid = wait(stat_addr)stat_addrは、子プロセスの

終了コードを格納する整数ポインタ

● 子プロセス終了との同期を取る為に使う。

● 子プロセスにゾンビ状態がいない場合、休眠する。

● 子プロセス終了シグナルによって、休眠より目覚め、

子プロセスのプロセス表領域の解放処理を行う。

ゾンビ状態となった子プロセスのPID

Page 37: Unixカーネルの設計 7 プロセスの制御

wait の処理順序1: ゾンビ子プロセスの確認と休眠

1. ゾンビ子プロセスがなければ、休眠状態となる。

S R

S

S

wait()いずれの子プロセスも実行中または休眠中

Page 38: Unixカーネルの設計 7 プロセスの制御

wait の処理順序2: ゾンビ状態の発生とシグナル送出

S R

S

Z

wait()

exit()によりゾンビ状態へ

1. ゾンビ子プロセスがなければ、休眠状態となる。

2. 子プロセスがexit() を実行してゾンビ状態となり、子プロセス終了シグナルを親に投げる。

子プロセス終了シグナル

Page 39: Unixカーネルの設計 7 プロセスの制御

wait の処理順序3: 親プロセスの目覚めと子プロセス解放

R R

S

Z

wait()

1. ゾンビ子プロセスがなければ、休眠状態となる。

2. 子プロセスがexit() を実行してゾンビ状態となり、子プロセス終了シグナルを親に投げる。

3. wait()していた親プロセスは目覚め、ゾンビ状態の子プロセスのプロセス表項目を解放する。

● 子プロセスのCPU使用量を親プロセスに加える

● 子プロセスのプロセス表の項目を解放する

Page 40: Unixカーネルの設計 7 プロセスの制御

wait の処理順序4: wait()が応答を返す

R R

S

Z

wait()

1. ゾンビ子プロセスがなければ、休眠状態となる。

2. 子プロセスがexit() を実行してゾンビ状態となり、子プロセス終了シグナルを親に投げる。

3. wait()していた親プロセスは目覚め、ゾンビ状態の子プロセスのプロセス表項目を解放する。

4. 子プロセスIDと、子プロセスの終了状態コードをwait() 呼び出し元に返す。

pid: 999, exit status: 0

pid = wait(stat_addr);999 0

Page 41: Unixカーネルの設計 7 プロセスの制御

他のプログラムの起動

Page 42: Unixカーネルの設計 7 プロセスの制御

execve の実行

システムコール execve

execve(filename, argv, envp)実行ファイル名 引数 環境変数

● プロセスのメモリ空間を実行可能ファイルに置き換える

● 元の利用者レベルコンテキストは上書きされる(テキスト、データ、スタック等)

Page 43: Unixカーネルの設計 7 プロセスの制御

execve の処理順序1: 実行可能ファイルヘッダの読み込み

1. iノードを割り当て、対象ファイルが実行可能であることを確認し、ヘッダを読み込む。 ヘッダから対象ファイルがロードモジュールか確認する。

マジック数区分の個数

レジスタの初期値

区分の型区分の大きさ仮想アドレス

.

.

基本ヘッダ

区分1ヘッダ

区分1 テキスト

データ区分2..

その他の情報区分n

ヘッダ部分

【マジック数】実行可のファイルの型を示す。例えば 『0x7f 'E' 'L' 'F'』 など(ELF形式)

実行可能ファイルの形式

Page 44: Unixカーネルの設計 7 プロセスの制御

execve の処理順序2: exec引数の避難

1. iノードを割り当て、対象ファイルが実行可能であることを確認し、ヘッダを読み込む。

2. execの引数を置き換え対象のユーザ空間から避難する。

execve()を実行したプロセス

execve引数

仮想メモリ

引数を退避する

この後、メモリ解放してしまうので、配列argsを避難する必要がある

Page 45: Unixカーネルの設計 7 プロセスの制御

execve の処理順序3: 古い領域の解放

1. iノードを割り当て、対象ファイルが実行可能であることを確認し、ヘッダを読み込む。

2. execの引数を置き換え対象のユーザ空間から避難する。

3. プロセスの古い領域を解放する。

execve引数

仮想メモリ

detachregアルゴリズムによる古い領域の解放

Page 46: Unixカーネルの設計 7 プロセスの制御

execve の処理順序4: 実行可能ファイルのロード

1. iノードを割り当て、対象ファイルが実行可能であることを確認し、ヘッダを読み込む。

2. execの引数を置き換え対象のユーザ空間から避難する。

3. プロセスの古い領域を解放する。

4. 領域を確保し、実行可能ファイルをメモリにロードする。

execve引数

仮想メモリ

実行可能ファイル

.text .data .bss

退避していた引数をbssに格納

Page 47: Unixカーネルの設計 7 プロセスの制御

execve の処理順序5: シグナルのリセットとレジスタ初期化

1. iノードを割り当て、対象ファイルが実行可能であることを確認し、ヘッダを読み込む。

2. execの引数を置き換え対象のユーザ空間から避難する。

3. プロセスの古い領域を解放する。

4. 領域を確保し、実行可能ファイルをメモリにロードする。

5. シグナル受信のリセットと、レジスタを初期化する。 プログラムカウンタは実行ファイルのヘッダに設定された、先頭の命令に設定。

.text .data .bss

u区域

シグナル処理ルーチン

現ディレクトリ

カーネルスタック

p領域

text data stack

親プロセスデータ

親プロセススタック

利用者スタック

プログラムカウンタ

PSレジスタ

スタックポインタ

レジスタコンテキスト

Page 48: Unixカーネルの設計 7 プロセスの制御

execve の処理順序6: iノードの解放

1. iノードを割り当て、対象ファイルが実行可能であることを確認し、ヘッダを読み込む。

2. execの引数を置き換え対象のユーザ空間から避難する。

3. プロセスの古い領域を解放する。

4. 領域を確保し、実行可能ファイルをメモリにロードする。

5. シグナル受信のリセットと、レジスタを初期化する。 プログラムカウンタは実行ファイルのヘッダに設定された、先頭の命令に設定。

6. 実行可能ファイルを開くために割り当てたiノードを iput アルゴリズムで解放

仮想メモリ

.text .data .bss

これで利用者レベルコンテキストの入れ替わり処理が完了。

旧実行ファイル 新実行ファイル

Page 49: Unixカーネルの設計 7 プロセスの制御

テキスト領域とデータ領域を分ける理由1

.text

.data .bss

heap

stack

命令領域への書込をブロックできない

テキストとデータを一緒の領域にした場合

.text

.data

heap

stack

テキストとデータを一緒の領域にした場合

.bss

× 保護領域なので書き込めない

書き込み可能

メモリ保護機構を使って、命令領域への書き込みを保護するため。

Page 50: Unixカーネルの設計 7 プロセスの制御

テキスト領域とデータ領域を分ける理由2

仮想メモリ

.text .data .bss .data .bss

同じテキストであれば共有する

/bin/date 1回目 /bin/date 2回目

● text領域だけを再利用することも理論的には可能

● 再利用を意図して、sticky bit を設定した場合は、 プロセス終了後もtext領域を解放しないことも可能。

※現在のSolaris/FreeBSD/Linuxではsticky bitによってテキストを残すことはないらしい。 メモリ大容量化に伴い、明示的に残さなくても、ページが再利用されずにtextが残る。 http://ja.wikipedia.org/wiki/スティッキービット

Page 51: Unixカーネルの設計 7 プロセスの制御

プロセスの利用者ID

Page 52: Unixカーネルの設計 7 プロセスの制御

実ユーザID(real user ID) と 実効ユーザID (effective user ID)

● プログラムを起動したユーザのID

● ps のUSER列に出てくるいつものユーザID

実ユーザID

実効ユーザID

● ファイルアクセス権の検査

● ファイルアクセスの可否は実効ユーザIDで判断

● kill システムコールでシグナルを送る許可の検査

$ ls -l /bin/ping

-rwsr-xr-x. 1 root root 40769 … ping

Page 53: Unixカーネルの設計 7 プロセスの制御

実効ユーザIDの例 (本書より)

● ユーザIDがrootに設定で、実効ユーザもrootで動く

● setuidで実効ユーザをログインしたユーザに変更してシェルをexecする

● ユーザにシェルが返るときには、実効ユーザはログインユーザになる

login

mkdir

● ディレクトリを作るのにrootが必要なので、実効ユーザがroot

● ディレクトリ作成が終わった後に、実ユーザIDに所有者を変更する

Page 54: Unixカーネルの設計 7 プロセスの制御

プロセスの大きさの変更

Page 55: Unixカーネルの設計 7 プロセスの制御

brk の実行

システムコール brk

brk(endds)プロセスのデータ領域の

最大仮想アドレス値(ブレーク値)

● プロセスのデータ領域を末尾より拡張する。

● メモリの解放にも使う

● oldendds = sbrk(increment); で差分指定もできる。

Page 56: Unixカーネルの設計 7 プロセスの制御

ページ単位でのメモリ割り当ての罠

140924番地

141312番地(69ページの終端)

仮想メモリ

sbrk(0) = 140924現在の仮想アドレス終端の値

プロセスの終端を超えてアクセスしてもSIGSEGVにならない範囲。(ページ単位では既に取得済みのため)

アクセスするとSIGSEGV発生

● ブレイク値を超えてもSIGSEGVが出ないパターン

プロセスで確保済み

未割当

Page 57: Unixカーネルの設計 7 プロセスの制御

システムの起動とinitプロセス

Page 58: Unixカーネルの設計 7 プロセスの制御

システムの起動1

起動ブロック(MBR)

起動プログラム

1. 起動ブロックから読み込む

/unix

2. ファイルシステムからカーネルのロード

バッファハッシュ iノードハッシュ 領域/ページ

3. 内部データの初期化

Page 59: Unixカーネルの設計 7 プロセスの制御

システムの起動2

4. ルートファイルシステムを/にマウント

5. プロセス0の環境を作る

● u区域の作成

● プロセス表の0番フィールドの初期化

● ルートをプロセス0のカレントディレクトリに設定

u区域p領域

text data stack

ユーザのFD表

カレントディレクトリ

state pid size ...

R 0 xxx

プロセス表

プロセスコンテキスト

6. プロセス0からforkして、子プロセスでinitを実行。/etc/inittabを読む。● 端末を開始する getty プロセスを生成する。

● gettyがloginを通し、ログインシェルをexecする。 co::respawn:/etc/getty console console 46:2:respawn:/etc/getty -t 60 tty46 4800H

/etc/initab

ラベル:ランレベル:アクション:コマンドの順番※respawnはプロセスが終了したら再起動する意味

7. プロセス0はスワッパプロセスとして継続する

Page 60: Unixカーネルの設計 7 プロセスの制御

まとめ

Page 61: Unixカーネルの設計 7 プロセスの制御

7. プロセスの制御のまとめ

● fork は親プロセスのコンテキストを複写して子を作る

● signal によって、シグナル受信時の振る舞いを定義

● kill によって、シグナルを対象のプロセスに投げる

● 子プロセス終了シグナルを契機に、ゾンビ状態のプロセスを消去

● wait をつかって子プロセスの終了と同期を取る

● text領域は共有することでメモリ効率の向上を目指す

● 実ユーザIDと実効ユーザID。ファイルのアクセス権は実効ユーザID

● 起動時にinitが様々なプロセスを生成し、端末の制御を始める

● 最初のプロセス0はスワッパとして継続する