ユユユユユ

webエンジニアです

K&R を読み通した

 K&R を読みはじめた - ユユユユユ より1ヶ月で、ひとまず通読した。

f:id:jnsato:20210905153156j:plain

 ANSI C の大きくない仕様を一冊で網羅する、という能書きで始まる。実際、シンタックスの説明は用法を折々に交えながらでも、150ページばかりで終わっている。残りの40ページは標準ライブラリとシステムコールの解説であり、やや応用に踏み込む。それでも200ページに収まる。

 名著であるとおもう。それは、 C を発明し、仕様を策定した当事者たちが執筆しているという時点ですでに疑いようがないのだが、実に平易な文体で議論が勧められて、ゼロから C の知識を理解させる、という入門書としてこれ以上の成果はありえないだろう。このような書物が成立するのは、そもそも仕様が十分に小さく、かつ平易であるからにほかならないわけで、 C のようにシンプルな言語がほかにないことをおもうと、このようにシンプルに言語を解説しきる、という書物はもう二度と成立しえないだろう。40年近く前に書かれた本でありながら、いまだ読むに耐える技術書であるということのなんとすさまじいことか。しかもそれは、 C そのものがいまだ利用に耐える言語であるということに照応している。

 ごく短い文量のうちに、読者はごく初歩的なシンタックスの理解からはじめて、最後には malloc の再実装にいたる。読み始める前の僕にこう伝えたら、「いくらなんでもそれは無茶な...」と萎縮するだろう。しかし、ごく単純な言語機能を理解して、ごく単純なルーチンを実装する、ということを積み重ねる手続きにおいて、無茶な飛躍は存在せず、ただ自然な因果の帰結としてそれが成立するのである。恐るべきことである。

 裏を返すと、このように基本的な部品を用意し、それら部品の組み合わせにより、より大きなシステムを作る、というのは、まさしく理想的なプログラミングの営みである。その営みがもっとも純粋な形でこの本には表現されている。それをさておいて、 C は難しいとか、 OS は難しいとか、食わず嫌いを正当化する意見を一方的に持って、無学をひけらかしていた以前の自分の至らなさを省みるきっかけともなった。

 と、殊勝なことを言いつつも、やや手抜きをしながら読み上げたことも事実である。練習問題は序盤から手強いことも少なくなく、終盤に至ってはさらに多くのエネルギーを要求する。これらを逐一踏ん張るよりも、まずは最後まで通し読みすることを優先した。ただそれでもなお、 UNIX の内包するいくつかの重要なデータ構造に関する知識を再発見することができたし、読み物としてもすでに上等な著作であることは言うまでもない。

 それから、入門書であることに疑いはないのだが、読者を選ぶ本であることも明らかである。それなりにプログラミングの素養のある者でなければ、およそ読むことはできないだろう。僕の場合は、 C++ をいくらか利用していたことが少なからずアドバンテージとなって、読み通すことができた。しかしまったくの初心者はいうに及ばず、スクリプト言語の経験のみを資産として本書に挑むことも、果たして可能であるかどうかはわからない。

 まあ、読める人は読めるし、読めない人は読めない、という表現しかしようがなさそうだ。仮に読めないとしても、いずれ読む必然性がどのプログラマにも開かれていることは間違いない。その意味では、万人必携の書ということにもためらいはない。ほかならぬ僕自身も、やがて時がくれば再読しうるし、その折にはあらためて、新しい読みを獲得するだろうという予感がある。

『大学生物学の教科書 1: 細胞生物学』を読んだ

カラー図解 アメリカ版 新・大学生物学の教科書 第1巻 細胞生物学 (ブルーバックス)
https://www.amazon.co.jp/dp/4065137438

 簡単な化学から始まり、やがて難しい化学になる。それを元手にして、タンパク質、脂質、糖質というみっつの高分子の多様性をみていく。ここまでが前半である。

 後半は、細胞の観察をおこなう。原核細胞の相対的に単純な構造をみたうえで、真核細胞のいっそう複雑な構造をみる。細胞小器官、細胞外構造、細胞膜をみる。そのご、最後の仕上げとして、細胞間の情報伝達が化学物質(リガンド)の結合をトリガーにおこなわれることをみる。

 生物、あるいは生命維持のメカニズムが、実にシステムのアナロジーでうまく説明できること。ごく表面的に理解したのみではあれ、感動を呼び覚ますには十分であった。 DNA が情報を暗号化して保存し、それを折々に復号・複製する。あるいはそれを設計図にしてタンパク質を産生する。はたまた、ある信号に反応する受容体と反応しない受容体があって、細胞は選択的・特異的に応答をおこなう。ある信号からはじまる反応が、自身の出力を入力にして連鎖的に反応を引き起こすことで、信号が指数的に増幅される。などなど、生命活動の様相がまるで精巧に設計された工学的機械のように描かれる。機械のアナロジーで生命を描写できること。因果としては逆になろうが、そこに興味深さと恐ろしさをないまぜにしたような感慨をもった。

 エラーだらけのシステムと格闘する仕事から離れて、神のみわざと言いたくなるのもうなずける均整美をもった生命というシステムの美しさを学ぶのは法悦である。これだけ完全に近いシステムが僕の身体を駆動しているのに、その僕が生み出す出力の不完全さときたらないな、とシニカルな笑いをついかき立てるところもある。

 精密機器のようでありながら凄まじくダイナミックな、生物学というこの世界への導きとして素晴らしい教科書であるとおもう。この一冊を読めばすべてがわかるようなテキスト、という指向性で書かれている気がする。それも実にアメリカの教科書らしく、好もしい。

 ひとまず後続のテキストも購入する。そして10月からは、放送大学の「初歩からの化学」も履修する。「初歩からの生物学」という講義もあったけれど、生物を知るうえでの下部構造として化学があるようだから、いささか遠回りになっても悪くはない道だろうと思っている。

K&R を読みはじめた

 The C Programming Language を読み始めた。著者が Kernighan and Ritchie の二人組であることより K&R という愛称で呼ばれるようである。

f:id:jnsato:20210809190137j:plain

 学生の頃に無料で落ちていた PDF 版を手に入れて冒頭を眺めたことを覚えている。そのころは古典を所有するという以上の欲望はなにもなかったので、特に誠実に学ぼうともせずに放置していた。

 いま、世の中が Rust 熱に浮かされている様子なのを眺めて、僕も興味はあるクチであるが、それより先に C の足固めをしようとおもった。 CS:APP が C で記述されていたことに触発されたところもある。そもそも Linux カーネルを題材にシステムプログラミングを学ぼうとするときに、 C 以外のなにを使えるだろうか?

 付録を含めてもたかだか 200 ページばかりの、薄い本である。仕様の小さな言語であるから、この薄さのなかにすべてが書かれていると、前書きでも誇らしく書かれている。

 実際にその「小さな仕様」で練習問題を解くと、あらゆる小さなルーチンを書くことになる。そしてそのいちいちについて、「はたしてこれで正しくできているだろうか」と疑い深くなる。いかに普段、便利なライブラリに守られているかを痛感させられるし、それを再実装する自分の腕のおぼつかなさに愕然とする。しかし、やってできないわけではない。じっくり取り組めば、時間はかかるが書ききることはできる。

 どうにも自信がなければ、参考書として Answer Book も用意している。こちらももちろん C で書かれているし、悪魔的に難しいことはやっていないので、読めないことはない。加えて、標準ライブラリの実装をみたければ、みればいいだけのこと。 glibc をクローンして、適当なヘッダやら実装を眺めていると、よくわからないマクロに囲まれてはいても、これこそが battle-tested なソースそのものであるという安心感を与えられる。実に、僕の生まれたころに積まれたコミットをみると、崇高な思いがする。

 という調子で、しばらくは C の世界で遊んでみようとおもう。終盤には UNIX システムプログラミングの話にも接続するようなので、最後まで飽きずに取り組んでゆきたい。

CS:APP を読み終えた

f:id:jnsato:20210806222526j:plain

CS:APP を読みはじめる」という記事を書いてから足掛け3ヶ月にして、読了に漕ぎつけた。案外ペースとしては悪くない。各章末に配された練習問題はスキップする方針で読んだ。平日には長くても1時間、週末には気が向く範囲で数時間といったくらいの時間を割り当てて、読み通した。少なくとも数十時間はつぎこんでいる。まあ大作ビデオゲームをそこそこ楽しく遊んだくらいのものとおもうと、そうたいしたものでもないか。読みながら「ここは」とおもった折にメモを残していたので、それを書き写そう。


 第4章、パイプラインプロセッサの設計のところがまず難関であった。途中から完全に(なにもわからない)という状態になり、とりあえず話を目でなぞってお茶を濁すことにした。それまでの議論はそれなりに追従できていたはずなのだが、にわかに力技の議論で押し通されるようになったゆえに置いてきぼりにされてしまったのかもと疑ってはいる。論の運びがパワフルなところが美点でも欠点でもある、とは訳者序文にても指摘されていた。まあ、 CPU はパイプライン化により高速化を図っており、それを補助するためにストールとかフォワーディングといった実装がある、というところを知識として持てただけでよしとしよう。実装の詳細については、また機会があったときに再訪すればよい。


 第9章、仮想メモリの話もタフだった。とりわけ、アロケータの実装を「簡単に」といいつつ詳解していく手際には置いていかれるよりなかった。もっとも、実装レベルで学ぶことを目的とせずに読んでいたこちらの怠惰が跳ね返されただけともいえる。仮想アドレシングの手続きはハードウェア層から OS までが相互に協調して最適化を与えているというあたり、「すごい技術だ...」と呆然とするほかなかった。「システムはモジュラーであるべき」というドグマがいっぽうにありつつ、ここぞというところでは結合を密にすることで速度をあがなうということは理解はできる。しかしアドレス参照というスーパークリティカルなシステムを動かすにあたってハードウェアと OS が共依存関係を構成していること、そしてそれがメモリ階層のシステムや追加のキャッシュもあわせて素晴らしいコレオグラフを描き出していることに感動した。ほんのすこしだけ表面をなぞったにすぎないわけではあるが、強く印象に刻まれた。


 など、困難を感じたところのメモから書き起こした。相対的に、残りの章については印象が薄かったかのような誤解を与えかねない。ところが事実は、すべての章がいちいち素晴らしい学びに満ちていて、とても整理しきれないのである。二進数表現のありきたりな話からはじまって、「そんなことは知ってるよ...」とこちらを飽きさせる暇もなく、浮動小数点のエンコード方式のような、いままでわかったようで実はわかっていなかったトピックを叩きこまれた。さらにそれらの演算命令がどのように機械語エンコードされるかをみたかとおもうと、最適化コンパイラの設計思想とその最適化の限界を暴き出してみせ、キャッシュメモリの階層化の説明を付け加えることによって、「プログラマの視点から」どのようにハードウェアをコントロールして最高のパフォーマンスをチューニングするかを鮮やかに描きだす。凄まじい熱量をもって議論が展開していくのを眺めるのは実に気持ちがいい。

 さらに高レベルに議論は進み、リンカの話、カーネルの例外制御の話がはじまる。仮想メモリやシステムレベルI/Oの話題となると、ハードウェアとソフトウェアの相互作用が語られるようになり、これも描写しがたいおもしろさだ。「ネットワークはデバイスである」という表現の意味がわかっていなかったのだが、ファイルデスクリプタという概念をおよそ理解できたことで、それも乗り越えられたようにおもう。初学者のころにソケットというものの意味がなにもわからずに退けていたことを思い出しながら読んだ。

 ひとつひとつ数え上げては感想を述べても飽きないが、それでもなお未知の部分が残っていることに疑いはない。わかった気になれているのは、僕の想像力が足りずに、わかったようになっているだけともいえる。 Nand2Tetris を読んだうえで CS:APP に進んできて、ハードウェアの世界とソフトウェアの世界の接合部についてはかなり手応えをもって知ることができたようにおもう。しかしそれでもなお、せいぜい学びの第一歩を踏み出したにすぎないこともはっきりと理解している。「なにがわからないかわからない」という状態であったところをひとまず脱して、コンピュータアーキテクチャの概略をおおむねマッピングできた、というあたりに自分を位置付けている。

 各章末に配された「書誌ノート」も卓越している。トピックごとに、読むべき古典を提示してくれている。正しくアカデミックな手続きで書かれており、いつでもここに立ち返って再読し、関連文献をあらためて逍遥することができる。

 一生読める一冊であるから、値段が 16k もしようが、あきらかにその価値はある。翻訳については、いくらかミスも見受けられるが、読める訳文である。こちらに前提知識が欠落しているため、はじめから原著で読むことは、不可能ではないとはいえかなりの時間を要したろう。しかしこの訳書で羅針盤を得たので、後続の学習は英語でも恐れずに進められるようにおもう。

シグナルを送ることで他のプロセスに割り込みをおこなう

シグナルとは、小さなメッセージのようなもので、何らかのイベントがシステム内で発生したことをプロセスに通知するものである1

 ハードウェアレベルで例外が起こるとする。たとえばあるプロセスがゼロ除算エラーを発生させるとする。通常はカーネルがこの例外を補足してハンドルするため、個々のユーザープロセスは個別にハンドラを定義しないし、そうする必要もない。要するに、ユーザープロセスにはこの例外は見えない。

 この種の例外の発生をイベントとして通知して、ユーザープロセスが例外を検出することができるようにするのがシグナルである。ゼロ除算エラーの場合、カーネルは SIGFPE (8) をユーザープロセスに送る。

 シグナルを受信したユーザープロセスは、シグナルを無視したり、実行を終了したり、ユーザー定義のハンドラを実行してシグナルをキャッチすることができる。なにもしない場合、シグナルごとに定義されたデフォルトアクションが実行される。たとえば SIGKILL (9) のデフォルトアクションはプロセスを終了させることである。

 シグナルは、低レベルの例外を検出したカーネルからユーザープロセスに送信されることもあれば、ユーザープロセスからユーザープロセスに送信されることもある。つまり、システムイベントの通知がシグナルとして抽象化された結果として、ユーザーが任意のシグナルを発行して、あたかもあるシステムイベントが発生したかのようにプロセスを制御することも可能にされたわけである。

 シグナルとそのデフォルトアクションのリストは、さまざまなところに掲示されている。ひとまず wikipedia のテーブルをみるのが無難とおもう。

https://en.wikipedia.org/wiki/Signal_(IPC)#Default_action

 たとえば kill コマンドをこうやって使うことは、駆け出しエンジニアのころに意味も知らないままに覚えて、以来無批判に使っている。

kill -9 pid

 引数にあたる -9 は SIGKILL に割り当てられた数字であり、これはプログラムの強制終了を指示するシグナルである。こう書いても同じことを意味することになる。

kill -s KILL pid

 引数 pid に負数を渡すと、プロセスグループに対するシグナル送信とすることができる。手元のマシンの kill のマニュアルにはその記載はなく、試してみてもいないのでわからない。とはいえ使う機会があるかもしれないので知識としてのみ覚えておく。

 unicorn の起動スクリプトに埋め込んでいた HUP や USR2 のシグナルの意味もいまならわかる。

 tablexi/capistrano3-unicorn は QUIT, HUP, USR2, TTIN, TTOU といったシグナルを送信するタスクを定義している2。これらのシグナル群について、 unicorn がシグナルハンドラを提供している3。この仕様に沿ってシグナルを送信することで、プロセスをたとえばグレースフルリスタートさせるような制御をおこなうことができるわけである。

rails/rails にはじめてのコントリビュートをした

rails/rails に初めての PR を出して苦い気持ち と書いていた。結局、そのあと二往復ばかりのレビューのやりとりがあって、無事にマージされた。これでぼくも rails コントリビュータのはしくれといえるだろうか。ふわふわした気分でいる。

rails/rails#42867

みてのとおり、たいしたことをしたわけではない。業務で読み書きするコードのほうがはるかに高い出力を要求される。それはたしかである。

でも、これは誰にやれといわれるでもなく、ぼく自身がやりたいからやった大切な仕事である。それをきちんと成就させられて、こんなにいい気持ちになることをぼくは知らなかった。もっとたくさん、開かれた世界でコードを書いてみたい。自分で自分を満足させられるような仕事をもっとしたい。そういう熱意が燃え上がってやまない...というまでに苛烈ではまだないが、すくなくともその火種になるべき刺激をあたえられている。

そしてそれを活かすも殺すもぼく次第なのである。

rails/rails に初めての PR を出して苦い気持ち

rails レポジトリのイシュー一覧をみていたら、 good first issue のついた手ごろな問題があった。

rails/rails#42862

初コントリビュートのチャンスかとおもって、ドキドキしながらパッチの準備をした。環境を整えて、修正して、テストを動かして、コミット。関連する PR やイシューから情報を取捨選択して、コミットメッセージもできるかぎり丁寧に書いた。「これで大丈夫か?」となんどもダブルチェックをして、提出した。

で、みると2分差で競り負けていた。僕がダブルチェックをしている隙に先を越された。

rails/rails#42866
rails/rails#42867

f:id:jnsato:20210725193437p:plain

競争相手はほとんど同じ変更セットを、最小限の説明文で提出している。こちらが初めての PR 提出であるのに対して、彼はこれまでにいくつかの変更をマージしており、 CI も承認を必要とせずに通っている(初めてのパッチを作成した開発者は CI の実行にメンテナの承認がいるようす)。

これは負けたな、とおもっている。悔しいが、まあまずは提出までをこなせたことでよしとすべきなのだろう。細部に気をつけたせいでスピードで負けるというのはありがちな話であるが、ここにおいては丁寧は明らかに美徳とおもうので、反省はしない。

学びはたしかにあった。おまけとして、なにかしらのレスポンスがもらえると嬉しいなとおもうが、はたしてどうなるか。ひとまずは close もせずに置いておくことにする。