[読書ノート] Linuxカーネルの設計と実装システムの呼び出し

システムコールのインターフェイスの実装は、主にシステムの安定性と信頼性を確保し、アプリケーションの任意のアプリケーションを回避することです。

1.与内核通信

システムコールは、ユーザー空間プロセスとハードウェアデバイスの間に中間層を追加します。
役割は次のとおりです。

  1. ユーザー空間にハードウェア抽象インターフェースを提供します。
  2. システムの安定性と安全性を確保します。
  3. 各プロセスは仮想システムで実行され、ユーザー空間とシステムの残りの部分にそのような共通のインターフェースを提供します。

ps:Linuxシステムでは、システムコールがカーネルへのユーザー空間アクセスの唯一の手段です;例外とトラップを除いて、それらはカーネルの唯一の有効なエントリポイントです。

2. API、POSIXおよびCライブラリ

一般に、アプリケーションは、システムコールから直接ではなく、ユーザー空間に実装されたアプリケーションプログラミングインターフェイス(API)を介してプログラムされます。
ps:このプログラミングインターフェイスを使用するアプリケーションは、実際にはカーネルによって提供されるシステムコールに対応する必要はありません。

理解を深めるために図に示すように:
ここに画像の説明を挿入
unixのインターフェース設計には、戦略(これらの機能を達成する方法)ではなく、メカニズム(機能が提供する必要があるもの)を提供する」という格言がありますつまり、Unixシステムコールは、特定の目的を達成するための関数を抽象化し、これらの関数の使用方法については、カーネルを考慮する必要はありません。

3.システムコール

システムコール(Linuxでは多くの場合syscallと呼ばれます)にアクセスするには、通常はCライブラリで定義された関数呼び出しを使用します。
システムコールエラーが発生すると、Cライブラリはエラーコードをerrnoグローバル変数に書き込みます。この変数は、oerror()ライブラリ関数を呼び出すことでユーザーが理解できるエラー文字列に変換できます。

Q:システムコールを定義するにはどうすればよいですか?
A:32ビットシステムと64ビットシステム間の互換性を確保するために、システムコールの戻り値の型は、ユーザー空間とカーネル空間で異なり、intはユーザー空間で、longはカーネル空間です。関数名の先頭にもsys_が付いています(たとえば、システムコールgetpid()は、カーネルではsys_getpid()として定義されています)。

3.1システムコール番号対応システムコール機能

Linuxでは、各システムコールにシステムコール番号(一意)が与えられます。
ユーザー空間のプロセスがシステムコールを実行するとき、システムコール番号はどのシステムコールを実行するかを示すために使用されます。プロセスはシステムコールの名前を示しません(したがって、システムコール番号は重要です)。

Linuxには「実装されていない」システムコールsys_ni_syscall()があり、これは-ENOSYSを返す以外に何もしません。

カーネルは、システムコールテーブルに登録されたすべてのシステムコールのリストを記録し、それらをsys_call_tableに格納します。この表は、有効なシステムコールごとに一意のシステムコール番号を示しています。

3.2システムコールのパフォーマンス

Linuxシステムコールは、他の多くのオペレーティングシステムよりも高速に実行されます。
なぜですか?
A:Linuxの短いコンテキスト切り替え時間が重要な理由です。インカーネルとアウトカーネルの両方がシンプルで効率的になるように最適化されています
。2つ目はシステムコールハンドラーで、各システムコール自体も非常にシンプルです。

4.システムコールハンドラ

ユーザー空間プログラムはカーネルコードを直接実行できません。
アプリケーションは何らかの方法でシステムに通知し、カーネルがアプリケーションに代わってカーネルスペースでシステムコールを実行できるようにする必要があります。
カーネルに通知するメカニズムはソフト割り込みによって実装さます。例外を発生させることにより、システムはカーネル状態に切り替わり、例外ハンドラー(システムコールハンドラー)を実行します。

4.1適切なシステムコールを指定する

すべてのシステムコールは同じ方法でカーネルに分類されるため、カーネル空間に分類するだけでは不十分です。システムコール番号をカーネルに渡す必要があります。
ここに画像の説明を挿入

4.2パラメータの受け渡し

システムコール番号に加えて、ほとんどのシステムコールでは、いくつかの外部パラメーター入力も必要です。したがって、トラップが発生した場合、これらのパラメーターをユーザー空間からカーネルに渡す必要があります。最も簡単な方法は、システムコール番号を渡すように、これらのパラメーターをレジスターに格納することです。

5.システムコールの実装

5.1システムコールの実装

新しいシステムコールを実装する最初のステップは、その目的を決定することです。すべてのシステムコールには明確な目的があるはずです。Linuxで多目的システムコールを使用することはお勧めしません(システムコールは、異なるパラメーター値を渡すことにより、異なるタスクを完了することを選択します)。

5.2パラメータの検証

システムコールは、それらのすべてのパラメータが合法で有効かどうかを注意深くチェックする必要がありますユーザーがカーネルに不正な入力を渡すのを防ぎます。
各パラメーターをチェックして、それらが有効かつ有効であるだけでなく、正しいことも確認する必要があります。プロセスは、カーネルがアクセスできないリソースにカーネルにアクセスさせてはなりません。
最も重要な種類のチェックは、ユーザーが指定したポインターが有効かどうかをチェックすることです。
ユーザー空間のポインタを受け取る前に、カーネルは次のことを保証する必要があります。

  1. ポインタが指すメモリ領域はユーザー空間に属します。プロセスは、カーネル空間のデータを読み取るためにカーネルを同軸化してはなりません。
  2. ポインタが指すメモリ領域は、プロセスのアドレス空間にあります。プロセスは、カーネルをだまして他のプロセスからデータを読み取ってはなりません。
  3. 読み取られた場合、メモリは読み取り可能としてマークされます。書き込まれた場合、メモリは書き込み可能としてマークされます。実行可能である場合、メモリは実行可能としてマークされます。プロセスは、メモリアクセス制限をバイパスしてはなりません。

カーネルは、必要なチェックを完了し、カーネル空間とユーザー空間の間でデータをやり取りするための2つの方法を提供します。次の表に示すように:

方法 機能 解説 戻り値
copy_to_user() ユーザー空間にデータを書き込む 最初のパラメータはプロセス空間の宛先メモリアドレス、2番目はカーネル空間のソースアドレス、最後のパラメータはコピーされるデータの長さ(バイト数)です 実行失敗:コピーできなかったデータのバイト数;成功:0;上記のエラーが発生すると、システムコールは標準の-EFAULTを返します
copy_from_user() ユーザー空間からデータを読み取る copy_to_userと同様に、3つのパラメーターもあります。2番目のパラメーターで指定された位置のデータは、最初のパラメーターで指定された位置にコピーされます。コピーされたデータの長さは、3番目のパラメーターで決定されます 上記と同じ

ps:copy_to_user()とcopy_from_user()の両方がブロッキングを引き起こす可能性があります。ユーザーデータを含むページが物理メモリ上ではなくハードディスクにスワップアウトされると、ブロッキングが発生します。この時点で、ページフォールトハンドラーがページをハードディスクから物理メモリに戻すまで、プロセスはスリープ状態になります。

最後のチェックは法的権限のためです。
Linuxカーネルの新しいバージョンでは、よりきめ細かい「機能」メカニズムが提供され、新しいシステムでは、特定のリソースに対する特別な権限を確認できます。呼び出し元は、enable()関数を使用して、指定されたリソースを操作する権限があるかどうかを確認できます。ゼロ以外の値を返す場合、呼び出し元は操作する権限を持ち、0を返すと権限がありません。
例:

if(!capable(CAP_SYSY_BOOT))	/* 启动系统的系统管理员 */
	return -EPERM;

所有権の機能とその権限のリストについては、<linux / capability.h>を参照してください。

6.システムコールのコンテキスト

システムコールを実行するとき、カーネルはプロセスコンテキストにあります。現在のポインタは、システムコールを引き起こしたプロセスである現在のタスクを指しています。

プロセスのコンテキストではカーネルスリープ(システムコールがブロックされたときや、schedule()が明示的に呼び出されたときなど)でき、プリエンプトできます

6.1システムコールをバインドする最後のステップ

Q:システムコールを記述した後、それを正式なシステムコールとして登録する方法を教えてください。
A:

  1. システムコールテーブルの最後にエントリを追加します(ほとんどのアーキテクチャでは、このテーブルはentry.sファイルにあります)。システムコールをサポートするすべてのハードウェアシステムは、この種の作業を行う必要があります。このテーブルでのシステムコールの位置は、0から始まり、そのシステムコール番号です。
  2. サポートされているさまざまなアーキテクチャでは、システムコール番号を<asm / unistd.h>で定義する必要があります。通常、適切なコメントを呼び出す習慣は、ファイル内の5エントリごとに追加され、対応するシステムコールで見つけることができます。電話するときに便利にする
  3. システムコールはカーネルイメージにコンパイルする必要があります(モジュールにコンパイルできません)。カーネル/の下の関連ファイル(sys.cなど)に配置するだけで、さまざまなシステムコールが含まれます。

6.2ユーザースペースからシステムコールにアクセスする– Cライブラリ/ Linuxマクロ

通常、システムコールはCライブラリでサポートされています。標準ヘッダーファイルをインクルードし、Cライブラリとリンクすることにより、ユーザープログラムはシステムコール(またはライブラリ関数によって実際に呼び出されるライブラリ関数を呼び出す)を使用できます。
Linux自体は、システムコールに直接アクセスするための一連のマクロを提供し(Cライブラリヘッダーファイルを導入する必要はありません)、レジスターをセットアップし、トラップ命令を呼び出します。
これらのマクロは_syscalln()で、nの範囲は0から6で、システムコールに渡す必要のあるパラメーターの数を表します。これは、マクロがレジスターにプッシュされるパラメーターの数を順序どおりに知っている必要があるためです。
例:
open()のシステムコール定義は次のとおりです。

long open(const char *filename, int flags, int mode);

Cライブラリのサポートがない場合、マクロによる直接呼び出しの形式は次のとおりです。

#define NR_open 5		/* <asm/unistd.h>中定义的系统调用号 */
_syscall3(long, open, const char*, filename, int, flags, int, mode)  

マクロごとに、2 + 2xnパラメータがあります。
最初のパラメータは、システムコールの戻り値のタイプに対応します。
2番目のパラメーターは、システムコールの名前です。
以下は、システムコールパラメータの順に並べられた各パラメータのタイプと名前です。

6.3なぜシステムコールを使わないのか

Linuxシステムは、新しい抽象概念が現れるたびに、単に新しいシステムコールを追加することを避けようとします。
通常は、実装するシステムコールを次のように置き換えます。
デバイスノードを実装し、これにread()およびwrite()を実装します。ioctlを使用して、特定の設定を操作したり、特定の情報を取得したりします。

  1. セマフォなどの一部のインターフェイスはファイル記述子で表すことができるため、上記のように操作できます。
  2. 追加した情報をsysfsの適切な場所にファイルとして配置します。
公開された91件の元の記事 賞賛された17件 50,000回以上の閲覧

おすすめ

転載: blog.csdn.net/qq_23327993/article/details/105382862