4. Linux カーネルとシステムコール

システムコールと libc

Linux のカーネルを自分でコンパイルする場合には gcc という Cコンパイラを 使用します. gcc を使う場合には標準のCライブラリ (libc5.x.x, glibc2.x.x) が気になりますがカーネルはソース中にすべての必要な関数のコードが用意され ており,標準のCライブラリを使用することはありません.

したがって生成されたバイナリは,libcをスタティックリンクすることなく, また実行時に共有ライブラリを使用することも無く,独立して単独で動作する 1つのプログラムとなっています.(動的に追加されるモジュール化された デバイスドライバも,この場合は「1つのプログラム」とみなします).

一方,多くのプログラムを含む OSとしての Linux (distribution) は, C または C++ で記述されてており,各コマンドは,ほとんどすべてが libcを代表とする共有ライブラリ群を実行時に動的にリンクして動作 するようになっています. この Cライブラリ群がカーネルのシステムコールを 呼び出してファイル入出力やプロセス管理などの多くのカーネルのサービスを 利用しています.したがって,プログラムがコンパイル時に指定された共有 ライブラリが実行時に必要となります.

各種の共有ライブラリが依存する最も重要な共有ライブラリである libc は メジャーバージョンが異なればまったく動作せず,マイナーバージョンの違い でも動作しない場合があります.

結局,同じディストリビューションである RedHatの 4.x, 5.x, 6.x 相互でも バイナリの互換性は無く,Slackware,Debian,SuSE, Caldera を代表として, 常に増加しつつあるディストリビューション間のバイナリ互換を検証することは 個人では事実上ほとんど不可能です. それならば,カーネルのメジャーバージョンと CPUにのみ依存するアセンブラで作成したバイナリを利用する価値もあるでしょう.

また,libc は巨大なライブラリであり,インストールディスクや1FD Linux 等 を作成する場合の障害となっています.

もし,アセンブラでカーネルのシステムコールを直接呼び出すプログラムを 作成した場合には,そのバイナリは,libcに依存せず,ターゲットとしている CPUが同じどのディストリビューションでも実行する事ができるはずです. ただし,実行バイナリの形式である aout, ELF 間の互換性はありません.

システムコールの呼び出し

カーネルのソース (fs/read_write.c の 145 行目付近) では以下の様に 宣言されています.他のシステムコールもカーネルソース内で同じような形式 で実装されています. asmlinkage がミソです.

    asmlinkage ssize_t
    sys_write(unsigned int fd, const char * buf, size_t count)

システムコールは,MS-DOSの INT 21h システムコールと同様にソフトウェア 割り込みで行います.割り込みベクターは 0x80 となっています. システムコールの引数は,すべてレジスタで渡されますが,個数は 0 - 5個 でシステムコールによって異なリます.使用するレジスタは以下の様になります.

レジスタ 意味
eax システムコール番号
ebx システムコールの第1引数
ecx システムコールの第2引数
edx システムコールの第3引数
esi システムコールの第4引数
edi システムコールの第5引数

ただし,sys_execve() などのように struct pt_regs regs の形式で引数を渡す システムコールでは,すべてのレジスタが渡ります.

    struct pt_regs {
        long ebx;
        long ecx;
        long edx;
        long esi;
        long edi;
        long ebp;
        long eax;
        int  xds;
        int  xes;
        long orig_eax;
        long eip;
        int  xcs;
        long eflags;
        long esp;
        int  xss;
    };

これらのレジスタに必要な値を設定して int 0x80 を実行する ことで Linux のすべての機能にアクセスすることができます.単純ですね.
なぜ C で記述されたシステムコールの引数をレジスタで渡すことができるの でしょう? もう少しあとで解説する予定です.

カーネルソースのヘッダファイルの利用

アセンブラプログラミングにおいても Linux カーネルのソースに付属する C 言語用のヘッダファイルは必要となります. システムコールを利用する上で各種の定数,構造体の構成が必要となるため です.

まず,システムコールの番号をすべて記憶するわけには行かないので linux/include/asm-i386/unistd.h を利用して以下のインクルードファイル を用意します.

; file          : syscall.inc
; created       : 2000/04/11
; assembler     : nasm 0.98
; description   : system call number
; comment       : derived from linux-2.3.99pre3/include/asm-i386/unistd.h
;
;Copyright (C) 2000 Jun Mizutani 

%ifndef __SYSCALL_INC
%define __SYSCALL_INC

%assign    SYS_exit                      1
%assign    SYS_fork                      2
%assign    SYS_read                      3
%assign    SYS_write                     4
%assign    SYS_open                      5
%assign    SYS_close                     6
%assign    SYS_waitpid                   7
%assign    SYS_creat                     8
%assign    SYS_link                      9
%assign    SYS_unlink                   10
%assign    SYS_execve                   11
%assign    SYS_chdir                    12
%assign    SYS_time                     13
%assign    SYS_mknod                    14
%assign    SYS_chmod                    15
%assign    SYS_lchown                   16
%assign    SYS_break                    17
%assign    SYS_oldstat                  18
%assign    SYS_lseek                    19
%assign    SYS_getpid                   20
%assign    SYS_mount                    21
%assign    SYS_umount                   22
%assign    SYS_setuid                   23
%assign    SYS_getuid                   24
%assign    SYS_stime                    25
%assign    SYS_ptrace                   26
%assign    SYS_alarm                    27
%assign    SYS_oldfstat                 28
%assign    SYS_pause                    29
%assign    SYS_utime                    30
%assign    SYS_stty                     31
%assign    SYS_gtty                     32
%assign    SYS_access                   33
%assign    SYS_nice                     34
%assign    SYS_ftime                    35
%assign    SYS_sync                     36
%assign    SYS_kill                     37
%assign    SYS_rename                   38
%assign    SYS_mkdir                    39
%assign    SYS_rmdir                    40
%assign    SYS_dup                      41
%assign    SYS_pipe                     42
%assign    SYS_times                    43
%assign    SYS_prof                     44
%assign    SYS_brk                      45
%assign    SYS_setgid                   46
%assign    SYS_getgid                   47
%assign    SYS_signal                   48
%assign    SYS_geteuid                  49
%assign    SYS_getegid                  50
%assign    SYS_acct                     51
%assign    SYS_umount2                  52
%assign    SYS_lock                     53
%assign    SYS_ioctl                    54
%assign    SYS_fcntl                    55
%assign    SYS_mpx                      56
%assign    SYS_setpgid                  57
%assign    SYS_ulimit                   58
%assign    SYS_oldolduname              59
%assign    SYS_umask                    60
%assign    SYS_chroot                   61
%assign    SYS_ustat                    62
%assign    SYS_dup2                     63
%assign    SYS_getppid                  64
%assign    SYS_getpgrp                  65
%assign    SYS_setsid                   66
%assign    SYS_sigaction                67
%assign    SYS_sgetmask                 68
%assign    SYS_ssetmask                 69
%assign    SYS_setreuid                 70
%assign    SYS_setregid                 71
%assign    SYS_sigsuspend               72
%assign    SYS_sigpending               73
%assign    SYS_sethostname              74
%assign    SYS_setrlimit                75
%assign    SYS_getrlimit                76
%assign    SYS_getrusage                77
%assign    SYS_gettimeofday             78
%assign    SYS_settimeofday             79
%assign    SYS_getgroups                80
%assign    SYS_setgroups                81
%assign    SYS_select                   82
%assign    SYS_symlink                  83
%assign    SYS_oldlstat                 84
%assign    SYS_readlink                 85
%assign    SYS_uselib                   86
%assign    SYS_swapon                   87
%assign    SYS_reboot                   88
%assign    SYS_readdir                  89
%assign    SYS_mmap                     90
%assign    SYS_munmap                   91
%assign    SYS_truncate                 92
%assign    SYS_ftruncate                93
%assign    SYS_fchmod                   94
%assign    SYS_fchown                   95
%assign    SYS_getpriority              96
%assign    SYS_setpriority              97
%assign    SYS_profil                   98
%assign    SYS_statfs                   99
%assign    SYS_fstatfs                 100
%assign    SYS_ioperm                  101
%assign    SYS_socketcall              102
%assign    SYS_syslog                  103
%assign    SYS_setitimer               104
%assign    SYS_getitimer               105
%assign    SYS_stat                    106
%assign    SYS_lstat                   107
%assign    SYS_fstat                   108
%assign    SYS_olduname                109
%assign    SYS_iopl                    110
%assign    SYS_vhangup                 111
%assign    SYS_idle                    112
%assign    SYS_vm86old                 113
%assign    SYS_wait4                   114
%assign    SYS_swapoff                 115
%assign    SYS_sysinfo                 116
%assign    SYS_ipc                     117
%assign    SYS_fsync                   118
%assign    SYS_sigreturn               119
%assign    SYS_clone                   120
%assign    SYS_setdomainname           121
%assign    SYS_uname                   122
%assign    SYS_modify_ldt              123
%assign    SYS_adjtimex                124
%assign    SYS_mprotect                125
%assign    SYS_sigprocmask             126
%assign    SYS_create_module           127
%assign    SYS_init_module             128
%assign    SYS_delete_module           129
%assign    SYS_get_kernel_syms         130
%assign    SYS_quotactl                131
%assign    SYS_getpgid                 132
%assign    SYS_fchdir                  133
%assign    SYS_bdflush                 134
%assign    SYS_sysfs                   135
%assign    SYS_personality             136
%assign    SYS_afs_syscall             137
%assign    SYS_setfsuid                138
%assign    SYS_setfsgid                139
%assign    SYS__llseek                 140
%assign    SYS_getdents                141
%assign    SYS__newselect              142
%assign    SYS_flock                   143
%assign    SYS_msync                   144
%assign    SYS_readv                   145
%assign    SYS_writev                  146
%assign    SYS_getsid                  147
%assign    SYS_fdatasync               148
%assign    SYS__sysctl                 149
%assign    SYS_mlock                   150
%assign    SYS_munlock                 151
%assign    SYS_mlockall                152
%assign    SYS_munlockall              153
%assign    SYS_sched_setparam          154
%assign    SYS_sched_getparam          155
%assign    SYS_sched_setscheduler      156
%assign    SYS_sched_getscheduler      157
%assign    SYS_sched_yield             158
%assign    SYS_sched_get_priority_max  159
%assign    SYS_sched_get_priority_min  160
%assign    SYS_sched_rr_get_interval   161
%assign    SYS_nanosleep               162
%assign    SYS_mremap                  163
%assign    SYS_setresuid               164
%assign    SYS_getresuid               165
%assign    SYS_vm86                    166
%assign    SYS_query_module            167
%assign    SYS_poll                    168
%assign    SYS_nfsservctl              169
%assign    SYS_setresgid               170
%assign    SYS_getresgid               171
%assign    SYS_prctl                   172
%assign    SYS_rt_sigreturn            173
%assign    SYS_rt_sigaction            174
%assign    SYS_rt_sigprocmask          175
%assign    SYS_rt_sigpending           176
%assign    SYS_rt_sigtimedwait         177
%assign    SYS_rt_sigqueueinfo         178
%assign    SYS_rt_sigsuspend           179
%assign    SYS_pread                   180
%assign    SYS_pwrite                  181
%assign    SYS_chown                   182
%assign    SYS_getcwd                  183
%assign    SYS_capget                  184
%assign    SYS_capset                  185
%assign    SYS_sigaltstack             186
%assign    SYS_sendfile                187
%assign    SYS_getpmsg                 188
%assign    SYS_putpmsg                 189
%assign    SYS_vfork                   190
; >2.4
%assign    SYS_ugetrlimit              191
%assign    SYS_mmap2                   192
%assign    SYS_truncate64              193
%assign    SYS_ftruncate64             194
%assign    SYS_stat64                  195
%assign    SYS_lstat64                 196
%assign    SYS_fstat64                 197
%assign    SYS_lchown32                198
%assign    SYS_getuid32                199
%assign    SYS_getgid32                200
%assign    SYS_geteuid32               201
%assign    SYS_getegid32               202
%assign    SYS_setreuid32              203
%assign    SYS_setregid32              204
%assign    SYS_getgroups32             205
%assign    SYS_setgroups32             206
%assign    SYS_fchown32                207
%assign    SYS_setresuid32             208
%assign    SYS_getresuid32             209
%assign    SYS_setresgid32             210
%assign    SYS_getresgid32             211
%assign    SYS_chown32                 212
%assign    SYS_setuid32                213
%assign    SYS_setgid32                214
%assign    SYS_setfsuid32              215
%assign    SYS_setfsgid32              216
%assign    SYS_pivot_root              217
%assign    SYS_mincore                 218
%assign    SYS_madvise                 219

%endif

各システムコールの使い方は後で順次解説する予定です.

このファイルはシステムコールを使うコードの先頭付近で %include "syscall.inc" という行を挿入することで,例えば SYS_read と書けば read システム コール番号の 3 を指定したことになります. 実際の使い方は今後の例を参照して下さい.

ここですべてのシステムコールを使うことはありません (と断言しておく). 子プロセスの起動程度はともかくネットワークまでは気力が続かないと思います. (万一,気力を奮い立たせる応援メールがあればわかりません.)

しかし書籍があります.システムコール全体の解説は Linux カーネルインターナル (アスキー, ISBN4-7561-3135-2, 4800円) の Appendix A にカーネルバージョン 2.0に関するものですが,詳細に解説されています.参考にして下さい.

syscall.inc はカーネルのバージョン 2.0, 2.2, 2.4 系列で使用できるはずです. 使用できるシステムコールが 2.0 とそれ以降で異なっています. 付録 A. システムコールとカーネルの関数 を参考にカーネルソースを調べてください.そして私に教えてください :-p