以下記事を読んでから技術書の読み方を変えてみました。
読書のやり方を変えてみたら知識の吸収速度・引き出し速度が上がった話
実際にこの読書方法を実践してみてどうだったか感想を書きます。
読んだ本の紹介
今回は以下の低レイヤーの本を3冊読みました。
- [試して理解]Linuxのしくみ 実験と図解で学ぶOSとハードウェアの基礎知識
- プログラムはなぜ動くのか 第2版 知っておきたいプログラムの基礎知識
- コンピュータシステムの理論と実装 モダンなコンピュータの作り方
なぜ低レイヤー?
僕は情報系の学部出身ではないので、CSの基礎知識がめっちゃ欠けています。。 エンジニアとしてアプリケーション以下の低レイヤーの理解を深めることでハードウェアを効率的に利用した開発ができるようにしたかったです。 また、システム障害時に素早く対応できるようにもしたかったです。 基礎が身についていると新技術の理解度も上がると思うのですぐには効果が出ないですが、以前から勉強したいと思っていました。
読書方法の紹介
基本的には参考にした記事の方法で読書しました。
違う点として電子書籍を読む端末を持っていないので紙の本を読み、ハイライト方法には付箋を貼って対処しました。
高校生みたいでちょっと懐かしかったです。笑
それと読書ノートや知見まとめノートは作っていないです。
赤色のハイライトまとめ
最初の「*」は赤色のハイライトをした部分です。 その下のスペースが入った後に「*」がある部分は自分の気づきや学びを書いてます。
[試して理解]Linuxのしくみ 実験と図解で学ぶOSとハードウェアの基礎知識
赤色のハイライト * コード領域とデータ領域の「メモリマップ開始アドレス」が必要な理由は、CPU上で実行される機械語命令においては、高級言語で書かれたソースコードと異なり、特定のメモリアドレスを指定する必要があるからです。32
気づきや学び * プログラムを実行するためにメモリアドレスの指定は必須。
* 1つのCPU上で同時に処理するプロセスは1つだけ 40
* 1つのCPUで複数の処理を同時に捌けない。最近のCPUは複数のコアを持っているから並列に処理を捌ける。
* 図04-11 プログラム内の関数が実行されるタイミング(正しい理解)52
* コンテキストスイッチによりプロセスが切り替わる。
* プロセスのページテーブルをうまく設定すれば、図05-23のように、物理メモリ上では断片化している領域を、プロセスの仮想アドレス空間上では大きな1つの領域として見せることができます。112
* プログラムを動かす環境では仮想化技術は必ず出てくる。
* 図05-25 仮想記憶では、他のプロセスのメモリ空間にはアクセスできない 115
* 他のメモリ空間を勝手に変更できなくなるので他のプロセスによりメモリ操作の心配がなくなる。
* 仮想メモリの枯渇とは、プロセスが仮想アドレス空間の範囲一杯まで仮想メモリを使い切った状態でメモリを獲得しようとしたときに発生します。133
* 仮想メモリの容量も気にしないといけないので管理が複雑になる。
* これはストレージデバイスの一部を一時的にメモリの代わりとして使用するしくみです。141
* スワップの話でOOM対策の一つである。
* プログラムのワークロードをキャッシュメモリのサイズに収めることによって、性能を大きく向上させます。165
* プログラムを高速に動かすにはCPUの近いところでデータを配置することが大切。
* 設定変更や時間の経過によってシステムの性能が突然、数行も劣化したような場合は、ファイルのデータがページキャッシュに収まらなくなった可能性があります。187
* ブラウザでもキャッシュの仕組みがあるが、速度を上げるためには必要。
* これらのシステムコールが発行されると、次のような順序でファイルのデータを読み出します。197
* ファイルシステムによらず統一的なインターフェースでアクセスできるのがLinux。
* SSDとHDDの一番大きな違いは、SSDの場合はデータへのアクセスに機械的な動作が一切なく、電気的な動作だけで住むことです。254
* SSDのメリットは高速にデータの読み書きができることだが、HDDと比べると費用が高い。
プログラムはなぜ動くのか 第2版 知っておきたいプログラムの基礎知識
* CPUの制御装置は、プログラム・カウンタの値を参照して、メモリーから命令を読み出して実行します。21
* プログラムの実行順序はこのカウンタを参照している。
* コンピュータは、引き算を行う場合に、内部的には足し算として演算するようになっています。40
* 2進数では補数を使ってマイナス表現をする。
* ポインタとは、データの値そのものではなく、データが格納されているメモリーのアドレスを持つ変数のことです。80
* C言語で出てくるポインタはアドレスを指定していて自由に値を入れられる。
* 仮想記憶によって発生するページインやページアウトは、低速なディスクのアクセスを伴うので、その期間だけアプリケーションの動作が遅くなってしまいます。103
* この機能はLinuxでいうところのスワップ。
* Java仮想マシンは、Javaバイトコードを逐次ネイティブ・コードに変換しながら実行します。145
* JVMで動かすときは高級言語をバイナリまでコンパイルしていない。まずはVMコードに変換している。
* 何らかのプログラミング言語で記述されたソースコードは、ネイティブ・コードに翻訳されなければCPUに理解してもらえません。153
* 動的言語でも静的言語でも実行するにはネイティブ・コードに変換しないとダメ。
* 複数のオブジェクト・ファイルを結合して1つのEXEファイルを生成する処理がリンクであり、リンクを行うプログラムのことをリンカーと呼びます。159
* リンクしないとプログラムが動かない。
* ライブラリ・ファイルは、複数のオブジェクト・ファイルをまとめて1つのファイルに格納したものです。160
* C言語ではライブラリ・ファイルに実行されるバイナリが入っていて標準関数をプログラムの中で実装したときはリンクさせないと実行する関数の中身がないってことになる。
* 外部シンボルとは、他のオブジェクト・ファイルの中にある変数や関数のことです。161
* 「シンボルの未解決」とはこのファイルに目的の変数や関数が記述されていないときに発生する。
* スタックは、関数の内部で一時的に使用される変数(ローカル変数)や、関数を呼び出すときの引数を格納するためのメモリー領域です。
ヒープは、プログラムの実行時に任意のデータやオブジェクトを格納するためのメモリー領域です。156, 166
* Javaでもスタックやヒープ領域に関するエラーなどが出てくるのでここら辺の理解は重要。
* レジスタは、メモリーよりアクセスが大幅に速いので、処理を高速化できるからです。219
* 他の低レイヤーの本を読んでも出てくる箇所。レジスタ、メモリー、ストレージの記憶媒体の使い方は重要。
* IN命令は、指定したポート番号のポートからデータを入力し、それをCPU内部のレジスタに格納します。
OUT命令は、CPUのレジスタに格納されているデータを、指定したポート番号のポートに出力します。235
* ポート経由でデータの入出力をする。レジスタに格納して実行するタイミングの詳細が気になる。
* CPU内部のレジスタは、データを演算処理するものですが、I/Oコントローラ内部のレジスタは、基本的にデータを一時的にに格納するだけのものです。 236
* I/Oコントローラ内部にもレジスタのような記憶装置を持っている。
コンピュータシステムの理論と実装 モダンなコンピュータの作り方
* 2の補数を用いることで、単純なビット単位の加算が行えるハードウェアがあれば、特別なハードウェアを用いることなく正と負のどちらの加算も行える、ということになる。31
* コンピュータを表現する2進数は低レイヤーでは必須知識。
* 機械語においてハードウェアとソフトウェアが交わり、機械語においてプログラマーの抽象的思考(これは記号命令によって表される)がシリコン上で実行される物理的操作に変換される。60
* プログラマの思考が最終的に機械語に変換される様は魔法みたい。
* アセンブラはアセンブリコマンドを入力として受け取り、その出力として対応するバイナリ命令を生成する。116
* アセンブリと機械語は1:1になっていて機械語の理解にはとてもいい。
* コンパイラにおいても、バイナリである機械語を直接生成したほうが都合がいいため、シンボルを含んだコマンドをわざわざ生成するようなことはしない。128
* アセンブリを作成する理由はあんまりなさそう。
* 最初のステージは高水準言語の仕様だけに依存し、2番目のステージは対象とする機械語の仕様だけに依存するからである。135
* VMのメリットとしてそれぞれのステージにのみ関心を持てば良くなる。
* コンピュータサイエンスという分野においては、「シンプルさと優美さを兼ね備えたものは表現力も豊かである」というのが常である。139
* CSだけではなくこれは言えると思う。シンプルで美しく保てると変更しやすく表現力が豊かだと思う。
* トークナイザは、ソースコードに大して意味を持つコードの最小単位である「トークン(字句)」に変換する。
パーサは、一連のトークンを言語の構文ルールに適合させ、その構文構造を明らかにする。225
* 上記二つで構文解析器を作成している。Javaとかでも同じなのか。
* 図10-1 Jackコンパイラ 225
* この図はとてもわかりやすい。
* 一般的に、プログラム言語は、それが許可するトークンと、そのトークンを意味のあるプログラム構造へ結合させる構文ルールを正確に指定する。226
* 字句解析では高級言語のコードを意味のあるトークンというグループにまとめる。
* プログラマーは通常、LEX(lexical analysis)やYACC(Yet Another Compiler Compiler)などの”コンパイラ生成器”を使ってトークンナイザやパーサを作る。243
* 一から構文解析器を書くことはないらしい。
* OSは通常、高水準言語によって書かれ、他のプログラムと同様にバイナリへとコンパイルされる。278
* OSの実装をする際も何かしらの高級言語で書かれている。
* 高水準言語を用いる利点のひとつは、--変数のためのRAM領域への割当や、変数が必要でなくなったときにそれを再利用する方法について、プログラマーはその詳細を気に掛ける必要がない--という点が挙げられる。284
* システムを作る上で各層毎に役割を与えてそれぞれを依存させないことでその中身を知らなくてもいいようになっている。クリーンアーキテクチャもこういった思考の元だと思う。
各本の総評
[試して理解]Linuxのしくみ 実験と図解で学ぶOSとハードウェアの基礎知識
この本は図が多めで理解しやすいように丁寧な説明がされています。 低レイヤー関連の本を読むとき、一番最初に読む本として適していると思います。 詳細な説明は割愛されているのでLinuxのしくみを知るための入門としてオススメです。
プログラムはなぜ動くのか 第2版 知っておきたいプログラムの基礎知識
プログラムが動くまでを順を追って説明してくれます。 基礎的な内容が豊富でわかりやすいように図で解説も豊富でとっつきやすかったです。 プログラムが動くまでの流れが具体的に頭に浮かばない人はこの本オススメです。
コンピュータシステムの理論と実装 モダンなコンピュータの作り方
ちょっと難しめの本です。「プログラムはなぜ動くのか」の内容を理解している人にはこの本オススメです。 この本はコンピュータを実際に作ってみて内容を深く理解しようという趣旨です。 僕は本を読んだだけで実際にコンパイラとかを作ってはいないですがそれでも十分勉強になりました。 Rustとかの勉強がてら実装をしてみたいです。
最後に
今回、読書方法を変えてみて知識が全然ない低レイヤーの勉強をしました。
複数の本を読むことで大事そうなところはどの本でも解説されていて、大事なところを掴むのにこの読書方法はとても良いと思いました。
ただ、紙の本に付箋を貼るのはやっぱりめんどくさかったです。笑
技術書はkindkeの端末では画面が小さくて読みにくそうだから、iPadを買ったら電子書籍に移行したいです。(iPad高い。。)
次はアーキテクチャーとかリファクタとかの本に手を出したいなと思っています。