Cライブラリ、API、およびシステム呼び出し
通常の状況では、アプリケーションは、システム呼び出しを介して直接ではなく、ユーザースペースに実装されたアプリケーションプログラミングインターフェイス(API)を介してプログラムされます。
LInuxシステム呼び出しは、ほとんどのUnixシステムと同様に、Cライブラリの一部として提供されます。
Cライブラリは、標準のCライブラリ関数やシステム呼び出しインターフェイスなど、Unixシステムの主要なAPIを実装します。
Unixで最も人気のあるAPIは、POSIX標準に基づいています。
例と手順
getpid()システム呼び出し:
SYSCALL_DEFINE0(getpid)
{
return task_tgid_vnr(current);
}
SYSCALL_DEFINE0は、パラメーターなしでシステム呼び出しを定義するマクロです。これは、一連のマクロの1つです。
#define SYSCALL_DEFINE0(name) asmlinkage long sys_##name(void)
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
展開された定義は実際には次のようになります。
asmlinkage long sys_getpid(void)
ここに:
asmlinkageは、スタックから関数のパラメーターのみを抽出するようにコンパイラーに通知するために使用されるコンパイル命令です。
Longは互換性を確保するためのもので、ユーザースペースではintを返し、カーネルスペースではlongを返します。
sys_ xxは、Linuxのすべてのシステム呼び出しが従う必要のある命名規則です。
システムコールリスト
Linuxには、登録されているすべてのシステムコールを記録するシステムコールテーブルがあり、各システムコールにシステムコール番号を割り当てて、システムコールを関連付けます。
比如(arch \ x86 \ kernel \ syscall_table_32.S):
ENTRY(sys_call_table)
.long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */
.long sys_exit
.long ptregs_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 ptregs_execve
.long sys_chdir
.long sys_time
// 后面略
一部のシステム呼び出しが実装されていない場合、対応する位置(システム呼び出し番号である0から始まる)は上書きされません。代わりに、sys_ni_syscall()を使用して、実装されていないシステム呼び出しを示します。-ENOSYSのみが返されます。
/*
* Non-implemented system calls get redirected here.
*/
asmlinkage long sys_ni_syscall(void)
{
return -ENOSYS;
}
システム呼び出しをトリガーする
ソフト割り込み:例外を発生させることにより、カーネルに切り替えて例外ハンドラーを実行するようにシステムに要求します。この例外ハンドラーは、システム呼び出しハンドラー(割り込み番号128、system_call()に対応)です。以下は、x86_64プラットフォームの実装コードの一部です。
arch\x86\kernel\entry_64.S:
ENTRY(system_call)
CFI_STARTPROC simple
CFI_SIGNAL_FRAME
CFI_DEF_CFA rsp,KERNEL_STACK_OFFSET
CFI_REGISTER rip,rcx
/*CFI_REGISTER rflags,r11*/
SWAPGS_UNSAFE_STACK
/*
* A hypervisor implementation might want to use a label
* after the swapgs, so that it can do the swapgs
* for the guest and jump here on syscall.
*/
ENTRY(system_call_after_swapgs)
movq %rsp,PER_CPU_VAR(old_rsp)
movq PER_CPU_VAR(kernel_stack),%rsp
/*
* No need to follow this irqs off/on section - it's straight
* and short:
*/
ENABLE_INTERRUPTS(CLBR_NONE)
SAVE_ARGS 8,1
movq %rax,ORIG_RAX-ARGOFFSET(%rsp)
movq %rcx,RIP-ARGOFFSET(%rsp)
CFI_REL_OFFSET rip,RIP-ARGOFFSET
GET_THREAD_INFO(%rcx)
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%rcx)
jnz tracesys
system_call_fastpath:
cmpq $__NR_syscall_max,%rax
ja badsys
movq %r10,%rcx
call *sys_call_table(,%rax,8) # XXX: rip relative
movq %rax,RAX-ARGOFFSET(%rsp)
別のトリガーシステム呼び出しは、命令sysenterです。
システム呼び出し番号もカーネルに渡す必要があります。カーネルはx86のeaxレジスタを介して渡されます。
システム呼び出し番号に加えて、追加の外部パラメーターが必要になる場合があります。
外部パラメータに関しては、プラットフォームごとに異なる必要があり、本のステートメントもコードとは異なるため、特別な注意を払う必要はありません。
システム呼び出しコンテキスト
システム呼び出しを実行するとき、カーネルはプロセスコンテキストにあります。
現在のポインタは、現在のタスク、つまりシステム呼び出しを引き起こしたプロセスを指します。
カーネルは、システム呼び出しの実行時にもスリープする可能性があるため(対応して、割り込みハンドラーはスリープできません)、システム呼び出しが再入力されるようにする必要があります。
新しいシステム呼び出しの実装は避けてください
単純な情報のみを交換する場合は、別の方法を使用できます。デバイスノードを実装し、これに対してread()とwrite()を実装します。ioctl()を使用して、特定の設定を操作したり、特定の情報を取得したりします。
セマフォなどのインターフェイスは、ファイル記述子で表すことができます。
追加した情報をファイルとしてsysfsの適切な場所に配置します。