急いでる人向け
more
やcat
のソースは読み始めるのに丁度いいよ。
はじめに
※ コードの中身の話はしません
昔話だけど「プログラミングの勉強のためにソースコードを読みたい」という人に、よく「UNIX/Linuxのコマンドのソースを見るといいよ」と言っていたんだけど、アプリ開発(とくにC言語)からしばらく離れてるので、すっかりそういうアプローチがあったことを忘れてて…
で、最近とあるイベント(飲み会)で、データベース界隈では大変有名な方に「『昔zakiさんにコマンドのソースみるといいよ』と言われたのが自分の中で大きかった」という話を(私はすっかり忘れていたんだけど…汗)ご本人から聞いて、せっかくの機会なので、以前どんな風にコードリーディングをやっていたかを簡単にまとめてみました。
OSSのコードというとApache Web Serverとかのサーバ系ミドルウェアなんかを想像することが多いかもしれないけれど、そんな 大きな プロダクトのソースを最初から読む必要はありません!何事も小さなところから始めましょう!
対象読者
MUSTではないですが、こういう人はとくにお勧めです。
- Linuxコマンドをよく使う人(Linuxに限らずCygwin含む…あ、macOSも大丈夫ですね)
- 普段コーディングはよくするけど、ほかの人のコードを見ることが少ない人
- コーディングはするしほかの人のコードも見るけど、自社開発でクローズドなプロダクトのコードしか見る機会がない人
- OSSに興味ある人
以下基本的にC言語の話になりますが、ほかの言語でも同じだと思います。
なぜLinuxのコマンドのソースなのか
- よく使うコマンドのソースであればどんな処理なのか想像しやすい
- コードの内容が難しくても、ある程度想像で補いつつ読み進められる
- 「いつも使うあの機能はどこで実装されているんだ」という視点でも読み進めやすい
- 知らなかった/隠された機能が見つかることも?
- ソースの全量が少なく最後まで読んでも負担にならない
- コマンド名=ソース名であることが多く、読み始めやすい
- エントリポイントも比較的わかりやすい
- ゴールが短いと達成感も得やすいので続けやすい
- 逆に例えばLinuxカーネルのソースを読もうと思っても、広大すぎてどこから手を付ければいいかわからない
- 単機能であることが多く、高度な前提知識が不要
- 本処理がソースファイル1本(プラス、共通処理でいくつか別ソース程度)だったり
- 入手しやすい
- オープンソースですから
- 歴史が長いものは(本来の意味で)レガシー
注意点として、機能が豊富なコマンド(OpenSSLとか…)のソースは難易度高いので最初は別のものにしましょう。(目安として、サブコマンドがあるものは大きすぎる)
ソースコードの入手方法
ググればだいたいすぐに出てくると思うので、改めて書くまでもないかもしれないけど、探し方を簡単に。
-
公式サイト
- 例えば
cat
のソースでればCoreutils - GNU core utilitiesに含まれている
- 例えば
-
ディストリビューションのサイトのパッケージ検索
- Debian: Debian -- パッケージ
- Ubuntu: Ubuntu – Ubuntu パッケージ検索
- CentOS、以前あったような気がしたけど見つからない…
ここに
bin/cat
とかで検索するとどのパッケージに入っているか確認できる。 -
パッケージの機能で検索・取得
-
apt
-
コマンドがどのパッケージに入っているか確認
zaki@chaource:~$ which cat /bin/cat zaki@chaource:~$ dpkg -S /bin/cat coreutils: /bin/cat
-
パッケージのソースを取得する(root権限不要)
zaki@chaource:~$ ls zaki@chaource:~$ apt source coreutils Reading package lists... Done Need to get 5,294 kB of source archives. Get:1 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic/main coreutils 8.28-1ubuntu1 (dsc) [2,302 B] Get:2 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic/main coreutils 8.28-1ubuntu1 (tar) [5,252 kB] Get:3 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic/main coreutils 8.28-1ubuntu1 (asc) [1,196 B] Get:4 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic/main coreutils 8.28-1ubuntu1 (diff) [37.9 kB] Fetched 5,294 kB in 3s (1,621 kB/s) dpkg-source: info: extracting coreutils in coreutils-8.28 dpkg-source: info: unpacking coreutils_8.28.orig.tar.xz dpkg-source: info: unpacking coreutils_8.28-1ubuntu1.debian.tar.xz dpkg-source: info: applying 61_whoips.patch dpkg-source: info: applying 63_dd-appenderrors.patch dpkg-source: info: applying 72_id_checkngroups.patch dpkg-source: info: applying 80_fedora_sysinfo.patch dpkg-source: info: applying 85_timer_settime.patch dpkg-source: info: applying 99_kfbsd_fstat_patch.patch dpkg-source: info: applying 99_float_endian_detection.patch zaki@chaource:~$ ls coreutils-8.28/ coreutils_8.28.orig.tar.xz coreutils_8.28-1ubuntu1.debian.tar.xz coreutils_8.28.orig.tar.xz.asc coreutils_8.28-1ubuntu1.dsc zaki@chaource:~$
-
-
Yum
-
パッケージの検索
[vagrant@vagrant-01 ~]$ which grep alias grep='grep --color=auto' /usr/bin/grep [vagrant@vagrant-01 ~]$ rpm -qf /usr/bin/grep grep-2.20-3.el7.x86_64 [vagrant@vagrant-01 ~]$
-
ソースの取得
- aptと異なりソースを取得する標準機能はまだないっぽいので、
yum-utils
パッケージに含まれているyumdownloader
コマンドを使用する。
[vagrant@vagrant-01 ~]$ which yumdownloader /usr/bin/yumdownloader
[vagrant@vagrant-01 ~]$ yumdownloader --source grep-2.20-3.el7.x86_64 Loaded plugins: fastestmirror Enabling updates-source repository Enabling base-source repository Enabling extras-source repository base | 3.6 kB 00:00 base-source | 2.9 kB 00:00 extras | 3.4 kB 00:00 extras-source | 2.9 kB 00:00 updates | 3.4 kB 00:00 updates-source | 2.9 kB 00:00 (1/7): base/7/x86_64/group_gz | 166 kB 00:00 (2/7): extras/7/x86_64/primary_db | 188 kB 00:00 (3/7): updates/7/x86_64/primary_db | 4.2 MB 00:00 (4/7): extras-source/7/primary_db | 25 kB 00:00 (5/7): updates-source/7/primary_db | 113 kB 00:00 (6/7): base-source/7/primary_db | 966 kB 00:07 (7/7): base/7/x86_64/primary_db | 6.0 MB 00:21 warning: /home/vagrant/grep-2.20-3.el7.src.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY Public key for grep-2.20-3.el7.src.rpm is not installed grep-2.20-3.el7.src.rpm | 1.2 MB 00:01 [vagrant@vagrant-01 ~]$ ls grep-2.20-3.el7.src.rpm [vagrant@vagrant-01 ~]$
これで
yumdownloader
実行時のディレクトリに、src.rpmファイルが置かれる。
src.rpmファイルを展開するにはrpm2cpio
コマンドを使用する。[vagrant@vagrant-01 ~]$ rpm2cpio grep-2.20-3.el7.src.rpm | cpio -id 2522 blocks [vagrant@vagrant-01 ~]$ ls colorgrep.csh grep-2.20-man-fix-gs.patch colorgrep.sh grep-2.20-pcre-backported-fixes.patch grep-2.20-3.el7.src.rpm grep-2.20.tar.xz grep-2.20-CVE-2015-1345.patch grep-2.20-w-multibyte-fix.patch grep-2.20-egrep-fgrep-symlinks.patch GREP_COLORS grep-2.20-help-align.patch grepconf.sh grep-2.20-long-pattern-speedup.patch grep.spec grep-2.20-man-fixed-regexp-option.patch
rpm関連ファイルが出てきただけで、まだソースファイルは展開されていない。
grep-2.20.tar.xz
を展開する。[vagrant@vagrant-01 ~]$ tar xf grep-2.20.tar.xz [vagrant@vagrant-01 ~]$ ls grep-2.20 ABOUT-NLS ChangeLog COPYING lib NEWS src aclocal.m4 ChangeLog-2009 doc m4 po tests AUTHORS config.hin gnulib-tests maint.mk README THANKS build-aux configure GNUmakefile Makefile.am README-alpha TODO cfg.mk configure.ac INSTALL Makefile.in README-release [vagrant@vagrant-01 ~]$ [vagrant@vagrant-01 ~]$ ls grep-2.20/src/ dfa.c dosbuf.c grep.h kwset.h pcresearch.c system.h dfa.h egrep.sh kwsearch.c Makefile.am search.h dfasearch.c grep.c kwset.c Makefile.in searchutils.c
でてきました。
- aptと異なりソースを取得する標準機能はまだないっぽいので、
-
参考
-
-
Cygwin
-
パッケージ名の確認
zaki@mascarpone% cygcheck -f /bin/more util-linux-2.33.1-1
-
パッケージのソースコード取得をチェックする
zaki@mascarpone% ls /usr/src/util-linux-2.33.1-1.src 2.24.2-agetty.patch 2.32.1-testsuite.patch 2.24.2-libblkid-topology.patch util-linux.cygport 2.24.2-libintl.patch util-linux-2.33.1.tar.xz 2.25.1-cygwin-include.patch zaki@mascarpone%
-
-
読み方の例
あくまで例なので自分のスタイルを見つけてください。
- 小さなコマンドあればだいたい「コマンド名=ソースファイル名」になっているのでそれを探す
- たいていは「
main()
から呼び出される関数」の実装がファイル先頭からいくつかあり最後にmain()
があるので、main()
から見始める - 関数先頭はだいたい「変数の定義」「初期処理」「オプションの解析」なので、さらっと流してその次のメイン処理から見る
- オプションの解析等はそれ自体も(自作ツール作ったりるす際に)参考になったりするので、そこに集中するのもアリ
- 関数呼び出しは関数名からなんとなく意味を察して中を見ずにとりあえず
main()
を読み進める-
main()
の中に主処理がない場合は都度、関数の中を見る
-
設定/ツール類
以下、参考情報です。
aptのソース取得設定
デフォルトではソースの取得がaptlineの設定に入っていないかもしれない。
その場合は、deb
と同じ内容でdeb-src
の行を追加すればよい。
zaki@chaource:~$ apt source coreutils
Reading package lists... Done
E: You must put some 'source' URIs in your sources.list
deb-src
の行を追加する。
deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic main universe restricted multiverse
deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security main universe restricted multiverse
deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates main universe restricted multiverse
deb-src http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic main universe restricted multiverse
deb-src http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security main universe restricted multiverse
deb-src http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates main universe restricted multiverse
変更したらapt update
して内容を適用する。
root@chaource:/etc/apt# apt update
Hit:1 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic InRelease
Get:2 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security InRelease [88.7 kB]
Get:3 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates InRelease [88.7 kB]
Get:4 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic/universe Sources [9,051 kB]
Get:5 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic/multiverse Sources [181 kB]
Get:6 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic/main Sources [829 kB]
Get:7 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic/restricted Sources [5,324 B]
Get:8 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security/universe Sources [128 kB]
Get:9 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security/main Sources [86.0 kB]
Get:10 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security/restricted Sources [1,504 B]
Get:11 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security/multiverse Sources [2,744 B]
Get:12 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security/main amd64 Packages [331 kB]
Get:13 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security/main Translation-en [118 kB]
Get:14 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security/universe amd64 Packages [242 kB]
Get:15 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-security/universe Translation-en [139 kB]
Get:16 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates/main Sources [264 kB]
Get:17 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates/universe Sources [236 kB]
Get:18 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates/restricted Sources [2,068 B]
Get:19 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates/multiverse Sources [4,612 B]
Get:20 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates/main amd64 Packages [597 kB]
Get:21 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates/main Translation-en [219 kB]
Get:22 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates/universe amd64 Packages [931 kB]
Get:23 http://ftp.jaist.ac.jp/pub/Linux/ubuntu bionic-updates/universe Translation-en [270 kB]
Fetched 13.8 MB in 7s (1,920 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
82 packages can be upgraded. Run 'apt list --upgradable' to see them.
root@chaource:/etc/apt#
Visual Studio Code …と思ったけど、愛用のエディタでどうぞ
VS Codeであればsrc
ディレクトリをVS Codeで開く。
C/C++
の拡張機能が入っていなければ入れる。
これで関数ジャンプなどの便利機能がすぐに使えるようになる。
他にはctags・etagsや、grepコマンド等を併用しても良い。
コードフォーマッタ
宗教上の理由などでどうしても書かれているコードのスタイルがなじまなくて読みづらい場合は、そこで苦労するよりは、エディタの機能やAStyleなどを使って思い切ってフォーマットをかけて読みやすいように変換しましょう。
とくにcoreutilsのgnuスタイルは所見だと制御構文まわりはちょっと癖があるのでstroustrupとかの方がいいかも。
Doxygen + Graphviz
今ってこの辺のドキュメンテーションツールのデファクトってなんだろう。
Doxygenを使うとソース内のコメントからドキュメンテーションしてくれるけど、Graphvizを連携させることで図の生成もできるようになり、関数のcall/callerグラフも作ってくれて、処理の流れをより追いやすくなる。
(cat.cの作成例 / main()
からusage()
をコールしている、という図)
書籍
こんなも本ある。
以前読んだんだけど、、探したけど見つからない…
昔書いた感想メモにこんなこと書いてた。
低品質のコードは、次の点に着目すればすぐ見分けることができます。
- コーディングスタイルに一貫性がない
- 意味もなく複雑な構造や理解不能な構造が使われている
- 明らかな論理ミスや手抜きがある
- 移植性のない構造が多用されている
- 保守されていない
おぉ、全部当てはまってる…
自分にあった愛読書(ソースコード)をみつけてください。