ホームページ  >  記事  >  システムチュートリアル  >  Linux のシステム コールはカーネルへの正当なエントリではありません

Linux のシステム コールはカーネルへの正当なエントリではありません

王林
王林転載
2024-03-19 10:34:02883ブラウズ

Linux では、システム コールはユーザー空間がカーネルにアクセスする唯一の手段であり、カーネルへの唯一の合法的な入り口です。実際、デバイス ファイルや /proc などの他のメソッドは、最終的にはシステム コールを通じて実行されます。

通常、アプリケーションはシステム コールを直接使用するのではなく、アプリケーション プログラミング ソケット (API) を介してプログラムされます。これらのプログラミング ソケットは、実際にはカーネルによって提供されるシステム コールに対応する必要はありません。 API は、アプリケーションによって使用されるプログラミング ソケットのセットを定義します。これらは 1 つのシステムコールで実装することも、複数のシステムコールを呼び出して実装することもできますが、システムコールを使用しなくても問題ありません。実際、API はさまざまな異なるオペレーティング システムに実装でき、アプリケーションにまったく同じソケットを提供しますが、そのようなシステムでの実装は大きく異なる場合があります。

Unix の世界では、最も一般的なアプリケーション プログラミング ソケットは POSIX 標準に基づいており、Linux は POSIX 互換です。

プログラマの観点から見ると、プログラマは API を処理するだけでよく、カーネルはシステム コールを処理するだけであり、ライブラリ関数やアプリケーションがシステム コールをどのように使用するかはカーネルの関心事ではありません。

システム コール (Linux ではシステム コールと呼ばれることが多い) は、通常、関数を通じて呼び出されます。通常、1 つまたは複数のパラメータ (入力) の定義が必要であり、何らかの副作用が発生する可能性があります。この副作用は、成功 (0 値) またはエラー (負の値) を示す長い戻り値で表されます。システムコールでエラーが発生すると、エラーコードが errno グローバル変数に書き込まれます。 perror() 関数を呼び出すと、この変数をユーザーが理解できるエラー文字列に変換できます。

システム コールの実装には 2 つの特徴があります。 1) 関数宣言に asmlinkage 修飾子があり、スタックから関数のパラメータのみを抽出するようにコンパイラに通知するために使用されます。 2) システムコール getXXX() はカーネル内で sys_getXXX() として定義されます。これは、Linux のすべてのシステム コールが従うべき命名規則です。

システム コール番号: Linux では、各システム コールにシステム コール番号が割り当てられ、システム コールをこの一意の番号に関連付けることができます。ユーザー空間プロセスがシステム コールを実行するとき、システム コール番号はどのシステム コールが実行されるかを示すために使用され、プロセスはシステム コールの名前については言及しません。システム コール番号は、一度割り当てられると変更できません (変更しないと、コンパイルされたアプリケーションがクラッシュします)。システム コールが削除された場合、そのシステム コール番号は再利用できません。 Linux には、「未使用」のシステム コール sys_ni_syscall() があり、-ENOSYS を返すだけでなく、他の作業は行われません。このエラー番号は、無効なシステム コール用に特別に設計されています。まれなことのようですが、システム コールが削除された場合、この関数は「ギャップを埋める」役割を果たします。

カーネルは、登録されているすべてのシステム コールのリストをシステム コール テーブルに記録し、sys_call_table に保存します。これはアーキテクチャ関連であり、通常はentry.sで定義されます。このテーブルは、有効な各システム コールに一意のシステム コール番号を割り当てます。

ユーザー空間プログラムがカーネル コードを直接実行することは困難です。カーネルは保護されたアドレス空間に存在するため、カーネル空間内の関数を直接呼び出すことはできません。アプリケーションは何らかの形式でシステムに通知し、システム コールを実行する必要があることをカーネルに伝え、システムはカーネル モードに切り替わりますlinux kernel を呼び出して、カーネルがアプリケーションに代わってシステム コールを実行できるようにします。カーネルに通知するためのこれらのメカニズムは、ソフト割り込みを通じて実装されます。 x86 システムのソフト割り込みは、int$0x80 命令によって形成されます。この命令は例外をトリガーし、システムをカーネル モードに切り替え、例外ハンドラー 128 番を実行します。このプログラムはシステム コール ハンドラーであり、その名前は system_call() です。これはハードウェア アーキテクチャと密接に関連しており、一般的にエントリ内の .s ファイル内のアセンブリ言語でコンパイルされています。

すべてのシステム コールは、危険信号 Linux システムと同じ形式でカーネルにトラップされるため、カーネル空間にトラップするだけでは十分ではありません。したがって、システム コール番号をカーネルに渡す必要があります。 x86 では、この転送は、softirq をトリガーする前にコール番号を eax レジスタに置くことによって実現されます。このようにして、システムコールハンドラを実行すると、eax からデータを取得できるようになります。前述の system_call() は、指定されたシステム コール番号を NR_syscalls と比較することによって、その番号の有効性をチェックします。 NR_syscalls 以下の場合、関数は -ENOSYS を返し、そうでない場合は、対応するシステム コールが実行されます: call*sys_call_table(,�x,4);

システム コール テーブルのエントリは 32​​ ビット (4 バイト) タイプで格納されるため、カーネルは指定されたシステム コール番号を 4 で除算し、その結果を使用してテーブル Location をクエリする必要があります。図 1 に示すように:

システムコール番号だけでなく、外部パラメータ入力も必要であることはすでに述べました。最も簡単な方法は、システム コール番号を渡すのと同じように、このパラメータをレジスタに格納することです。 x86 システムでは、ebx、ecx、edx、esi、edi は最初の 5 つのパラメータを順番に保存します。まれに 6 つ以上のパラメータが必要な場合は、別のレジスタを使用して、それらすべてのパラメータのユーザー空間アドレスを指すポインタを格納する必要があります。ユーザー空間への戻り値もレジスタを介して渡されます。 x86 システムでは、eax レジスタに格納されます。

システムコールは、すべてのパラメータが正当で有効であるかどうかを慎重にチェックする必要があります。システムコールはカーネル空間で実行されます。ユーザーがカーネルに不正な入力を渡すことを許可されると、システムのセキュリティと安定性は大きな試練に直面することになります。最も重要なテストは、ユーザーが提供した監視ポインターが有効かどうかを検出することです。カーネルがユーザー空間監視ポインターを受け取る前に、カーネルは次のことを確認する必要があります:

1) メーターの針が指すビデオメモリ領域はユーザー空間に属します

2) テーブルニードルが指すビデオメモリ領域はプロセスのアドレス空間にあります

3) 読み取り中の場合、読み取りメモリは読み取り可能としてマークされる必要があります。書き込みの場合、メモリは書き込み可能としてマークされている必要があります。

カーネルは、必要な検出を完了し、カーネル空間とユーザー空間の間でデータをコピーする 2 つの方法を提供します。これら 2 つのメソッドのいずれかを呼び出す必要があります。

内核调用用户态函数_内核调用call_linux 内核调用

copy_to_user(): ユーザー空間にデータを書き込むには 3 つのパラメーターが必要です。最初のパラメータは、プロセス空間内の宛先メモリ アドレスです。 2 番目はカーネル空間のソース アドレスです。

.3 番目は、コピーする必要があるデータ幅 (バイト数) です。

copy_from_user(): ユーザー空間からデータを読み取るには 3 つのパラメーターが必要です。最初のパラメータは、プロセス空間内の宛先メモリ アドレスです。 2 番目は、カーネル空間内のソースの場所です。

Address. 3 番目は、コピーする必要があるデータ幅 (バイト数) です。

注: これらは両方ともブロッキングを引き起こす可能性があります。このような状況は、ユーザー データを含むページが演算メモリではなくハードディスクにスワップアウトされるときに発生します。この時点で、

linux カーネルは

を呼び出し、ページ フォールト ハンドラーがページをハードディスクからケミカル メモリに置き換えるまで、プロセスはスリープ状態になります。 システム コールの実行中、カーネルはプロセス コンテキスト内にあり、現在のポインタはシステム コールを引き起こしたプロセスである現在のタスクを指します。プロセスのコンテキストでは、カーネルはスリープすることはできますが (たとえば、システム コールでブロックしている間、または明示的にスケジュール() を呼び出している間)、占有されている可能性があります。システム コールが返されると、制御は system_call() に残ります。system_call() は最終的にユーザー空間に切り替え、ユーザー プロセスが実行を継続できるようにする役割を果たします。

Linux にシステム コール時間を追加するのは非常に簡単ですが、システム コールをどのように設計して実装するかがジレンマです。システム コールを実装する最初のステップは、その目的を決定することです。この目的は明確かつ固有である必要があります。多目的のシステム コールを作成しようとしないでください。 ioctlはバックエンド教材です。新しいシステムコールのパラメータ、戻り値、エラーコードは非常に重要です。システム コールがコンパイルされると、それを今後のシステム コールとして登録するのは面倒な作業であり、通常は次の手順に従います。

1) システムコールテーブル (通常はentry.s にあります) の最後にエントリを追加します。 0 から数えて、テーブル内のシステム エントリの位置がそのシステム コール番号になります。

のように

10 システムコールはシステムコール番号 9

に割り当てられます。

2) どのアーキテクチャでも、システム コール番号は include/asm/unistd.h

で定義する必要があります。

3) システム コールはカーネル イメージにコンパイルする必要があります (モジュールにコンパイルすることはできません)。これを kernel/ の下の関連ファイルに置くだけです。

通常、システムコールはCライブラリでサポートされており、標準ヘッダファイルを組み込み、Cライブラリとリンクすることで、ユーザプログラムからシステムコール(または実際にライブラリ関数から呼び出されるライブラリ関数)を利用することができます。幸いなことに、Linux 自体には、システム コールに直接アクセスするための一連のマクロが用意されています。レジスタを設定し、int$0x80 命令を呼び出します。このマクロは _syscalln() です。n の範囲は 0 ~ 6 です。これは、システム コールに渡す必要があるパラメータの数を表します。これは、マクロは引数の数とその順序を正確に把握しておく必要があるためです。オープン システム コールを例に挙げます:

open() システム コールの定義は次のとおりです。

longopen(constchar*ファイル名,intflags,intmode)

このシステムが呼び出すマクロを直接呼び出す方法は次のとおりです。

#defineNR_open5

_syscall3(long,open,constchar*,ファイル名,int,flags,int,mode)

このようにして、アプリケーションは open() システム コールを呼び出してマクロをアプリケーション内に直接配置するだけで、直接 open() を使用できるようになります。各マクロには 2 つの 2*n パラメータがあります。各パラメータの意味は単純かつ明確なので、ここでは詳しく説明しません。

以上がLinux のシステム コールはカーネルへの正当なエントリではありませんの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はitcool.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。