新しいモジュール・フォーマット(その2)

カーネル2.6のテストバージョンもすでに9回のリリースを数え、Change-Logの量も徐々に減ってきた。先日来日したカーネルのメインテナであるAndrew Mortonが語るように、2.6の正式リリースが近い事の証拠であろう。今回は、前回に続いてカーネル2.6で採用される新しいモジュール・フォーマットができた経緯をさぐる。

2002年7月16日にRoman Zippelが、前回紹介した6月オタワのRusty Russellのセッションでの提案に応えるように、[RFC] new module formatと題した以下のようなパッチを投稿した。

New Module Format

以下は、「未完成な」パッチであるが、モジュールフォーマットに小さいが重要な変更を加えるものである。モジュール・フォーマットは、もはやinsmodによって生成されず、今後は代わりに、カーネルによって制御される。これは、insmodに伝える事を必要とせずに、適切にモジュール・コードをCleanupする自由を持ったことを意味する。insmodとカーネルの間の依存性は、これによって大いに縮小される。

さらに依存性を縮小するためには、自動的にセクションのstart/stop シンボルを含めるためにkbuildへのいくつかの変更を現在必要とする。しかし現在私はまだ、insmodにそれをさせている。この変更では、毎回insmodを変更せずに、自由にモジュールレイアウトを変更することができる。

私が次に考えているステップは次の通りである

1. 適切にモジュール競合を操作すること

私は、init/start/stop/exit モデルを試している。これは、誰かのモジュール再利用をやめさせることができるという優位性を持つ。または、単に残りのユーザがいなくなり、安全にモジュールをアンロードできるまで、待つこともできる。

古いスタイルのモジュールのために、全てのマジックはmodule_init()で隠されるべきである。しかし、このことは、これらのモジュールがロードされることができるが、決してアンロードされることができないことを意味する。私の考えとしては、それは安全なデフォルトである。

DEFINE_MODULE
        init: foo_init,
        start: foo_start,
        stop: foo_stop,
        exit: foo_exit,
END_MODULE

ところでこの方法では、内部組み込みモジュールのリストを簡単に生成することができて、ローダブルモジュールと組み込みモジュールの違いを減らすこともできる。

2. モジュール構造の拡張ができるということから、次のような記述を可能にする

DEFINE_FS_MODULE 
        fs_type: &foo_fs_type, 
        init: foo_init, 
        start: foo_start, 
        stop: foo_stop, 
        exit: foo_exit, 
END_FS_MODULE

これは、register_filesystem(&foo_fs_type)を自動的に呼び出す、そしてモジュールのアンロード時にも、正しく面倒をみさせるものである。

3. 最終的な目的

カーネルでシンボル情報を保存しなければならない本当に正当な理由があるだろうか。ただ一人の実ユーザはinsmodである。そして、それはカーネル・メモリを検索して、その情報を保存している。insmodはこの情報を、どこかほかにうまく格納することができる。これは、モジュールのロードについて記述できるfsにも要求される、そうでなければ少しの本当の問題は見えない。大きな優位性は、より少ないデータがカーネル・メモリに保存され、モジュールコードが小さくなる事である。

評価

これに対してRusty Russellから、彼が開発している 2.5.26用のモジュールローダ・パッチを、これに合わせて更新することや、Roman Zippelが考えている今後の方向性に賛成であり、それらの実装はなるべくユーザ空間に押し出すべきであるとのアドバイスで応えた。

New Module Interface

この後さらにRoman Zippelは、7月24日に[PATCH][RFC] new module interfaceと題したPatchを投稿した。このパッチは2.4用であり、まだ完全ではなかったが、usercountをモジュール構造の一部としないという事や、module functionがFAIL等の値を持ってEXITする機能を始め、彼の考える新しいモジュール・インタフェイスを実装したものであった。このパッチで提案された多くのアイデアのうちいくつかは、Rusty Russellやほかの開発者で検討され、その後のモジュール・インタフェイスにも導入されたが、このパッチ自体はそのまま受け入れられなかったのは、先に記述したとおりである。カーネル開発者の間で交わされた議論の詳細はわからないが、Rusty Russellが提案した実験的な実装であった「新しいモジュール・インタフェース」が、カーネル2.6への導入を目指すようになったのは、この頃だと思われる。

新しいモジュールの作り方

新しいモジュール・フォーマットのローダブル・モジュールを作成するためには、いくつかの手順が必要になった。従来のようにコンパイラだけでは作成できない。カーネル・ツリーの下にある、modpostを使って後処理とリンクモジュールの作成を行う必要がある。

参考までに、hello.koのローダブル・モジュールをコンパイル・リンクする場合のMakefileの例を以下に示す。

#
CC      = gcc
LD      = ld
RM      = rm -f
KERN_VER = `uname -r`
MARCH   = pentium4
COPTS   = -O2
KERNDIR = /usr/src/linux-${KERN_VER}
INCDIR  = ${KERNDIR}/include
INCLUDES  =  -I${INCDIR} -I${INCDIR}/asm-i386/mach-default
CFLAGS = -Wall $(COPTS) -D__KERNEL__ -DMODULE ${INCLUDES} -march=${MARCH} \
         -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing \
         -fno-common -pipe -mpreferred-stack-boundary=2 \
         -mregparm=3 -nostdinc -iwithprefix include
MODPOST = ${KERNDIR}/scripts/mod/modpost
TARGET = hello
#=========================================================
all: ${TARGET}.ko

${TARGET}.ko : ${TARGET}.mod.o
        $(LD) -m elf_i386 -r -o ${TARGET}.ko ${TARGET}.o ${TARGET}.mod.o

${TARGET}.mod.o : ${TARGET}.mod.c
        $(CC) -Wp,-MD,.${TARGET}.mod.o.d ${CFLAGS} -fomit-frame-pointer \
         -DKBUILD_BASENAME={TARGET} -DKBUILD_MODNAME=${TARGET} \
         -c -o ${TARGET}.mod.o ${TARGET}.mod.c

${TARGET}.mod.c : ${TARGET}.o
        $(MODPOST) ${TARGET}.o

.c.o:
        $(CC) -Wp,-MD,.${TARGET}.o.d ${CFLAGS} -fomit-frame-pointer \
        -DKBUILD_BASENAME=${TARGET} -DKBUILD_MODNAME=${TARGET} -c $<

clean:
        $(RM) ${TARGET}.*o ${TARGET}.mod.c ${TARGET}.mod.o .${TARGET}.* *~

以上は従来通りコンパイラのオプションを指定して、直接呼び出す方法である。

Documentation/kbuild/*.txtに解説されているように、カーネルのmakeツリーを利用する方法だと以下の様にもっと簡単になる。

#
TARGET:= hello.ko

all: ${TARGET}

hello.ko: hello.c
        make -C /usr/src/linux-`uname -r` M=`pwd` V=1 modules

clean:
        make -C /usr/src/linux-`uname -r` M=`pwd` V=1 clean

obj-m:= hello.o
clean-files := *.o *.ko *.mod.[co] *~

(2004年10月13日 最近のDistributionの状況とLinux Kernel Conference 2004のチュートリアルに合わせて修正)