open system callを処理する部分を見つけよ。Linuxのシステムコールは、
- アプリケーションが呼び出すライブラリ
- ライブラリが呼び出すtrap
- trapを受け取り振り分ける部分
- 実際にkernel内部で処理する部分
がある。それぞれ、どこにあるのか調べよ。
作成したアプリケーションがライブラリと結合されるのは
リンカ(ld)が実行され、実行ファイルが生成されるときである。
ライブラリ自体はldがライブラリ参照に使うファイル(/usr/lib/libc.so)から
/lib/libc.so.6 と /usr/lib/libc_nonshared.a であることがわかる。
コメントで書かれているように、
ほとんどのプログラムで使用されるshared libraryは前者のlibc.so.6、
いくつかの機能で使用されるstatic libraryは後者のlibc_nonshared.aである。
/usr/lib/libc_nonshared.a
/* GNU ld script
Use the shared librarys but some functions are only in
the staticc library. so try that secondarily. */
OUTPUT_FORMAT(elf32-i386)
GROUP(/lib/libc.so.6 /usr/lib/libc_nonshared.a )
ライブラリに含まれる関数の中には、それ自体がさらにシステムコールを呼び出しているものがある。
これには、次のような利点がある。
- より使いやすい形で関数を利用できる。
システムコールは非常に一般化された形で書いてあるので、
様々な要求に対応できるかわりに利用が難しい。
ライブラリ関数ではそれを使いやすいものにするために、
用途を限定したいくつかの関数を作ってその中からシステムコールを呼び出している。
- 移植性が高まる
ライブラリは複数のアーキテクチャやカーネルなどでも同一の関数を提供するので、
システムコールの実装が違う別カーネルや、
アーキテクチャが全く違う別ハード上でもソースを変更することなく、
または最小限のソース変更で移植できる。
ライブラリが呼び出すtrap
ライブラリ内でシステムコールを呼び出すには、
レジスタに必要な引数をストアしてアセンブラのsc命令を実行する。
(powerpcの場合。i386はint 0x80命令)
sc命令は、呼び出されたプログラムの直後のアドレスを格納し、
0xc00番地(powerpcの場合。i386は0x80番地)に格納されたシステムコールの割り込みベクタに処理を移す。
trap_init
linux/arch/(アーキテクチャ名)/kernel/trap.c に記述されている、
trapの初期化(割り込みベクタのレジスタ配置)を行う部分。
ppcでは、割り込みベクタ自体が0xc00番地に格納されるよう、
head.Sに直に記述してある(ppcはそのような記述が可能)ので、
trap_initは空のダミー関数になっている。
trapを受け取り振り分ける部分
0xc00番地の割り込みベクタでは、レジスタをカーネルスタックに退避し、
transfer_to_handlerを呼ぶ。
transfet_to_handlerからDoSyscallに処理が移る。DoSyscallはシステムコール
番号からテーブルを参照し、実際のシステムコールを呼び出す。
テーブルはソースファイル中では
linux/arch/(アーキテクチャ名)/kernel/misc.S
の中にある・・・と様々な場所に書いてあったが、
どうやらテーブルのみ別に分離されたらしく、
systbl.Sというファイル(powerpcの場合。i386ではsyscall_table.S)
に別にとられていた。
それぞれのカーネルのソースは、以下の位置にある。
0xc00割り込みベクタ
powerpcの場合
linux/arch/powerpc/kernel/head_32.S
. . .
/* System call */
. = 0xc00
SystemCall:
EXCEPTION_PROLOG
EXC_XFER_EE_LITE(0xc00, DoSyscall)
/* Single step - not used on 601 */
EXCEPTION(0xd00, SingleStep, single_step_exception, EXC_XFER_STD)
EXCEPTION(0xe00, Trap_0e, unknown_exception, EXC_XFER_EE)
. . .
他アーキテクチャでは基本的に
linux/arch/(アーキテクチャ名)/kernel/head.S
というファイルに記述されている。
DoSyscall
linux/arch/powrpc/kernel/entry_32.S
他アーキテクチャでは基本的に
linux/arch/(アーキテクチャ名)/kernel/entry.S
というファイルに記述されている。
実際にkernel内部で処理する部分
それぞれの関数の実体は、基本的にC言語で書かれており、
linux/fs/ 以下や linux/kernel 以下などkernelソース内に散在している。
考察
システムコールを呼び出すまでに必要なソース群は数回ジャンプしており、
結局いくつかのソースファイルを見て回ることになった。
機械語レベルに関わる箇所が多いためにアセンブラを使用している箇所が多かったが、
極端に複雑な記述はなかったように思える。
今回調査しきれなかったのは途中で出てきたtransfer_to_handler。
DoSyscallを呼び出すことまではわかったが、これ自身が何をしているものなのかは
突き止めることができなかった。powerpcに関するソース中でしか出てこないので、
アーキテクチャ固有の、必要なモノだとは思われる。
linux/-2.6.16.18/Documentation/powepc/cpu_features.txt
内には、それらしき記述があるが、カーネルソースにはそれらしき記述が見つからなかった。
...
After detecting the processor type, the kernel patches out sections of code
that shouldn't be used by writing nop's over it. Using cpufeatures requires
just 2 macros (found in include/asm-ppc/cputable.h), as seen in head.S
transfer_to_handler:
#ifdef CONFIG_ALTIVEC
BEGIN_FTR_SECTION
mfspr r22,SPRN_VRSAVE /* if G4, save vrsave register value */
stw r22,THREAD_VRSAVE(r23)
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
#endif /* CONFIG_ALTIVEC */
If CPU 0 supports Altivec, the code is left untouched. If it doesn't, both
instructions are replaced with nop's.
...
cpu_features.txtの記述によれば、PowerPC G4 に搭載された
ベクトル演算ユニットAltiVecを使用する場合にのみ有効になるマクロ
(それ以外の場合は含まれる全命令がnopになる)らしいが、
今回との関わりが見つけられなかった。