Nginxのコードを読むために準備したこと
はじめに
僕には無理な話だった。
いろいろな事情があって、 nginxのモジュールの仕組みなどを知る必要が出てきた。 チョットデキル人になる必要はないが、完全に理解する必要があり、重い腰をあげてソースコードを読むことにした。
読み始めて3時間で後悔し、何週間かで完全理解するまでに自分が行ったことをメモとして残しておく。
読むにあたって準備したもの
今回、僕がNginxを読むためにいくつか準備したものがあるので記述しておく
今回Nginxのソースはgithubのものを用いることにし、リバースプロキシの部分を追いたかったので、 curl でリクエストしたら hello が返ってくるような ping 程度のAPIサーバとnginxサーバを Docker を用いて用意することにした。
またnginxのソースコードは非常に抽象化されており、僕のC言語のスキルでは太刀打ちできる気配もなかったため、gdb を用いてブレークポイントを貼ることで順次コードを追うアプローチをとることにした。僕にはvoidポインタが何者にも見えなかった。
gdb でデバッグするにあたって公式のDebugging NGINX を参考に、 --with-debug を有効にするなど、あらかじめ nginx をデバッグできるようなオプションをつけてビルドを行った。
余談だが、 上のような構成で gdb を起動する場合、 Docker 内で gdb -p ${pid} によってプロセスへアタッチする必要があるが、権限周りでオペレーションが拒否されたので注意が必要。
僕は docker の --privileged オプションを使って gdb を実行することで回避した。
Nginxについて理解する
ソースコードを読む前に、そのソフトウェアがどういった背景や設計思想(アーキテクチャ)の元開発されたものかを知ることは非常に大事だと思う。
ソースコードの読解効率にも関わってくるし、書かれたコードの裏側にある意図をきちんと理解するためにもまずはソフトウェアの全体像を知っておいた方が良いはず。
というかむしろ全体像さえ知っておけばソースコード読まなくても困らないことの方が多いし、本当に必要にならない限りはコードを読むのはカロリーが高いし僕は身が持たない。 YAGNI の思考でいこう。
ここでは今回僕がソースコードを読む上で知っておいて良かったものに関して、ざっくり簡単に記載しておく。記載内容にあまり自信もなく精度もあまり良くないと感じているので、できればちゃんと自分で調べることをオススメする。
Nginx
エンジンエックスと読む。ンギックスではない。
一言で簡単にざっくり言えば、10K問題を解決したWebサーバーで、多重化されたノンブロッキングI/Oや非同期のイベント駆動アーキテクチャを採用したWebサーバ。
よく比較されていたApacheは、マルチプロセスモデルを採用しており差別化されていたらしい。 なお、Apacheも現在ではいくつかのモデルを採用できるのかもしれないが本筋ではないので調査しておらず不明。
詳しい話はいくつかのLinkを貼るのでそちらを参照してもらえれば良いと思う。下からいくらでもキーワードは抽出できると思うので、あとは自分で調べて欲しい。
Nginxのモジュールについて
この仕組みを引数などにとにかく副作用的に入れ込んで実現しているので読むの辛かった
全体の設計概念よりもう少し内部の実装に近い設計の話になるが、ソースコードを読むにおいては理解しておくべき内容だと思う。
nginx はプラガブルに設計されており、 nginxが持つ様々な機能は module という単位で管理されている。
例えば、リバースプロキシも module の一つとして管理されているし、 main でmaster/worker プロセスが実行される前に呼び出されるコアな機能もCore Module として管理されている。
モジュールにもいくつかあって、コアとしてあるもの、標準モジュールとして提供されているもの、オプションとして提供されているもの、サードパーティ/自作のものがある。
当然プラガブルな実装ということで抽象化されており、各々が好き勝手に実装されているというわけではなくいくつかのインタフェースを実装することで実現されている。
http://mogile.web.fc2.com/nginx/dev/development_guide.html#Modules
もし、自作したい場合はインタフェースに則った実装をすることで作ることができる。
この辺りは、 Hello world のような最小単位の自作モジュールを作っている人もいるのでその辺りを参照すると良いかもしれない。
また、auto/module や auto/modules を読んでみると、どうやら module は nginx をbuildする際に、オプションなどをもとに nginx_modules.c を自動生成することでバイナリに含むようなアプローチをとっているみたい。
ソースコードを読む
ざっくり nginx について理解できたのであとは読むだけ。
nginx が起動しているコンテナ内で worker プロセスに対して gdb を起動し、nginx.conf で指定した port に対して curl でリクエストを投げる。
その後、br などを用いてブレークポイントを仕込んだりして、一歩一歩コードを読み進めていけば良いはず。
docker$ ps aux | grep nginx
docker$ gdb -p ${worker_process_id}
bash$curl http://localhost:${port}/${request_uri}
おわりに
Docker で gdb を用いながら nginx を読もうとして結構色々ハマったので念のため残しておくことにした。
ここではあくまで準備した事しか書いていないが実際に読んで、カーネル弱弱すぎて、epoll の使い方の基本を知ったり色々と勉強になる部分も多かった。
また、nginx の内部をざっくりと理解するという点では、読んで良かったかなとも感じる。
とはいえ学習カロリーが半端ないし、このカロリー消費してやらないといけない事も結構あったりするので、本当に必要になるまでもうこれ以上を読むことはきっとないだろう。優先順位の問題ともいう。モチベーションがもたない
Atcoderは難しい!?彼女はいる?緑色になる方法について調べてみた!!
はじめに
アフェリエイトとオンラインサロンで稼ぎたい。あとは水素水で儲けたい。
コロナで引きこもりすぎてやることが尽きてきたので数年ぶりにブログを書くことにした。

無事緑色になったので、始めた動機ややってきたことなどをとりとめもなく適当に書いていこうと思う。
有名企業に勤めているわけでもなければ、東大京大を出ているようなそういう頭を持っているわけでもない、どこにでもいる一兵卒エンジニアなので安心してほしい(?)
同じようなモチベーションで始めた人の参考になれば少し嬉しいかもしれない。
はじめたきっかけ
ただの隙自語で読んでも得はないので暇人以外は飛ばしてほしい。
きっかけは二つある。
一つは、アルゴリズムに関する知識不足をここ最近少し感じるようになったことだ。
これから30歳に向けてソフトウェアエンジニアとしてどう歳をとっていくかを考え、より横にスキルセットを伸ばそうと考えたときに圧倒的にアルゴリズムやCS知識が足りていないと感じたのだ。
基本的なデータ構造やソートアルゴリズムに関する最低限の知識は持っていたが、少し複雑なものに関してはほとんど知識を持ち合わせていなかった。
アルゴリズムの基礎知識がないとつらいことはたまにあって、例えばある技術基盤の学習や、OSSをソースコードレベルで理解しようとしたときに、意図が理解できないことがあったりする。
例えとして適切かは自信がないが、例えば暗号の基本を理解するには mod の知識が必要だったりする。よりモダンな楕円曲線によるアプローチを理解するには離散数学の知識が必要だろう。
もちろん、特定の分野に対して興味がある人はそこを入り口にして基礎的な部分へ深掘りしていく方が良いと思うし、僕もそういう状況であればそういうアプローチをしたと思う。 ただ僕にはそういう分野が思いつかなかったから競技プログラミングをすることにした。また勝つことが好きで負けることが嫌いな自分にはちょうど良かった。
これは完全に余談だが、最近はネットワークや分散トランザクションに関するちょっとした知識を得る必要性があってできる範囲で無理せず気が向いたときにゆっくり勉強していたりするが、競技プログラミングが役立ったという実感はあんまりなかったりする。
もう一つは俗物的な話になるが、外部から見たときの普遍的な肩書きが少しでも欲しかったというところにある。
これについては色々な事情があるので割愛する。
Atcoderを始めた頃のレベル感
緑色になるまでに自分がやったことについて話す前に、最初期の自分のレベル感を書いておく。
- C問題までは時間をかければ解けた
- BFSやDFSくらいの簡単なものについてはあらかじめ知識があった
つまりAやBで詰まっている人がこれを読んでもあんまり参考にならないだろう。
緑色になるための戦略
当然始めたての頃はパフォーマンスなんて全然出なかったし、どうすればパフォーマンスが出るのかも知らなかった。
なのでまずどうすれば緑色になることができるのかについて調べることにした。 先人の知恵がWebにたくさんあったので、それらを読んだところ以下のどちらかをすればなれそうだということがわかった。
- C問題までを早く解く
- D問題までを解く
僕はAtcoderのレートを伸ばすことが目的ではなかったし、早く解くのもあまり得意ではなかったので、D問題までを解くことにしようと思った。
ある程度の速さで4完を目指すという感じの戦略で行くことにした。
4完を目指してやったこと
過去の問題を解く
習うより慣れろ ということでまずはとにかく問題を解くことにした。
土日は気が乗らないことが多いので、週に3~4くらいのペースで解いていないABCのD問題までを解くことを日課とした。今でもやっていて今は水色を目指してEまで解いている。
最初のうちはCまででも結構時間がかかってしまったが、慣れるうちにCまでで10~15分くらいで済むようになったしD問題を含めても30~60分程度で終わるので、無理なく続けられている。
知っていなければ解けないみたいな問題も多々あるので、わからないなら大人しく解説を見ることも必要なことだと思う。
そもそも引き出しにないものが解けないのは当たり前で、身に付けた知識を適切な時に適切に扱えるようになることが大事なことである。
できないにもいくつかレベルがあって、
- 知らない
- 引き出しから出てこない
- 実装できない
このうち 2 まではもう仕方ないと割り切って、3 だけはないように精進する必要がある。
公式の解説(pdf,youtube) はもちろん、ネットに解説ブログもたくさんあるので ABC123 D のように検索すればわかりやすい解説がたくさん出てくる。とても感謝。
ライブラリをもっておく
ライブラリと書いたがそんなに仰々しいものではない。
問題を解いていくといくつかのデータ構造などある程度一般化できるものがあることに気づいた。
僕は Go で問題を解いているが、 Go は Python や C++ に比べて汎用ライブラリが少なく実装が多くなるものもあったりするので、都度都度実装しているのも面倒になってきたのでライブラリ化することにした。
スニペットとして持つという選択肢もあったが、とりあえず github で管理することにした。
一人のWebエンジニアとして
完全な余談。
Webアプリケーションを普段開発しているエンジニアから見たときのAtcoderの問題はどこまで解けると良いのか、みたいなある程度の指針を書いてみる。 また、新卒などのできない前提の採用枠に関しては完全に対象外である。
結論から先に言うと、ある程度信頼を得るにはC問題までは解ける必要があると感じた。早く解ける必要はない。
もちろんリードやシニアレベルではなく、機能(API)開発をするようなエンジニアにおいての話だ。
また、社会に出ているエンジニア全員が全員解けるわけではないのでもし解けていなくても安心してほしい。
要は特別なアルゴリズムを使うことなく基本的なデータ構造や再帰を用いて物事を筋道立てて考えることができる、と言うことを示せるラインがC問題だと思っていて、これができればアプリケーションのレイヤーを作ることはそう難しくない。
このレイヤーで働くエンジニアは仕様を一般化してプログラムに落とし込むことが一番に求められることだからだ。
ただしあくまで論理的思考やコーディング能力が示せるだけなので、これだけできれば良いというわけではないし、逆にこれができていないから悪というわけでもないから、この観点のみで悲観も楽観もする必要はない。
ただしA問題が解けない人は危機感持った方が良いと思う
おわりに
いかがでしたでしょうか
とりあえず勢いで始めて無事緑になったので書いてみた。
僕がAtcoder始めて一番衝撃だったのは、学生ほんまにすごいなということ。 唯一僕が勝っているのが年齢だけってことに気付いちゃったよね。ぴえん。
最近の高校生は授業中に Bluetooth でイヤホン接続して映画見るらしいし、だんだんジェネレーションギャップみたいなのも感じ始めたりしていて、おじさんになってきたなあというなんともいえない気持ちもある。
老害にならないように良い感じに歳取って、将来彼彼女らが社会人になって一緒に働くことになった時に幻滅されないようにガンバリタイトオモイマシタ。
目的はないもののとりあえず何かコードを書きたいのであれば、Atcoder結構おすすめ。
