ずっと読みたくて読んでいなかった「はじめてのOSコードリーディング - UNIX V6で学ぶカーネルのしくみ (技術評論社)」を読んだ際のメモを記載します。少し古い本ですがカーネル、OSの仕組みについて、またコードにてその詳細が説明されています。
ただ取り上げているのがUNIX V6だからなのかスレッドの話などないですし、全て網羅するのは流石に厳しいけれども、わかりやすいため取っ掛かりに良いかと。
・はじめてのOSコードリーディング-UNIX-V6で学ぶカーネルのしくみ (技術評論社)
書籍を読むための前提知識
・簡単なC言語、特に関数、構造体の知識
・簡単なアセンブリ言語の知識(いきなり"汎用レジスタ"などと言われてもわかる)
メモ
カーネルの機能
・実行プログラム(プロセス)の管理
・メモリ管理
・ファイルシステム
・ファイルと周辺デバイスで共通のI/O
・割り込み
・端末処理サポート
プロセス
プロセスは実行中のプログラムを管理する単位
実行プロセスを随時切り替えることで、複数のプログラムを並行して実行している
proc構造体とuser構造体が各プロセスの制御・状態情報を表す
proc構造体はメモリに常駐する。user構造体はスワップアウトの対象
各プロセスは独立した仮想アドレス空間を持つ
各プロセスにはテキストセグメントとデータセグメントの、2つの連続した物理メモリ領域が割り当てられる
データセグメントはPPDA、データ領域、スタック領域の3つから構成される
テキストセグメントは仮想アドレス空間の最下位アドレスにマッピングされる
forkシステムコールはプロセスを生成する。子プロセスは親プロセスのコピー
waitシステムコールは子プロセスの終了完了を待つためにスリープし、子プロセスが終了すると子プロセスの後始末を行う
execシステムコールは実行プログラムファイルをメモリに読み込み、プログラムの実行環境を構築する
exitシステムコールはプロセスをゾンビ状態にする。ゾンビ状態になったプロセスは親プロセスに始末される
initプロセスは親プロセスを失って宙に浮いていたプロセスを始末する責務を持つ
実行プロセスを中断すると、実行優先度の高い実行可能状態のプロセスに切り替わる
r5,r6,ユーザARP、カーネルのPAR6の復帰、堆肥を行うことでコンテキストスイッチを実現している
sleep()とwakeup()を使って資源を待ち合う処理を行なっている
物理メモリとスワップ領域は、map構造体を使用して同じロジックで空き領域を管理している
空き領域獲得処理のアルゴリズムにはFirst Fitを採用している
coremap[]とswapmap[]は起動時に初期化される
スワッピングやテキストセグメントの共有によって限られたメモリを有効活用している
proc[0]が定期的にスワッピング処理を行う。proc[0]はスケジューリングプロセスやスケジューラと呼ばれる
一度のスワッピング処理では、スワッピング対象がなくなるまで処理が行われる
テキストセグメントはメモリにデータが読み込まれた時、スワップ領域にも同じデータが残る。データセグメントはスワップインされるとスワップ領域にもから解放される
テキストセグメントは2つの参照カウンタを持つ
メモリ中のプロセスから参照されなくなると、テキストセグメントはスワップ領域される
全てのプロセスから参照されなくなると、テキストセグメントはスワップ領域からも解放される
スティッキービットが立っていると、スワップ領域からも解放されない。起動時の処理を速くすることができる
テキストセグメントは、スワップ領域にいったん作成してからメモリに読み込む
割り込み
割り込みとトラップは、何らかの要因により処理を中断して、カーネルプロセスがハンドラを実行し、その後中断していた処理を再開する仕組み
割り込みは周辺デバイスからの非同期割り込み要求を効率よく処理するためのしくみ
トラップはCPU内部で発生し得る例外を効率よく処理するためのしくみ
システムコールはユーザープログラムがカーネルに対して機能要求を行う手段。トラップを用いて実現される
システムコールはsysent[]によって管理されている。sysent[0]は間接呼び出しのためのエントリ
シグナルはプロセス間で通信を行う仕組みの1つ
シグナルを受信したプロセスは、通常処理を中断し、シグナルの種類に応じた処理を行う。このことから、シグナルは割り込みの一種と捉えることもできる
周辺デバイスからの割り込みと異なり、全てがソフトウェア上で実現されている
シグナルを無視したり、独自のシグナルハンドラを実行させることができる
複数のシグナルを受信すると、古いシグナルは上書きされる(ただしSIGKILを除く)
実行プロセス自身がシグナルを受信したかどうかを確認する。そのため、送ったシグナルがすぐに処理されるとは限らない
トレースは親プロセスが子プロセスに介入する仕組み。デバッガなどで用いられる
ブロックI/Oシステム
デバイスにはブロックデバイスとキャラクタデバイスの2種類がある
デバイスドライバ群はデバイスドライバテーブルで管理される
デバイスはクラスとデバイス番号で管理される
デバイスを仕様するために、あらかじめスペシャルファイルを作成しておく必要がある
ブロックデバイスの処理はブロックデバイスサブシステムに集約される
ブロックデバイスとのやりとりにブロックデバイスバッファを用いる。バッファはデータ整合性を保ち、キャッシュの機能を果たす
読み込みには同期読み込みと非同期読み込みがある。非同期読み込みは先読みに使用される
書き込みには同期書き込みと非同期書き込みがある。すぐに書き込みに行かない遅延書き込みもある
バッファを経由せず、ブロックサイズ(512バイト)の制限を受けないデータ転送を行うRAW入出力もある
ブロックデバイスごとに対応したブロックデバイスドライバがある
ブロックデバイスドライバはbdevsw[]で管理する
メジャー番号がbdevsw[]インデックスに対応する
ブロックデバイスドライバはデバイス処理キューを持つ
ファイルシステム
ファイルシステムによりブロックデバイスの構造が抽象化される
データはファイルとディレクトリと木構造の名前空間によって管理される
ファイルはデータそのものを表す。ディレクトリはその下にファイル/ディレクトリのinode番号とファイル名を持つ
ファイル/ディレクトリにはアクセス権限を設定できる
マウントにより複数のブロックデバイスのファイルシステムの読み込み/取り除きが可能
ブロックデバイスの領域は起動用領域、スーパーブロック、inode領域、ストレージ領域の4つに分かれる
スーパーブロックはブロおくデバイスの制御情報を扱う
inodeはファイルの定義情報を扱う
ストレージ領域のブロックはデータそのものを扱う
ファイルの実体は1つのinodeと複数のストレージ領域のブロックのセット
ファイルが使用するストレージ領域のブロックはinodeからマッピングされる
inodeからストレージ領域へのマッピングは直接参照、間接参照、二重間接参照の3種類がある
空きinodeとストレージ領域の空きブロックはスーパーブロックのフリーリストにより管理される
バス名の1要素を取得し、ディレクトリ中の対応するinodeを取得し、バス名のその次の要素を取得する、という処理を繰り返して、バス名から該当するファイル(のinode)を取得する
オープン済みのファイルはuser.u_ofile[],file[],inode[]の3つの構造体で管理される
ファイルをオープンするとuser.u_ofile[]のインデックスであるファイルディスクリプたがユーザプログラムに返される
file[]はオフセットなど、ファイルの捜査情報を扱う
read,writeはブロック単位で行われる
ファイルの削除とはディレクトリエントリのinode番号を0にすること
リンクによってファイルに複数の名前を付けることができる
パイプを使って親子プロセス間で通信を行うことができる
4096バイトのパイプ(ファイル)に対し、送信側プロセスと受信側プロセスが交互に入出力処理を行う
pipe,fork,close,dupシステムコールを組み合わせてパイプ通信を確立する
システムの起動
UNIX V6の起動時には、ブートストラップローダ→ブートストラップ→カーネル本体と、徐々に大きなプログラムを実行していく
起動時にMMUの初期化、カーネルAPRの設定、クロック装置の初期化、メモリ、スワップ領域の初期化、I/O資源の初期化、proc[0],proc[1]の生成、などを行う
proc[0]はシステムプロセスであり、スケジューラとして駆動する
proc[1]は/etc/init.dを実行する
所感
Linuxカーネル、システムコールについて曖昧だったことがクリアになった。また仕組みもコード付きでわかりやすく記載されていて、OSの基礎を勉強するには良いと思いました。
Linuxカーネル HACKS (オライリー・ジャパン)を読む前に知っておきたい知識かもしれません。