より良い環境を求めて このページをアンテナに追加 RSSフィード

2008-08-04

[] カーネルコーディングスタイルの続き

http://d.hatena.ne.jp/n314/20080413/1208081924 の続き。というか前に流し読んだ部分をもう一度読む。

http://www.linux.or.jp/JF/JFdocs/kernel-docs-2.6/CodingStyle.html

関数プロトタイプ内には、データの型と一緒に、パラメータ名を含めてください。

これはC言語にとって、仕様上、必要ではありませんが、読み手にとって価値のあ

る情報を加える簡単な方法なので、Linux では推奨されます。


ロックは、参照カウンタの代わりにはなりません。

ロックは、データの一貫性を保つのに使います、一方、参照カウンタは、メモリ管

理において利用されます。通常、両方とも必要であり、互いに混同してはいけません。


実は多くのデータ構造は、異なる "class" のユーザーを持つ場合において、2段

階の参照カウンタを持っています。subclass カウンタは subclass ユーザー数を

カウントし、カウンタが0になった場合に限ってグローバルカウンタをデクリメ

ントします。


この多段階の参照カウンタの例が、メモリ管理に見受けられます(mm_struct

構造体の mm_users と mm_count です)。ファイルシステムのコードにも見受けら

れます(super_block 構造体の s_count と s_active です)。


覚えておいてください - 別のスレッドがあなたのデータ構造を探すことができる

のに、あなたはそのデータ構造に参照カウンタを持っていないとすると、ほぼ確

実にバグを引き起こします。


マクロは大文字が好ましいですが、関数形式マクロは小文字でも構いません。


一般的に、関数形式マクロよりもインライン関数の方が望ましいでしょう。


3) 左辺値として使われる引数マクロ - FOO(x) = y - 例えば、もし誰かがこの

マクロをインライン関数に変更しようとすれば文句を言われるでしょう。


include/linux/kernel.h ヘッダファイルには、明らかにあなた自身がその変種を

コーディングするよりも、あなたが使うべき数多くのマクロを含んでいます。例えば

あなたが配列の長さを計算する必要があるなら、以下のマクロを上手く利用してくだ

さい。


#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))


同様に、もしあなたが構造体メンバのサイズを計算する必要があるなら、次のマク

ロを利用してください。


#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))

2008-04-13

[] カーネルコーディングスタイル

http://www.linux.or.jp/JF/JFdocs/kernel-docs-2.6/CodingStyle.html

via: http://d.hatena.ne.jp/takkan_m/20080413


これは楽しい。

Do not unnecessarily use braces where a single statement will do.

1つの文しか実行しないところに、必要のない括弧を使わないでください。

if (condition)
	action();

if ( 条件文 )
	action();

最近はCでもPHPでもずっとこのスタイル。目がこっちの方が見易くなってしまった。

C言語はスパルタ言語なのですから、それにふさわしく変数や関数を命名しまし

ょう。Modula-2 や Pascalプログラマとは違って、C言語プログラマは 「こ

の変数は一時的に使われるカウンターである(ThisVariableIsATemporaryCounter)」

などというような気のきいた名前は使いません。C言語プログラマは、"tmp" な

どの十分に書きやすく、それでいて少なくとも分かりにくくはならない変数名を

選ぶものです。

大文字小文字を混ぜたような名前は嫌がられますが、グローバル変数には、意味

のわかる名前が必要です。グローバル関数に "foo" などという無意味な名前を

付けることは不快感を与えてしまいます。

これがいつも不思議に思うところ。まぁ単語単位の操作はやりやすい。

もしローカル変数の名前付けに迷っているようなら、別な問題を抱えているもの

です。「関数成長ホルモン不均衡症候群」と呼ばれています。詳しくは第6章(

関数)を見てください。

"vps_t" のような定義は使わないでください。


It's a _mistake_ to use typedef for structures and pointers. When you see a

vps_t a;

in the source, what does it mean?

構造体やポインタに typedef を使うことが間違っています。ソース上で、

「vps_t a;」という文を見たとき、どんな意味があると考えますか?

たまに野良モジュールのソースを見ると*_tが使われているから迷ったりする。が、使っては駄目なのか。

関数内のローカル変数の数も目安となります。ローカル変数は5〜10個にとど

めるべきもので、それを超えているようなら何か悪いことをしているのです。そ

ういう場合は関数を見直し、より細かく分割してください。一般的に、人間の脳

が余裕を持って同時に追えることは7つくらいが限界で、それ以上あると混乱し

てしまいます。自分の素晴らしさは自分が一番知っていることでしょう、がしか

し、2週間前に書いたものがすぐに分かると嬉しいとは思いませんか?

goto 構文の利用に否定的な人もいますが、コンパイラは goto 構文に等しい無

条件 jump 命令を頻繁に出力しているのです。

goto 構文は、関数がいくつかの場所で処理を終了してから共通的な動作(例え

クリーンアップ動作)を行う場合に重宝します。

Cなら普通にgoto使う。PHPにも欲しい。

一般的に、コメントでコードが何をしているのか(WHAT)を示そうとはしますが、

どうやっているのか(HOW)は示さないでしょう。また、関数の中にコメントを記

載することは避けましょう - 関数が複雑なので、別途に関数の説明コメントが必

要と考えるのであれば、第6章を読み直してください。「極めて巧妙なワザ(汚い

手ともいう)を使ったよ」と注意したい時にはちょっとしたコメントを付けても良

いですが、やりすぎないでください。それよりも、関数の先頭にコメントを付与し、

何をする関数なのか、さらに可能であれば、何故それをするのかまで示しましょう。

Linux カーネルにおけるコメントスタイルは、C89 の "/* ... */" スタイルです。

C99 スタイルの "// ..." のコメントは使用してはいけません。

これは開発中で未完成の場合に // でコメントして使い分けたりとか。

なので、GNU emacs の利用をやめるか、設定をまともにするかのどちらかです。

設定の見直しについては、ホームディレクトリの「.emacs」ファイルに次のよう

な追記を行ってください -

(defun linux-c-mode ()
  "C mode with adjusted defaults for use with the Linux kernel."
  (interactive)
  (c-mode)
  (c-set-style "K&R")
  (setq tab-width 8)
  (setq indent-tabs-mode t)
  (setq c-basic-offset 8))

これで M-x linux-c-mode コマンドが定義されます。ソースファイルの先頭2行

のどこかに、「-*- linux-c -*-」という文字列を置いておけば、ハッキングする

際に、自動的に linux-c-mode になります。また、/usr/src/linux にあるソース

ファイルを編集する場合は自動的に linux-c-mode へ切り替えたいというのであ

れば、「.emacs」ファイルに

(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode)
                       auto-mode-alist))

を加えてください。

なるほど。pathで場合分けするのか。早速使うことにする。

インデントにタブを使うんだねえ。


複数の文から構成されるマクロは do - while ブロックで囲むべきです。

#define macrofun(a, b, c) 			\
	do {					\
		if (a == 5)			\
			do_this(b, c);		\
	} while (0)

これはif文の後に処理が一つなら中括弧を付けないコーディングスタイルが元になっているからか。

カーネルメッセージはピリオドで終える必要はありません。

意外。

好ましい構造体サイズの渡し方は次のものです。

	p = kmalloc(sizeof(*p), ...);

これの代わりに構造体型の名前を記述する渡し方をすると、読みにくい事に

加え、ポインタ変数の型を変更した場合、メモリアロケータに渡すサイズが対応

しないバグの温床にもなります。

だいたいの経験則では4行以上の関数はインライン関数にしないことです。この

規則の例外は、引数コンパイル時に定数であることがわかっている場合と、そ

れに伴いコンパイラ最適化によって関数の大部分が削除されると分かっている

場合です。例えば、後者の良い例が kmalloc() インライン関数でしょう。

関数は多くの異なる種類の値を返すことができ、最もありふれたの値の一つは関

数が成功したか失敗したかをを示す値です。そのような値はエラーコードの整数

値(-Exxx = 失敗、0 = 成功)か、「成功したかどうか」を表す boolean(0 = 失

敗、0 以外 = 成功)で表現されます。

この二つの表現が混在したソースには、見つけることの難しいバグが豊かに実り

ます。もしC言語が整数型と boolean 型を明確に区別してくれるなら、コンパイ

ラは私達のためにこれらのミスを見つけてくれるでしょう...しかし区別してくれ

ません。このようなバグを防ぐには、常に次のしきたりに従うことです -

もし関数の名前が動作か命令的な指示だったらその関数はエラーコード

として整数を返すべきです。もし名前が述部(条件の真偽を判定するよう

なもの)であればその関数は成功したかどうか」を表す boolean を返す

べきです。

例えば、"add work" は指示であり、そして add_work() 関数は成功時に0を返す

か、失敗時に -EBUSY を返します。同じように、"PCI device present" は述部で

あり、そして pci_dev_present() 関数は、対応するデバイスを見つけられたら、

1を返し、見つけられなかったら0を返します。

なるほど・・・。意識すると少しはコード探索しやすくなるかな。

[][] カーネルコンフィグメモ

make menuconfig でどのチェックを外すかのメモ。

元々モジュールのものを外してもカーネルのサイズは小さくならない。今回はコンパイル時間を減らすのが目的。

詳しく調べたわけではないので、自分はまず使わないだろうというものだけ。

  • Device Drivers
    • ISDN subsystem
    • Telephony Support
    • Multimedia device
    • Graphics support
      • Support for frame buffer devices
    • Sound card support
  • File systems
    • Reiserfs support
    • JFS filesystem support
    • XFS filesystem support
    • OCFS2 file system support (!)
    • Minix fs support
    • Miscellaneous filesystems
    • Network File System
      • SMB file system support
      • CIFS support
      • NCP file system support
      • Plan 9 Resource Sharing Support
  • Kernel hacking
    • Kernel debugging 以下を適当にオン

コンパイルしている画面を見ていると、もっと省けそう。acpiとか。

その他メモ。

drivers/bluetooth
drivers/hwmon
drivers/i2c
drivers/ieee1394
drivers/infiniband

drivers/input/joystick
drivers/input/mouse
drivers/input/touchscreen その他いろいろ
drivers/media/dvd

drivers/net 以下の使っているNIC以外(ここのコンパイル時間は非常に長い)
drivers/pci/hotplug
drivers/pcmcia

net/ipv6

i2cとかって全部省いてもいいの?あとでやる。

2008-04-12

[] debian etchkdb を入れてlinuxカーネルデバッグ

どこかにメモったはず・・と思って検索しても全然見つからない。

で、色々探してローカルのmeadow使ってhowmでメモしてたことに気付いた。

今見ると結構な数のメモがある・・howmは便利だけどはてなの方が便利なので途中から切り替えたんだった。




# 追記 >>>>>


調べようと思って検索したら、なんかここがトップに出てくるようになってしまった。orz

で、どうやらkernel-patch-kdbは要らないようだ。(ただしman kdbのために入れるのはアリ)

make menuconfig で kernel hacking -> kernel debugging をオンにすれば kdb が使えるようになる。

ということで

make oldconfig
make menuconfig # kernel debugging をオン
make-kpkg --added-patches kdb --append-to-version "-6-686-kdb \
  --revision=2.6.18.kdb --initrd kernel-image

とすればよい。バージョン等は適当に。

kdbを使っている状態では単純なコンソール上の操作しかしないだろうから、 カーネルコンフィグメモ - より良い環境を求めて にあるようにオプションを減らしてコンパイルすると楽。


カーネルヘッダパッケージも同時に作成する場合は

make-kpkg --added-patches kdb --append-to-version "-6-686-kdb \
  --revision=2.6.18.kdb --initrd kernel-image kernel-headers 

ソースからコンパイルしたマシンでは不要だが、別のマシンに入れて開発を行う場合は必要。

# 終了 <<<<<



※ 以下は不要

で、メモの内容をetchで確認しつつコピー。


apt-get install linux-source-2.6.18
apt-get install kernel-patch-kdb

cd /usr/src
tar xvjf linux-source-2.6.18.tar.bz2
cd linux-source-2.6.18

cp /boot/config-2.6.18-6-686 .config
make oldconfig

echo "patch_the_kernel := YES" >> /etc/kernel-pkg.conf

make-kpkg --added-patches kdb --append-to-version "-6-686-kdb \
  --revision=2.6.18.kdb --initrd kernel-image 

dpkg -i ../linux-image-2.6.18-6-686-kdb_2.6.18.kdb_i386.deb
reboot

デバッガのオン・オフは

echo 1 >/proc/sys/kernel/kdb
echo 0 >/proc/sys/kernel/kdb

pauseキーでデバッガの起動。

参考: no title


詳細は man kdb で結構詳しく書いてある。


只今コンパイル中...........後で書き直すかも。


で、コンパイル終わったけど有効にならない。etchだと手順が違うのかな?すぐ解決しそうだったらあとでやる。

2006-12-04

[] カーネルスレッド

メモ

議論/スレッド - Mona OS developers Wiki

ps auxでメモリ使用量が0になっているやつが、カーネルデーモン(カーネルプロセスといわれたりカーネルスレッドと言われたりする)です。

2006-11-19

[] Linuxカーネル2.6解読室

Linuxカーネル2.6解読室

Linuxカーネル2.6解読室


http://d.hatena.ne.jp/heppokoprogram/20061118 ここで紹介されていたので早速購入。

Amazonで買おうかとも思ったけど、すぐ読みたかったので本屋に行ってきた。



この本は素晴らしい。

詳解Linuxカーネル 第2版

[絶版2011.3.17] オペレーティング・システム入門

この二つを足して二で割らない感じ。



取り敢えずPart1を一通り読んで自分の読みたい部分から読むと良い。

まだざっと眺めただけなんだけど、分かり易そう。

日本人がゼロから書いて、更にそれをまとめたものだから良いのかもしれない。


分厚いけれど、カーネルを知りたい人は一通り全部読んだ方がいい。自分が扱う分野以外でも斜め読みできそうな表現だし。

洋書の和訳とかだと日本語に詰まって斜め読みが大変なんだよなー。




ファイルシステムの部分を見た感想としては、図が分かり易かった。

Webや他の書籍にもあるけどカーネル2.2や2.4だったりしてちょっと構造が違ってるんだよね。

文章の説明が短くても図から推測できるのは良い。


ソースコードが結構載ってるんだけど、直に読んでる人にとっては必要ないかもしれない。

昔の本みたいに内容の半分がソースっていうわけじゃないから邪魔にはならないけど。


バッファやキャッシュに関する必要な部分もちゃんと載っている(っぽい)。

概念だけ説明してる本とかはこの部分の実装が分かり難いんだよね。

概念を把握していざソースを読んでみると、キャッシュ処理が多くて訳が分からなくなる。が、この本は分かり易い。

先読みウィンドウの図なんかも知ってる人は常識かもしれないけど、書いてくれると頭がすっきりする。



まだちゃんと読んでないけど、カーネルに関することを知りたい人にはまずお勧めできる本かな。





まだこの本も半分しか読んでなかった・・こっちを読み切って、早くカーネルに取り掛かろう。

2006-10-19

[] lsコマンドの内部動作

メモ。

DIR *opendir()
  __open64 (name, O_RDONLY|O_NDELAY|EXTRA_FLAGS)
    sys_open()
      filp_open()
        open_namei()
          path_lookup()
            link_path_walk() // スラッシュで区切られた各トークンに対してdo_lookup()
              do_lookup()
                real_lookup()
                  down(&dir->i_sem) <- down
                  d_lookup()
                  if (キャッシュが見つからない場合){
                  d_alloc()
                  i_op->lookup()    <--- fs ---
                  }
                  up(&dir->i_sem)   <- up
          __lookup_hash()
            d_alloc()
            i_op->lookup()    <--- fs ---
          i_op->follow_link() // リンクの場合
        dentry_open()
          f_op->open()        <--- fs ---
      fd_install()

struct dirent *readdir()
  __GETDENTS (dirp->fd, dirp->data, maxread);
    sys_getdents()
      vfs_readdir()
        down(&inode->i_sem)   <- down
        f_op->readdir()       <---
        up(&inode->i_sem)     <- up

gobble_file (const char *name, enum filetype type, int explicit_arg,
             const char *dirname)
  int stat(const char *path, struct stat *buf); 
  int fstat(int filedes, struct stat *buf); 
  int lstat(const char *path, struct stat *buf);
    // ディレクトリに含まれるファイルそれぞれに対して行われる
    vfs_stat()
      user_path_walk()
        path_lookup()
      vfs_getattr()
        i_op->getattr(mnt, dentry, stat) <--- fs ---