インフラエンジニアのためのLinuxカーネルソースコード読解入門
3月 10日 @ 5:00 PM - 7:00 PM
スピーカー
発表資料
講義
ゴール
今日のお題: Linux Kernel ソースコードの読み方を覚えましょう
- どこからソースを取ってくるの?から実施。
- プログラムの意味を理解するところまではいかない。
- 雰囲気でカーネルが読めるようになる。
前提知識
C言語
- 本日の資料だけならば、C言語はそこまで必要ではない。
- C言語の基本文法を習得しておこう。
- 関数、ポインタ、マクロを理解する。
- コンパイラ: gcc の拡張機能 (ANSI-C非標準) があって、kernelが使っているものがある。そこがわからないとソース読むのが難しいところがある。
- gccのマニュアルは英語のpdfが公開されているので、そこから学ぶ。
GCC online documentation
アセンブリ言語
- 基本的に知らなくても大丈夫
- カーネルダンプの解析とかで使う程度。プロセッサレジスタの値取るとかクラッシュ箇所のアドレスを取るときに逆アセンブルして落ちた箇所の特定をしたり。
- kernel 処理の末端でアセンブラがでてくることがある。そこもおいかけたい場合は必要になる。
ソースコードを読むために必要なツール
テキストエディタ
- エディタが使えればいいけど、タグジャンプ機能がないと読むのがつらい。覚えましょう。
- 大量にコードがあるので、キーワード検索機能が重要。grep コマンド使えるようになるのがよい。
- Linux なら emacs or vim, Windows ならタグジャンプが使える好きなエディタを。
- Eclipse でも可能
grep コマンド
- 大量のソースを検索
- find コマンドと組み合わせることが多い
- find, grep 単体でディレクトリ再帰検索は重かったりする。
- CLI苦手でsambaにソースおいてWindowsから検索するとかだと遅い…。コマンドラインからサクッと検索するのがやれるようになった方が早い。
find . -name "*.*" -print -type f | xargs grep -n "$1" /dev/null | more
- $1 は引数
- find の後の . はカレントディレクトリ
- * は全てのファイル名を指定。.c ファイルだけを指定したい場合は
"*.c"
という指定の仕方をする。 - -type f は通常のファイルを指定
- xargs使うとbufferをうまいこと加味して検索してくれる。(findの結果が大きすぎてパイプのあとの処理で受けきれずエラーとなることがある)
- -nは行番号を出すオプション
- /dev/null は 1ファイルしかマッチしないときにファイル名を出力させるため
参考資料
せっかくなので Linux を勉強する上での参考文献を紹介。
- 「図解Linuxカーネル2.4の設計と実装」
- ちょっと古いけど素晴らしい連載だった
- LinuxJapan最終号の付録にCDROMがついていて、連載記事PDFがはいっているので、古本とかで手に入るのであれば。
- UNIX MAGAZINEの連載記事
白崎 博生さんの「Linuxのブートプロセスをみる」- ブートプロセスに特化しているが、わからないことが全部解説されている。
- 2004年に書籍化。2014年に改訂版。Amazon リンク
- 初期化とかやるところを抑えると後の話に入っていき易い。むしろ初心者にお勧め。
- UNIX USERの連載記事
西田瓦さんの「GCCプログラミング工房」- Linux kernel にフォーカスしているわけではないが、低レイヤの所を抑えていてわかりやすい。
- 書籍化されていない。「まるまるUNIX USER」を買うと読める。Amazonリンク
- 西田亙さんのゲームボーイ本は大人の事情で復刊しないだろう。
- UNIX USERの連載記事
- Linuxカーネル2.6解読室
- 書籍化されているが廃刊。古本でしか手に入らない。
- オライリー 詳解Linuxカーネル第3版
- わかりやすい。監修がカーネル解読室の高橋さん。
- 拙著
- Linuxカーネル解析入門 (2005執筆,2011改訂) amazonで買えます。イーサネットドライバを読めるようになりましょう、という内容。ちょっと敷居が高い。
- Linuxカーネル ソースコードを読み解く, 今日のハンズオンに近い、コードの読み方を書いた本で中身までは深く追っていない。
- Linuxデバイスドライバプログラミング, オライリーのドライバ本は訳がよくわからないところがある。2008年に出した後廃刊になったが問い合わせが多かったらしく、Amazon print on-demand で再版。
- Linuxのしくみ
- 最近出た話題の本。難しいことを凄くわかりやすく書いてある。
ハンズオン
目標設定(お題)
RHEL7カーネルの「/dev/null」の実装を読んでみる(≠理解する)
RHELカーネル
- カーネルバージョンを調べる
- uname -a
Linux localhost 3.10.0-229.el7.x86\_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86\_64 x86\_64 x86\_64 GNU/Linux
- 上記の結果では、
3.10.0-229
がバージョン。 SMP
はマルチプロセッサに対応していることを指す。Symmetric Multiprocessing/対称型マルチプロセッシング。Fri Mar 6 11:36:42 UTC 2015
はビルドした時間
- uname -a
- RHEL6は2.6.32、RHEL7は3.10。
- CentOSはまったく同じカーネルバージョン。
- 最新のLinuxカーネルは4.15。
- Fedoraは半年に1回リリースされるため、その時々のバージョンををベースとしてRHELが開発およびリリースされる。
RHELカーネル6
- Red HatのftpサイトからSRPM(Source RPM)が入手できる
- (http://ftp.redhat.com/pub/redhat/linux/enterprise/6Server/en/os/SRPMS/ )
RHELカーネル7
- Red Hatがftpサイトで配布をやめてしまったので、CentOSのgithubから。
- カスタマーサポートから入手
CentOSカーネル
- SRPMがすべて公開されている
SRPMの展開方法
-
RHEL/CentOS/Cygwin
-
rpm -ivh kernel-***.src.rpm
⇒このコマンドを実行すると、大体は"ユーザがいない"と怒られる。
SRPMを作った時にbuilder
というユーザがいる状態で作られているため。
~/rpmbubuild に展開される。
毎回、~
に展開されるので、この方法は面倒。 -
rpm2cpio kernel-***.src.rpm | cpio -id
-
-
tarballの展開方法
tar xf linux-***.tar.xz
-
カーネルソースコードの配布形式
- 5まではオリジナルにパッチをつけて配布
- 6からはパッチ適応済みの状態で配布
/dev/null(デブヌル)とは
データの標準出力を捨てることができるゴミ箱というよりブラックホール
システムコール
- straceコマンドで調べる
- strace echo infra
~~~省略~~~~ write(1, "infra\n", 6infra ) = 6 ~~~省略~~~~
- strace echo infra > /dev/null
straceは
echo infra
まではかかっているが、リダイレクトまではかかっていないので、この書き方だと調べられない。 - strace sh -c "echo infra > /dev/null"
- strace echo infra
/dev/nullって?
yutakapon@sf-usr-shell(~) ls -l /dev/null crw-rw-rw- 1 root root 1, 3 Mar 5 19:31 /dev/null
キャラクタ型のデバイススペシャルファイルというもの。
メジャー番号とマイナー番号
- デバイスファイルの番号を調べる
yutakapon@sf-usr-shell(~) ls -l /dev/null crw-rw-rw- 1 root root 1, 3 Mar 5 19:31 /dev/null
1と3に注目
- ドライバを調べる
yutakapon@sf-usr-shell(~) cat /proc/devices Character devices: 1 mem
デバイスドライバ
- drivers/char/mem.c
tar xf linux-***.tar.xz
で展開したディレクトリの中に存在する。
タグジャンプするためtagsファイルを作る
make tags
カーネルを展開したディレクトリで実施する。
ソースを開いてみる
vi drivers/char/mem.c
カーネルを展開したディレクトリで実施する。 エディタはお好みで変えてください。
- 着目する箇所
static const struct file_operations null_fops = { .llseek = null_lseek, .read = read_null, .write = write_null, .aio_read = aio_read_null, .aio_write = aio_write_null, .splice_write = splice_write_null, }; static ssize_t write_null( struct file *file,const char __user *buf, size_t count, loff_t *ppos) { return count; }
/dev/nullへ振り分けられた後、 何もせずreturnしていることが分かる。 → /dev/nullへリダイレクトした内容がどこかへ消えてしまったようにみえる。
チャットで出てきたもの
grep
よりripgrep
を最近は使ってます。- gnu global は使わないのかな? (https://www.gnu.org/s/global/ )
- Debian 派他の人は、rpm2cpio と cpio を使う等して頑張りましょう。
- Red Hat Developer Programを拡張し、無料のRed Hat Enterprise Linuxデベロッパーサブスクリプションを追加
- 環境ない方はCodingGroundでCentOS環境を使えます。
https://www.tutorialspoint.com/unix_terminal_online.php