LoginSignup
25
21

More than 5 years have passed since last update.

学習目的のコードリーディングはLinuxの基本コマンドから始めよう

Posted at

急いでる人向け

morecatのソースは読み始めるのに丁度いいよ。

はじめに

※ コードの中身の話はしません

昔話だけど「プログラミングの勉強のためにソースコードを読みたい」という人に、よく「UNIX/Linuxのコマンドのソースを見るといいよ」と言っていたんだけど、アプリ開発(とくにC言語)からしばらく離れてるので、すっかりそういうアプローチがあったことを忘れてて…

で、最近とあるイベント(飲み会)で、データベース界隈では大変有名な方に「『昔zakiさんにコマンドのソースみるといいよ』と言われたのが自分の中で大きかった」という話を(私はすっかり忘れていたんだけど…汗)ご本人から聞いて、せっかくの機会なので、以前どんな風にコードリーディングをやっていたかを簡単にまとめてみました。

OSSのコードというとApache Web Serverとかのサーバ系ミドルウェアなんかを想像することが多いかもしれないけれど、そんな 大きな プロダクトのソースを最初から読む必要はありません!何事も小さなところから始めましょう!

対象読者

MUSTではないですが、こういう人はとくにお勧めです。

  • Linuxコマンドをよく使う人(Linuxに限らずCygwin含む…あ、macOSも大丈夫ですね)
  • 普段コーディングはよくするけど、ほかの人のコードを見ることが少ない人
  • コーディングはするしほかの人のコードも見るけど、自社開発でクローズドなプロダクトのコードしか見る機会がない人
  • OSSに興味ある人

以下基本的にC言語の話になりますが、ほかの言語でも同じだと思います。

なぜLinuxのコマンドのソースなのか

  • よく使うコマンドのソースであればどんな処理なのか想像しやすい
    • コードの内容が難しくても、ある程度想像で補いつつ読み進められる
    • 「いつも使うあの機能はどこで実装されているんだ」という視点でも読み進めやすい
    • 知らなかった/隠された機能が見つかることも?
  • ソースの全量が少なく最後まで読んでも負担にならない
    • コマンド名=ソース名であることが多く、読み始めやすい
    • エントリポイントも比較的わかりやすい
    • ゴールが短いと達成感も得やすいので続けやすい
    • 逆に例えばLinuxカーネルのソースを読もうと思っても、広大すぎてどこから手を付ければいいかわからない
  • 単機能であることが多く、高度な前提知識が不要
    • 本処理がソースファイル1本(プラス、共通処理でいくつか別ソース程度)だったり
  • 入手しやすい
    • オープンソースですから
    • 歴史が長いものは(本来の意味で)レガシー

注意点として、機能が豊富なコマンド(OpenSSLとか…)のソースは難易度高いので最初は別のものにしましょう。(目安として、サブコマンドがあるものは大きすぎる)

ソースコードの入手方法

ググればだいたいすぐに出てくると思うので、改めて書くまでもないかもしれないけど、探し方を簡単に。

  • 公式サイト
  • ディストリビューションのサイトのパッケージ検索

    ここに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
        

        でてきました。

      • 参考

    • Cygwin

      • パッケージ名の確認

        zaki@mascarpone% cygcheck -f /bin/more
        util-linux-2.33.1-1
        
      • パッケージのソースコード取得をチェックする

        image.png

        image.png

        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の行を追加する。

/etc/apt/sources.list
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()をコールしている、という図)

image.png

書籍

Code Reading ~オープンソースから学ぶソフトウェア開発技法~ (プレミアムブックス版) | Diomidis Spinellis, まつもと ゆきひろ, 平林 俊一, 鵜飼 文, トップスタジオ |本 | 通販 | Amazon

こんなも本ある。
以前読んだんだけど、、探したけど見つからない…

昔書いた感想メモにこんなこと書いてた。

低品質のコードは、次の点に着目すればすぐ見分けることができます。

  • コーディングスタイルに一貫性がない
  • 意味もなく複雑な構造や理解不能な構造が使われている
  • 明らかな論理ミスや手抜きがある
  • 移植性のない構造が多用されている
  • 保守されていない

おぉ、全部当てはまってる…


自分にあった愛読書(ソースコード)をみつけてください。

25
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
21