「Linuxカーネルの設計と実装」メモを読む-システムコール

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の適切な場所に配置します。

 

おすすめ

転載: blog.csdn.net/jiangwei0512/article/details/106143215