「コンピュータ・システム プログラマの視点から」を読みました
「コンピュータ・システム プログラマの視点から」を読みました
はじめに
コンピュータシステムを理解したいと思い「コンピュータ・システム プログラマの視点から」(通称CS:APP)を読んだ。
内容はコンピュータシステムがどのようにプログラムを実行しているのかということをC言語プログラマの視点から解説したものになっている。 ちなみに、原著のホームページはこちらにある
ページ数は906ページと膨大で、一回読んだ程度では3割ほども理解していないと思うので復習も兼ねて振り返ってみる。
第1章 コンピュータ・システム・ツアー
この章は序章である。 残りの章を一気に概観してテキストファイルに書いたhello, worldをプリントするC言語プログラムが、どのように実行されるのかを説明している。
第2章 情報の表現と操作
この章は2進数による整数や少数の表現とその計算についての話が展開される。 有限の範囲で表現するためオーバーフローや数値の誤差がどのように生まれるのかが理解できるようになる。
第3章 プログラムのマシン・レベルの表現
この章ではアセンブリ言語についての話が展開される。 C言語プログラムがどのようにアセンブリ言語に変換されるのかわかるようになる。
個人的にこの章が一番面白かった。 基本的に関数はレジスタとやりとりをして計算したり変数を保持したりしている、変数を保持するレジスタの数が足りなくなるとスタック領域に値を退避する。 また、if文やfor文などの処理が条件フラグとアドレスへのジャンプとして表現されていることがわかり、よく出来ていることに感心してしまった。 配列や構造体というものがポインタとポインタ演算そのものであることに驚いたと同時にポインタというものが何であるかはっきりと理解できた。 関数内で別の関数を呼び出すことも関数ポインタもポインタやジャンプが理解できていれば仕組み自体は簡単なものであった。 さらに、関数呼び出し時のレジスタの退避などの決まりによって再帰関数というものがどのように実行されているのか、なぜ再帰関数はスタックを食い尽くすのかが腑に落ちた。
第4章 プロセッサ・アーキテクチャ
この章では機械語命令をCPUがどのように実行していくのかについての話が展開される。 ブラックボックスも多い説明だったが、CPUというのがどのようなステップで機械語を実行するのか理解が深まった。 また、処理効率を上げるためにパイプライン化をしていくが、命令が分岐するものや途中で結果を待っていないといけない命令については複雑な工夫が必要なことがわかった。 かなり難しかったのでまた勉強し直す。
第5章 プログラム性能の最適化
CPUの限界までにプログラムを最適化していくのが面白かった。 関数の呼び出しや無駄なメモリの参照を避けるということはなんとなくやっていたが、第3章の話を元に定量的に評価すると実際に最適化されることがわかるのがよかった。 ループアンローリングという最適化の手法は全く知らなかったし、実際にやることはほとんど無いだろうが勉強になった。 メモリや次の章で詳しく説明されるキャッシュなどについても視野に入れながらプログラムの高速化をしていくのが面白い。
第6章 メモリ階層
ハードウェアの話が中心になる。 処理が早いが高価で少量のメモリと処理が遅いが安価で大容量のメモリを効率的に使用するために、どのような工夫がされているかわかる。 キャッシュというものがどのようなものなのか理解が深まった。
第7章 リンク
プログラムをコンパイルする際に複数の実行可能オブジェクトファイルを繋げる仕組みについて話が展開される。 プログラムは機械語に翻訳されるわけだが、標準ライブラリやその他の別のテキストファイルに書いた関数をmainの処理内で使用する際にどのようにメモリに展開されるのかがわかった。 関数を別々のファイルに分離したくなることはしばしばあるが、関数を更新したりするときに分けた関数が含まれる複数ファイルを全てコンパイルし直さなくてもいいように工夫されているのかなと思った。
第8章 例外的な制御フロー
これまでは一つのプロセスが一直線に実行される制御について学んできたが、ここでは例外的な制御フローといって制御フローに割り込んで制御する方法を学んでいく。 I/OやシステムコールなどがCPUやOSと連携してどのように例外的な制御フローを実行するのかの概要がわかった。 一番勉強になったのはシステムコールというのが単なる関数ではなくてカーネルモードという特別なモードで実行される関数だという説明である。
第9章 仮想メモリ
メモリは言ってしまえば巨大な配列になるが、それをプロセスごとに一つのメモリを占有しているかのように扱う仕組みについて解説している。 ハードとOSの連携によって物理メモリを仮想メモリに割り当てる仕組みがなんとなくわかった。
第10章 システム・レベルI/O
システムプログラミングの簡単な説明と実際のプログラム例を説明した章。 Cのmain関数についている引数が何なのかわかってきたが、説明が簡潔すぎたのでもう少し別の本で勉強しようと思う。
第11章 ネットワーク・プログラミング
こちらも上に引き続き簡単なネットワークプログラミングの説明と例について展開される。
第12章 並行プログラミング
Rustで並行プログラミングを学んだときに出てきたミューテックスなどの概念がようやく理解できた。 並行処理では排他制御を行わないと意図しない結果を返すプログラムが書かれることはわかっていたが、何故そんなことになるのかわかっていなかった。 この章で危険なプログラムがどのようにアセンブリ言語に翻訳され実行されるかを見ることで理解が深まった。 また、クリティカルセクションという概念を知ることができたのも収穫だった。
最後に
めちゃくちゃ長い本で読み通すのも一苦労だった。 しかし、振り返ってみると全然内容を覚えていないような気がして、ひと月以上費やした価値があったのか疑問が生まれてくる。 とりあえず、目次が出来たと思って今回は終えようと思う。