システムコール
Rev.2を表示中。最新版はこちら。
カーネルへの切り替えはint 80またはsysenterによる方法とがあります。そしてその両方をサポートするためvsyscallというもので、int 80かsysenterを振り分ける仕組みを取り入れているそうです。ここではint 80の内容です。カーネルが起動すると、trap_init関数からset_system_gate(SYSCALL_VECTOR, &system_call)で、ソフト割り込みint 0x80でsystem_callがコールされるよ設定します。すなわちユーザプロセスがint 0x80を実行すると、コードセグメントディスクリプターCSが__KERNEL_CSに切り替わり。特権レベル0であるカーネルモードとして実行できることになります。特権レベルとはCSの下位2ビットにより決定され、__KERNEL_CSでは0、__USER_CSでは3に設定されています。
カーネルモードモードに切り替わると、スタックも切り替わり、ユーザプロセスからスタック経由で渡された引数は、そのままでは参照できなくなります。従ってユーザプロセスではint 80を発行する前に、ユーザスタックで渡された引数をレジスタにセットしなおして、int 80すなわち、system_callがコールされることになります。
レジスタはAXにファンクションコード、BX,CX,DX,SI,DIと続く引数がセットされます。
system_callではレジスタで渡された引数を、再度カーネルスタックにDI,SI,DX,CX,BX順にpushすることで、該当の処理関数へとコールします。従ってカーネルサイドの各処理関数はCの引数参照規約で、ユーザプロセスから渡されて引数を参照することができるようになっています。
ENTRY(system_call) RING0_INT_FRAME # can't unwind into user space anyway pushl %eax # save orig_eax CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL GET_THREAD_INFO(%ebp) # system call tracing in operation / emulation /* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */ testw $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) jnz syscall_trace_entry cmpl $(nr_syscalls), %eax jae syscall_badsys syscall_call: call *sys_call_table(,%eax,4) movl %eax,PT_EAX(%esp) #SAVE_ALLでレジスタ群をカーネルスタックに退避し、GET_THREAD_INFOでbpにthred_infoのポインタを取得し、そこからstraceされていないかチェックします。されていれば関数を呼び出す前に、syscall_trace_entryへジャンプしています。そこではsyscall_trace_entryの処理が終わるとsyscall_callにジャンプするようになっています。
sys_call_tableは、処理関数のアドレスが番号順に設定されているテーブルです。サイズは4バイトです。従ってAXを4倍したsys_call_tableからのオフセットに目的とする処理があり、そこをコールすることで、AXコード(システムコール)が呼び出されるようになっています。
p/s
sys_call_tableに新規にテーブルを追加して、該当する処理を書けば、簡単にシステムコールを追加できそうです。