プロパティベーステストのときにラムダノートさんの本を漁ってて見つけたので買って読了しました。正直最初は楽しく読んでましたが後半に行くにつれてだいぶ低レイヤーチックになってきて挫けそうになりながらもなんとか読了しました。ので一応感想文。
ただ、あんまり理解できてないのでたぶん本当に感想文です。
モチベーション
低レイヤーを知りたい。あと、Goに詳しくなりたい。
わたしは大学でコンピューターサイエンスを学んだわけではなく、あくまで業務としてアプリケーションを作成するためのスキルや知識を優先してキャッチアップしてきました。そうするとどうなるかというとC言語を通らないのです。
現代におけるアプリケーション開発にC言語の知識が必要かというとたぶん作るだけなら必要ないと思う。それでも、定期的にXなんかではC言語必要不要みたいな話題はよく見かける気がする。
本書にも書いてあった気がするけどC言語が重要というよりかはOSなどの低レイヤーの話になるときにC言語でしかコードが書かれていないからC言語が読めないとそこらへん学ぶ時に大変だよねみたいなことらしい。
わたしも業務で書くことはおそらくないであろうC言語を学びたいかというと学びたいわけではないのだけど低レイヤーについては勉強しておきたい。なぜなら、低レイヤーについて知ってると強そうに見えるからである。
DockerやDB、Rustなんかの強そうなエンジニアの方々の発言を見ているとその知識のレイヤーの深さが滲み出ているというかなんかシステムが動くということへの解像度が果てしなく違う気がする。
そういえば、エンジニアの力量は掘り下げられるレイヤーの深さに比例するみたいなポストを見た気がする。まさにそんな感じ
以下のスライドにも基礎知識をちゃんと理解することの重要さのようなものを話されている。
真面目に今後長くエンジニアとして生きていくことを考えた時にただの機能開発以上のことをするときに低レイヤーというかプログラミングやアプリが動くOSやCPUのようなハードの深い理解が必須になってくると感じる。
ただの機能開発エンジニアで終わりたくないのでわたしは低レイヤーを学びたいと思うのです。ただ、その優先事項はそれほど高くは設定しておらず、もっとアプリケーションの部分でやらなきゃいけないことは多いなと思っている。
本書はテーマがGoということもありGoの勉強と低レイヤーを知るという目的が叶えられそうだったため読んでみることにしました。
デバッグができない
全然本書関係ないけどVSCodeでデバッグしてGoの関数が内部でシステムコールを呼び出しているところを見に行こうから始まるのだけどそのデバッグができなくてめっちゃハマった。
結局Goのプロジェクトを作成した場所がシンボリックリンクになっていて、そのちゃんとしたフルパスをVSCodeに教えてあげる必要があったらしい。
出鼻をさっそく挫かれた
Goのio.Writer/Reader
いろんなところで言われているけどGoのio.Writerとio.Readerはインターフェースとしてよくできている、らしい。
ここらへんのio関係の標準パッケージをちゃんと使ったことはなく、いい勉強になった。ここの章だけ章末に練習問題もあってそこを考えるのも楽しかった。
多くのヘルパー関数があったり、zipやjsonのようなio処理をio.Writerやio.Readerの処理をラップしたデコレータと呼ばれるものが用意されていたり学びになりました。
ここら辺SI時代のJavaではけっこうやったのだけどなー
ファイルディスクリプタ
標準入力や標準出力がそれぞれ0, 1と番号が割り当てられているというやつ。実際はOSの仕組みとして何かしらの処理をするときに擬似的なファイルが作られてそれに番号が割り振られていくらしい。
ここら辺シェルワンライナー160問やったときに学んだけど、より理解が深まった気がする。
ファイルを開いた時にクローズ処理を呼び出すのはこのファイルディスクリプタで割り当てられた番号を含むリソースを解放するということだよね?だから、適切にクローズしないとその割り当てられた番号やリソースが残ってメモリリークになるということだよね?
そうやるもんだと思っていた処理がなぜ必要なのかを知ることができた。本書ではこのような原理と今までの経験が結びつくことがけっこうあってその時はなんか「つながった!!低レイヤーおもしろい!」みたいな感覚になった。
Unixドメインソケット
TCPとUDPくらいはさすがに知ってますよ。ただ、Unixドメインソケットは知らなかった。
これはTCPやUDPと違い外部ではなく内部での高速なソケット通信に使用されるファイルによる通信方法らしい。内部でのやり取りなのでIPやポートではなくファイルパスを指定する。
内部の通信なのでNGINXとサーバー間の通信やDBとのやり取りなんかで使える。
で、Unixドメインソケットはファイルを作成してコネクションを確立するのとDockerのサーバーとクライアントでこのUnixドメインソケットによる通信が使われているそう。
なんか知ってる!独学時代にDockerでrailsとMySQLを起動しようとしたときになんちゃら.sockがすでにあるからとかないとかのエラーがよく出て調べてた気がする。関係あるのかよくわからないけどたぶんこのUnixドメインソケットのコネクションのために作成されるファイルに関することだったのかなーと勝手に納得した。
メモリ管理の話
アプリケーションがメモリを読み書きするには直接物理メモリを参照するわけではない。アプリケーションのプロセスがメモリを確保しようとするとCPUがキャッシュであるTLUに問い合わせをし、なければページテーブルを用意し、仮想メモリを通して物理メモリに読み書きをする。
ユーザーが利用するメモリ空間は3つの領域があり、小さいアドレスからプログラム+静的データ、ヒープ、スタックがある。
メモリの話はアプリケーションを実装していて一番気にする低レイヤーの概念かもしれない。
大量のクラスや構造体を生成しようとすると全てメモリに乗るなーとか。
OOMのエラーが出たとか
sliceの初期化で容量確保しとかないとメモリ確保できてないからコピー発生してパフォーマンス悪いなーとか
100Tipsに書いてあったことだけど関数内で宣言されたローカルの変数はスタック領域が使われる。しかし、その変数のポインタを関数の戻り値にするとその変数への参照がCやC++ではできなくなるのでエラーになるそうです。Goの場合、自動でコンパイラがやってくれ、上記の場合、スタックからヒープに移動されるためエラーにならない。
しかし、基本的にスタックよりもヒープの方がGCが発生することもありコストが高いとされている。100TipsによるとこのようにGoの場合は開発者がヒープなのかスタックなのかを判断する必要はなく、コンパイラがやってくれるのでなるべくヒープ領域に移されるようなコードを書かないように注意する必要がある。
まあ、そこまでメモリを気にしてコードを書く必要はないと思うが、アプリケーション開発者としてこういったメモリの確保や割り当ての流れを理解することは重要だなと感じた。
おわりに
出だしがGoのioパッケージについての解説と練習問題から始まったので比較的楽しく読んでいたが、後半にいくにつれて低レイヤー感が増していきけっこう読むの大変だった。
とはいえ、IO処理、システムコール、ソケット通信、OS、メモリといった低レイヤーの知識をGoを通して学ぶことのできる貴重な書籍だと思います。
本書にも書いてありましたがそういった低レイヤーのことを学ぶのにはCやC++のような言語がほとんどのためCやC++を読める必要がある。
Goを利用することでCやC++のような言語を使わずに低レイヤーの学習をすることができた。
Go自体の知識や理解も上がり、Goに対してのキャッチアップにもつながりました。
本書はどちらかと言えば難解な技術書の部類かなとは思っていて、一応全部読んだは読んだけど4割程度しかおそらく理解できていない。
せっかくなので、メモリの話と実行ファイルの実行の処理の流れはちゃんと理解しようと思い、全部読み終わった後にもう一度その章だけ読み直したが一回では理解できず、2、3回読み直してやっと理解できた。
一朝一夕でここらへんの知識が付くわけはないので、今後くり返し学ぶことで知識を定着させていきたい。