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