システムコールは、カーネル内の機能を普通のユーザ空間のプロセスから使うための仕組みです。
レジスタに決まった値をセットして、int 0x80 という命令を実行すると、割り込みが発生します。
割り込みが発生すると、カーネルは普通のプロセスの処理を一時中断して、
カーネル内の割り込み処理を行います。
システムコールが呼ばれる仕組みがトリッキーなのは、普通の C 言語の関数呼び出しとは手続きが異なることです。
普通の関数呼び出しだと、ユーザーモードからカーネルモードに移ったりは出来ません。
そうではなくて発生させるのは割り込みです。(イメージとしてはシグナルみたいなのに近いです)
そうすると、ユーザー空間の関数はそこでストップして、カーネルの例外ハンドラに飛びます。
カーネルの例外ハンドラは、元々のユーザ空間のプログラムカウンタの値をちゃんと覚えておいて、
システムコールの処理が終わったあと、何事もなかったようにユーザー空間に戻れるようになっています。
システムコールを呼ぶときには、レジスタにシステムコール番号と引数をセットして int 0x80 命令を実行します。
(最近の x86 だと、sysenter で入ることもあります。こっちの方が呼び出しコストが小さいようです)
x86 には %eax, %ebx, ... というレジスタがありますが、この %eax にシステムコール番号を、
%ebx から引数を入れます。(引数が入りきらない場合は、メモリに積むこともできます。これはシステムコールの実装次第です)
システムコールの引数は様々ですが、返り値は成功時0以上, 失敗は -1 と決まっています。
失敗したときには、エラーコードがグローバル変数 errno に格納されています。
システムコール番号の一覧は、
arch/i386/kernel/syscall_table.S にあります。(これはカーネル内の実装で、番号から対応する関数を探す方です。
だから、全てsys_ がついています)
read() とか exit とか、おなじみの関数名が並んでいますが、これらは全て番号に変換されて呼ばれているのです。
ENTRY(sys_call_table)
.long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open /* 5 */
.long sys_close
.long sys_waitpid
.long sys_creat
.long sys_link
.long sys_unlink /* 10 */
.long sys_execve
.long sys_chdir
...
一般にユーザが直接システムコールをたたくことはあまりありません。
そもそも、C 言語の機能だけで直接システムコールを呼ぶことはできません。
なので、大抵は libc/glibc (GNU C Library) の関数経由で呼び出します。
libc の関数名はシステムコールの名前とよく似ていますが、1:1に対応するわけではありません。
例えば、printf というシステムコールはありません。実際には標準出力な write しています。
また、send や recv といった、socket API も上の表に直接は出てきません。
これらは、sys_socketcall という一つのシステムコールにまとめられていて、カーネル空間な入った後に
sys_send や sys_recv に"振り分け"されます。
みなさんは普通にシステムコールを呼んだことがあると思いますが、単に"read" などの関数を実行するのは
カーネルを読む…といいつつ、システムコールを呼ぶのはユーザ空間のコードです。
しかも、このコードは直接レジスタを操作するので、
だから、ここで見るのは libc のソースです。
[an error occurred while processing this directive]