linuxシステム呼び出しの原理と実現
4553 ワード
linuxシステム呼び出し
システムコールはlinuxカーネルがユーザーステータスプログラムに提供する主な機能インタフェースです.システム呼び出しにより、ユーザー・ステータス・プロセスを一時的にカーネル・ステータスに切り替えることができ、カーネル・ステータスがアクセスできるハードウェアとリソースを使用して特定の機能を完了します.システム呼び出しはlinuxカーネルとカーネルモジュールによって実現され、カーネルはシステム呼び出しを処理する際にシステム呼び出し要求とパラメータが正しいかどうかを確認し、特権リソースとハードウェアへのアクセスの正確性を保証します.このようにして、linuxはカーネルとハードウェアリソースアクセスインタフェースを提供すると同時に、カーネルとハードウェアリソースの使用の正確性と安全性を保証する.
本文は主にlinuxの下でシステム呼び出しの原理と実現に対して分析を行う.本明細書の分析はx 86アーキテクチャに基づいており、linuxカーネルコードのバージョンは4.17.6である.
ユーザ状態呼び出しインタフェース
ユーザー・ステータス・プロセスは、主に次の方法でシステム・コールを直接使用します.
Syscallインタフェースはglibcによって提供され、実装され、最初のパラメータnumberは呼び出す必要があるシステム呼び出し番号を表し、後続の可変パラメータはシステム呼び出しタイプに基づいて決定される.カーネルでサポートされているシステムコール番号は、に表示されます.関数呼び出しに失敗すると-1が返され、エラーの原因はerrnoに保存されます.errnoの意味は参照できます.
なお、ここでの戻り値とerrnoはglibcパッケージで提供され、カーネルのシステム呼び出し応答関数自体はerrnoを提供せず、戻り値も異なる.
実装の原理
1回のシステム呼び出しの完全な実行手順は、次のとおりです.特定の命令によりシステム呼び出し(int$80、sysenter、syscall) を発行する. CPUは、ユーザ状態からカーネル状態に切り替える、レジスタや環境設定 を行う. system_を呼び出すcallカーネル関数は、システム呼び出し番号によって対応するサービスルーチン を取得する.システム呼び出し処理ルーチン を呼び出す.特定の命令を使用してシステム呼び出しからユーザ状態(iret、sysexit、sysret) に戻る.
システムコールめいれい
カーネルにシステム呼び出しを開始するには、特定のコマンドを使用する必要があります.Linuxでは、従来の方法では、アセンブリ命令intを使用して割り込みを開始し、0 x 80(128)番割り込みを使用してCPUをカーネル状態にし、その後、対応する割り込み応答関数system_を呼び出す.callは、システム呼び出しルーチンを実行します.
割り込み方式でシステム呼び出しを開始する性能が悪いため、新しいCPUとカーネルはsysenterとsyscallの2つの専用命令を使用してシステム呼び出しを開始することをサポートしています.ここでsysenterは32ビットシステムで使用され、対応する終了命令はsysexitである.Syscallは64ビットシステムで使用され、対応する終了命令はsysretである.
sysenterを例にとると、このコマンドを使用する場合、まず__を呼び出すkernel_vsyscall()関数はユーザ状態スタックを保存します.その後sysenter命令を実行してカーネル状態に切り替える.最後にsysenter_の実行を開始entry()関数はカーネル状態スタックを設定し、システム呼び出し番号に基づいて処理ルーチンを呼び出し、その後の論理とsystem_callは似ています.
カーネル実装ロジック
カーネル呼び出しシステム呼び出し処理ルーチンのコアデータ構造はsys_call_table、このデータ構造は以下のように定義されています.
sys_call_tableは、すべてのシステム呼び出し処理関数のポインタを保存する関数ポインタ配列です.system_callなどの関数はシステム呼び出し番号を下付きとしsys_call_tableで対応するシステム呼び出し関数を検索して実行します.
sys_call_tableの初期化では、最初のステップは、すべてのポインタ配列要素をsys_に割り当てることです.ni_syscall.これは,一部のシステム呼び出し番号が使用されず,対応する処理関数が定義されていないことを避けるためである.sys_ni_Syscallはで定義され、直接-ENOSYSに戻り、システム呼び出しが存在しないことを示します.
sys_call_tableの具体的な内容は中で提供して、内容は:_に類似しますSYSCALL_64(19, sys_readv, sys_readv).前から対_SYSCALL_64マクロの2つの定義が表示されますsyscall_64.c先将_SYSCALL_64マクロ展開関数宣言extern asmlinkage long sym(const struct pt_regs*)に展開し、配列要素初期化文[nr]=symに展開します.
システム呼び出し番号マクロ定義を提供するヘッダファイルなどのファイルは、カーネルソースツリーには存在せず、カーネルコンパイルのプリコンパイルフェーズで自動的に生成されることに注意してください.カーネルソースでシステム呼び出し番号と処理関数を本当に定義するファイルは、次のとおりです.
カーネルプリコンパイルシステムは、このファイルに指定されたシステム呼び出し番号、システム呼び出し名、および対応する処理関数名に基づいて、対応するヘッダファイルを生成する.
新しいシステムコールの追加
上記の解析によれば、新しいシステム呼び出し番号と処理関数を追加する必要がある場合は、次の変更が必要です. syscall_64.tblに新しいシステム呼び出し番号、名前、および処理関数名を追加します.例えば「666 common mycal__x64_sys_mycal」 sys_を提供mycal関数実装.関数はasmlinkage long sysとして定義する必要があります.mycall(...) sys_mycal関数は独立して実現される.cファイルにはlib/パスの下のmakefileに追加するobj-yに追加する必要がある.cファイルパス を使用して、独自のシステム呼び出し機能を提供します.
注意しなければならないのはsys_call_tableデータ構造はソースコードでconst変数であるため、システム呼び出し関数ポインタの初期化が完了すると変更できません.実行中にシステム呼び出し処理関数を動的に変更または追加する必要がある場合(たとえば、カーネルモジュールをロードして処理関数を提供する)、const制限を削除し、実行中に呼び出し処理関数を切り替えることができます.
システムコールはlinuxカーネルがユーザーステータスプログラムに提供する主な機能インタフェースです.システム呼び出しにより、ユーザー・ステータス・プロセスを一時的にカーネル・ステータスに切り替えることができ、カーネル・ステータスがアクセスできるハードウェアとリソースを使用して特定の機能を完了します.システム呼び出しはlinuxカーネルとカーネルモジュールによって実現され、カーネルはシステム呼び出しを処理する際にシステム呼び出し要求とパラメータが正しいかどうかを確認し、特権リソースとハードウェアへのアクセスの正確性を保証します.このようにして、linuxはカーネルとハードウェアリソースアクセスインタフェースを提供すると同時に、カーネルとハードウェアリソースの使用の正確性と安全性を保証する.
本文は主にlinuxの下でシステム呼び出しの原理と実現に対して分析を行う.本明細書の分析はx 86アーキテクチャに基づいており、linuxカーネルコードのバージョンは4.17.6である.
ユーザ状態呼び出しインタフェース
ユーザー・ステータス・プロセスは、主に次の方法でシステム・コールを直接使用します.
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include
#include /* For SYS_xxx definitions */
int syscall(int number, ...);
Syscallインタフェースはglibcによって提供され、実装され、最初のパラメータnumberは呼び出す必要があるシステム呼び出し番号を表し、後続の可変パラメータはシステム呼び出しタイプに基づいて決定される.カーネルでサポートされているシステムコール番号は、に表示されます.関数呼び出しに失敗すると-1が返され、エラーの原因はerrnoに保存されます.errnoの意味は参照できます.
なお、ここでの戻り値とerrnoはglibcパッケージで提供され、カーネルのシステム呼び出し応答関数自体はerrnoを提供せず、戻り値も異なる.
実装の原理
1回のシステム呼び出しの完全な実行手順は、次のとおりです.
システムコールめいれい
カーネルにシステム呼び出しを開始するには、特定のコマンドを使用する必要があります.Linuxでは、従来の方法では、アセンブリ命令intを使用して割り込みを開始し、0 x 80(128)番割り込みを使用してCPUをカーネル状態にし、その後、対応する割り込み応答関数system_を呼び出す.callは、システム呼び出しルーチンを実行します.
割り込み方式でシステム呼び出しを開始する性能が悪いため、新しいCPUとカーネルはsysenterとsyscallの2つの専用命令を使用してシステム呼び出しを開始することをサポートしています.ここでsysenterは32ビットシステムで使用され、対応する終了命令はsysexitである.Syscallは64ビットシステムで使用され、対応する終了命令はsysretである.
sysenterを例にとると、このコマンドを使用する場合、まず__を呼び出すkernel_vsyscall()関数はユーザ状態スタックを保存します.その後sysenter命令を実行してカーネル状態に切り替える.最後にsysenter_の実行を開始entry()関数はカーネル状態スタックを設定し、システム呼び出し番号に基づいて処理ルーチンを呼び出し、その後の論理とsystem_callは似ています.
カーネル実装ロジック
カーネル呼び出しシステム呼び出し処理ルーチンのコアデータ構造はsys_call_table、このデータ構造は以下のように定義されています.
/* this is a lie, but it does not hurt as sys_ni_syscall just returns -EINVAL */
extern asmlinkage long sys_ni_syscall(const struct pt_regs *);
#define __SYSCALL_64(nr, sym, qual) extern asmlinkage long sym(const struct pt_regs *);
#include
#undef __SYSCALL_64
#define __SYSCALL_64(nr, sym, qual) [nr] = sym,
asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
/*
* Smells like a compiler bug -- it doesn't work
* when the & below is removed.
*/
[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include
};
sys_call_tableは、すべてのシステム呼び出し処理関数のポインタを保存する関数ポインタ配列です.system_callなどの関数はシステム呼び出し番号を下付きとしsys_call_tableで対応するシステム呼び出し関数を検索して実行します.
sys_call_tableの初期化では、最初のステップは、すべてのポインタ配列要素をsys_に割り当てることです.ni_syscall.これは,一部のシステム呼び出し番号が使用されず,対応する処理関数が定義されていないことを避けるためである.sys_ni_Syscallはで定義され、直接-ENOSYSに戻り、システム呼び出しが存在しないことを示します.
sys_call_tableの具体的な内容は中で提供して、内容は:_に類似しますSYSCALL_64(19, sys_readv, sys_readv).前から対_SYSCALL_64マクロの2つの定義が表示されますsyscall_64.c先将_SYSCALL_64マクロ展開関数宣言extern asmlinkage long sym(const struct pt_regs*)に展開し、配列要素初期化文[nr]=symに展開します.
システム呼び出し番号マクロ定義を提供するヘッダファイルなどのファイルは、カーネルソースツリーには存在せず、カーネルコンパイルのプリコンパイルフェーズで自動的に生成されることに注意してください.カーネルソースでシステム呼び出し番号と処理関数を本当に定義するファイルは、次のとおりです.
#
# 64-bit system call numbers and entry vectors
#
# The format is:
#
#
# The __x64_sys_*() stubs are created on-the-fly for sys_*() system calls
#
# The abi is "common", "64" or "x32" for this file.
#
0 common read __x64_sys_read
1 common write __x64_sys_write
2 common open __x64_sys_open
3 common close __x64_sys_close
4 common stat __x64_sys_newstat
5 common fstat __x64_sys_newfstat
6 common lstat __x64_sys_newlstat
7 common poll __x64_sys_poll
8 common lseek __x64_sys_lseek
9 common mmap __x64_sys_mmap
カーネルプリコンパイルシステムは、このファイルに指定されたシステム呼び出し番号、システム呼び出し名、および対応する処理関数名に基づいて、対応するヘッダファイルを生成する.
新しいシステムコールの追加
上記の解析によれば、新しいシステム呼び出し番号と処理関数を追加する必要がある場合は、次の変更が必要です.
注意しなければならないのはsys_call_tableデータ構造はソースコードでconst変数であるため、システム呼び出し関数ポインタの初期化が完了すると変更できません.実行中にシステム呼び出し処理関数を動的に変更または追加する必要がある場合(たとえば、カーネルモジュールをロードして処理関数を提供する)、const制限を削除し、実行中に呼び出し処理関数を切り替えることができます.