Linuxシステムプログラミング
概要
- コンパイルの仕方:
gcc -Wall -Wextra -O2 -g -o hoge hoge.c
- Linuxコンパイル: v2.6
- gcc Cコンパイラ: v4.2
- Cライブラリ 2.5
システムコール
システムプログラムの入口に相当するものが**システムコール(system call)**です。syscallと短縮されることもよくある。システムコールとはテキストエディタやゲームなど、ユーザ空間からOSに何かしらの動作やリソースを要求するための、カーネル(システムコア、中核)機能を呼び出すものです。read()
やwrite()
などよく利用されるものから、get_thread_area()
やset_tid_address()
などめったにお目にかかれないものまで、幅広く用意されています。
Linuxで実装されているシステムコールは他のOSのカーネルに比べ、ずっと数が少なくなっています。例えば現在のi386アーキテクチャ用のシステムコール数はおよそ300になりましたが、Microsoft Windows のシステムコールは数千にも上るそうです。LinuxカーネルではAlpha、i386、PowerPCなどマシンアーキテクチャごとにシステムコールを容易しています。このため、あるアーキテクチャでは使用可能なシステムコールでも、他のアーキテクチャでは対応していない場合があります。とは言っても、大部分のシステム(90%以上)はすべてのアーキテクチャで実装されています。
システムコールの発行(コール)
アプリケーションはカーネルに対し、実行するシステムコールを通知し、**CPUのレジスタ(machine register)**へそのパラメータをセットします。システムコールには0から始まる番号が振られています。例えばi386アーキテクチャでシステムコール番号5(open()の番号です)を通知する場合は、アプリケーションはint命令を実行する前に、5をeaxレジスタにセットします。
APIとABI
API
APIはソフトウェア間のソースレベルでのインターフェースを定義したものです。他のソフトウェアから呼び出し可能なインターフェースの標準セットを定義し、抽象化したものです。通常は上位ソフトウェアから下位ソフトウェアの機能を呼び出します。APIを提供するソフトウェアを、**APIの実装(impliementation)**といいます。
ABI
APIがソースレベルのインターフェースを定義するのに対し、ABIは特定のアーキテクチャにおけるソフトウェア間の低レベルなバイナリインターフェースを定義したものです。アプリケーションが自信やカーネルライブラリと何らかの情報をやり取りする際に使用する方法です。ABIが保証するのは**バイナリレベルの互換性(binary compatibility)**です。ABIが同一ならばリコンパイルせずともオブジェクトコードはどんなシステムでも動作することが保証されます。
ABIが定義するものは、呼び出し規約(calling convention)、バイトオーダ、レジスタの使用、システムコールの発行、リンク、ライブラリの動作、バイナリオブジェクト形式などです。呼び出し規約では例えば次のような内容を定義します。
関数コールの方法
関数パラメータの渡し方
値が維持される、またはされないレジスタ
関数の戻り値の受け取り方
異なるアーキテクチャでシステムコールを共通化する試みがされたが頓挫。
ABIはアーキテクチャに密接に関係するもの
ほぼすべてのABIはレジスタやアセンブリ命令など、マシン独自の実装に依存します。
このためLinuxでもマシンアーキテクチャごとにそれぞれのABIを定義しています。
ABIは丸暗記しなくて良い
ABIを決定するのはコンパイラや隣家などの**ツールチェイン(toolchain)**であり、通常は他の場面では登場しません
標準仕様
- POSIX(Portable Operating System Interface)
- SUS(Signal UNIX Specification)
Linuxと標準仕様
通常ファイル
一般のファイルをLinuxでは**通常ファイル(vergular file)と呼ぶ。VMSなど他のOSはレコード(record)**などの高度に構造化されたファイルを持つものがあるがLinuxにはない。
ファイルセットとファイルポジション
ファイルの読み書き開始位置は、ファイルセットとファイルポジションで定められる。
ファイルセットはオープンされたファイルに対しカーネルが対応させるメタデータの中で重要なものである。ファイルが初めて開かれたときのファイルポジションは0である。終端以降へ書き込むことは可能である。また、ファイル先頭よりも前には書き込めない。つまり、ファイルポジションは正の数で表される。
ファイルポジションの最大値はファイルポジションを代入するC言語の型のサイズによって制限され、現在のLinuxでは64bit型を使用している。
ファイルサイズ
ファイルを構成するバイトストリームのバイト数トランケート(トランケーション、切り詰め)操作を行うとファイルサイズを変更できる。
大→小 は可能である。また、小→大 も可能である。この場合、空きは0で埋められる。
ファイル
ファイルはinodeで管理される。ファイル名からinode番号を解決することをディレクトリ解決やパス解決と呼ぶ。ファイル名とinode番号の組をリンクと呼ぶ。
絶対パス「完全表記」 <—> 相対パス
- ディレクトリは通常ファイルのように扱えるがオープンなどの操作がカーネルによって禁止されている。
- ディレクトリ操作は専用のシステムコールを使用する。
- 本質はリンクの追加、削除を行うだけ。
- カーネルの仲介なしだと小さなミスでファイルシステムの破壊につながる危険がある。
ハードリンク
ファイルの削除はリンクの削除を行っている。
リンクカウントが0になるとinodeや対応データが消える。
シンボリックリンク
存在しないファイルを参照するシンボリックリンクは「broken link」
- ハードリンクに比べ、シンボリックリンクはオーバーヘッドが大きくなる。
- シンボリックリンクをたどるには、シンボリックリンク自身のパス解決に加え参照先のパス解決も必要になるため。
- 複数のファイルシステム横断できるのがメリット
スペシャルファイル
- **スペシャルファイル(special file, 特殊ファイル)**はファイルとして表現されたカーネルオブジェクト。
- Linuxでは4種類のスペシャルファイルに対応
- ブロックデバイス
- キャラクタデバイス
- 名前付きパイプ
- Unixドメインソケット
- スペシャルファイルは昼食化した概念をファイルシステムできれいに収める方法で「全てのものはファイル(everything is a file)」の思想に基づく。
- Linuxではスペシャルファイル作成のシステムコールを備える。
- デバイスファイルも通常ファイルのようにファイルシステム上に存在するもの。
- デバイスファイルはオープン、読み取り、書き込みが可能。
- ユーザ空間からシステムのデバイス(実デバイス、仮想デバイス)へのアクセス、操作が可能。
- Unixのデバイスはキャラクタデバイスとブロックデバイスがある。
キャラクタデバイス
- バイトがリニアに並んだものとしてアクセス。
- デバイスドライバがキューにデータを1バイトずつ起きユーザ空間からキューに置かれたデータを読み取る。
- e.g. キーボード
- キャラクタデバイスへはキャラクタデバイスファイルを介してアクセスする。
ブロックデバイス
- データをバイト配列としてアクセスする。
- デバイスドライバはシーク可能なデバイスにデータバイトをマッピングし、ユーザ空間からはバイト配列内ならばどのバイトにもアクセス可能。
- ブロックデバイスは通常ストレージデバイス。
- e.g. HDD, FDD, CD-ROM, フラッシュメモリ
- ブロックデバイスへはブロックデバイスファイルを介してアクセスする。
名前付きパイプ(Name pipe, First In First Out => FIFO)
- IPC(プロセス間通信, Interprocess communication)の仕組みの1つで、スペシャルファイルのファイルディスクリプタを介した通信チャンネル。
- シェルのパイプはシステムコールによってメモリ上に作成されるものでファイルシステム上に存在するものではない。
- 名前付きパイプは、FIFOスペシャルファイル(FIFO)というファイルを介してアクセスする。親子関係をもたないプロセス同士でもFIFOスペシャルファイルを介して通信可能。
ソケット(Socket)
- ソケットはIPCが進化したもの
- プロセス間通信に使用
- 同じマシン内のプロセスだけでなく、異なるマシン間のプロセスでも通信できる。
- Unixドメインソケットは同一マシン内での通信にしか使用できない。
- インターネット経由のソケットはホスト名とポート番号で通信を特定するのに対して、Unixドメインソケットではファイルシステム上に存在するスペシャルファイルを用いて特定する。
ファイルシステムと名前空間
- すべてのUnixがあてはまるのは名前空間が存在する。
- Windowsだと
A:\a.jpg
やC:\Users\test\test.png
のように分割している。
ファイルシステム
ファイルやディレクトリの集合体。ある形式に従い、階層を形成する。
グローバルなファイル/ディレクトリの名前空間へファイルシステムを追加、削除することをマウント、アンマウントという。
ファイルシステムは名前空間の指定した位置「マウントポイント」に割り当てられる。
ブロックデバイスへのアクセスはセクタ単位で行われる。512バイトが多く使用される。
ファイルシステムへのアクセスはブロック単位で行われる。
- ブロックはファイルシステムの概念で物理メディアの概念ではなない。
- 一般にセクタサイズより大きい。
- ページサイズ(Page Size)を越えることはない。
- Page Size: ハードウェアのMMUが使用する最小単位
- MMU: メモリ管理ユニット(Memory Management Unit)
Unixでは名前空間1つをシステム内のユーザ、プロセスで共有していた。
Linuxではプロセスごとの名前空間(per-process namespace)という革新的な方式を採用しており、固有のファイル、ディレクトリをもてる。(親プロセスの名前空間をデフォルトで引き継ぐ)