最近OSとかカーネルに興味があって以下の書籍を読み進めている。
結構CPUとかメモリが生々しく登場するので頭が慣れず読むのに時間かかっているが、今までかなり抽象的に認識していた事柄が具体的にイメージできるようになってきたので楽しくもある。
読み進めているとふと、OSがどうやってCで書かれたプログラムのリソース管理を行っているのかわからなくなった。(まだ途中までしか読んでない状態ではあるが)
OSはメモリとかデバイスなどのリソース管理の機能を提供している。 だからプロセスはメモリやデバイス利用で他のプロセスと競合したりしないし、1つのCPUを使って複数のプロセスを実行できたりする。
その説明でなんとなくイメージはできるものの、イマイチ腑に落ちてなかった。 以下、よくわからなくなったまでの思考の流れを記載する。
- 「Cで書かれたプログラムはコンパイルされて機械語になり、メモリに読み込まれて、CPUがそれを順次実行するとのこと。」
- 「あれ?この説明だとOSが介入する場所がなくない?」
- 「どうやってOSはこのプログラムの実行状態を把握するのか?」
- 「OSがプログラムを呼び出すのだから把握できるんじゃない?」
- 「でもCで書いたプログラムをコンパイルすると機械語の実行ファイルになるわけで、この実行ファイルってOS無しでも実行できるんじゃないの?だってOSって大部分がCで書かれていて、OS無しで実行できてるわけで。。」
- 「Cで書いたプログラムがコンパイルされたものは機械語の実行ファイルで、機械語ってのはCPU命令なので、CプログラムとCPUの間にOSが挟まってないやん。」
- 「イメージ的にはCで書いたプログラムの実行ファイルをOSが実行し、OSがプログラムから命令を受けて、OSがCPUに命令し、、みたいな流れをイメージしてた。」
- 「Cの標準ライブラリを使うときにOSの機能を使っているのでは?」
- 「OSって標準ライブラリの事なん?」
- 「仮にそうだとして、標準ライブラリをリンクしてコンパイルしてできた実行ファイルは機械語なので、直接CPUに命令するやん。」
- 「OSのレイヤーは何処にあるんだ。。」
かなり混乱したので調べたり(あまりいい感じの調べ方がわからなかった)色々考えて「こうじゃないか?」という感じにまとまったので書いてみる。 (間違いはあるかもしれない)
C言語にはホスト環境とフリースタンディング環境という物がある
知らなかったのだが、C言語にはホスト環境とフリースタンディング環境という物がある。
フリースタンディング環境はOSが無くても単体で動く。 ブートプログラムから直接読み込まれて実行される感じ。 OSも大部分はフリースタンディング環境のC言語で書かれている。 ブートプログラムからOSがフリースタンディング環境として実行される。
ホスト環境はOSが存在することが前提になっている。 ファイルの読み書きやメモリの確保などは、OSの提供するシステムコール経由で行うことでOSが各プロセスで競合しないように調整される。
たぶん、C言語の標準ライブラリとかを使ってデバイスやリソースを扱う処理を実行したとき、標準ライブラリはOSに対してシステムコールを発行するようになっている。
OSが実行ファイルを起動するときのイメージ
ホスト環境用の実行ファイルが起動される手順は以下の感じ(ざっくり。細かくは違うと思う)。
- OSが実行ファイルをメモリ上の開いている領域に読み込む。
- OSがプロセスを表現する構造体データ(エントリポイントのメモリアドレス等、実行に必要な情報を保持する)を作成し、プロセススケジューラに追加する。
- プロセススケジューラによって実行順が回ってくるとCPUを使って処理を実行する。
OSが実行ファイルを読み込んで実行することでOSは実行プロセスを把握しているし、各プロセスが読み込まれるメモリ領域が競合したりしないし、複数のプロセスを1CPUで同時実行できたりする。
システムコールはどうやってOSのプロセスに情報を伝えているのか?
ホスト環境で動作するプロセスがOSのシステムコールを発行しているのはわかった。 では、どうやって各プロセスはシステムコールでOSの処理を呼び出しているのだろう?
Cで書かれたプログラムはコンパイルされると機械語の実行ファイルになる。機械語で表現されているのはCPU命令だから、CPUに対して命令してる。OSじゃない。 標準ライブラリをリンクしていても、それも単なる実行ファイルでOSのプロセスそのものではない。
プログラムがシステムコールを実行するタイミングで、なんらかの方法でOSのプロセスの処理が呼び出されているハズと思うのだが、どうやっているのかわからなかった。 最初はプロセス間通信かなんかでOSプロセスの処理を呼び出しているのかと思ったが違うようで、こちらの記事を読んでなんとなくわかった。
つまり、
たぶんOSは各システムコール用の処理があるメモリアドレスを、CPUのレジスタに事前に登録しているのだと思う。(本にもそんな記述あったきがする) 各プロセスは直接OSの処理を呼び出すのではなく、CPU経由で呼び出しているということらしい。
つまり、86-64とかarmのCPUってOSが乗ること前提の設計になってる感じなのだなとなんか意外に思った。
システムコール(主にUNIXのもの)はPOSIXで定義されてるらしい
標準ライブラリが内部でこういったシステムコールを行ったりするのはわかった。 じゃあ、システムコールのインターフェースにはどういう取り決めがあるのだろうかと思った。
取り決めがないと、プログラミング言語とOS間の調整がカオスになりそうなのでなにかあるのだろうと思ったら、POSIXがそれに当たるらしい。
POSIXって耳にはしてたけどそういう事なんですね。なるほどなー