2

read を呼び出した後に実際に何が起こるか:

n = read(fd, buf, try_read_size);

ここで fd は TCP ソケット記述子です。buf はバッファーです。try_read_size は、プログラムが読み取ろうとするバイト数です。

これにより、最終的にカーネルへのシステムコールが呼び出される可能性があると思います。しかし、誰かが詳細を提供できますか? glibc またはカーネル ソースでのソース コードの実装を教えてください。

4

1 に答える 1

6

大まかに見ると、次のようになります。

  • glibc が提供するラッパー関数が呼び出される
  • ラッパー関数は、スタックに渡されたパラメーターをレジスターに入れ、syscall 番号をその目的専用のレジスターに設定します (x86 の EAX など)。
  • ラッパー関数は、トラップまたは同等の命令を実行します (例: SYSENTER)
  • CPU が ring0 に切り替わり、トラップ ハンドラが呼び出されます
  • トラップ ハンドラーは、syscall 番号の有効性をチェックし、カーネル関数へのジャンプ テーブルでそれを検索します。
  • それぞれのカーネル関数は、引数が有効かどうかをチェックします (たとえば、アクセス可能なメモリ ページbufを参照する範囲は、実際にはファイル記述子です)。何か問題がある場合は、負のエラー コード (-EFAULT など) が生成され、CPU がユーザー モードに戻り、呼び出しがラッパーに戻ります。buf+try_read_sizefd
  • ファイル記述子のタイプに応じて別の関数が呼び出されます(あなたの場合はソケットですが、ブロックデバイスまたはprocエントリまたはよりエキゾチックなものから読み取ることができます)
  • ソケットの入力バッファがチェックされます:
    • バッファにデータがある場合は、min(available, try_read_size)にコピーされbuf、その量が戻りコード レジスタ (x86 では EAX) に書き込まれ、CPU がユーザー モードに切り替えられ、呼び出しがラッパーに戻ります。
    • 入力バッファが空の場合
      • 接続が閉じられている場合は、リターン コード レジスタにゼロが書き込まれ、CPU がユーザー モードに切り替えられ、呼び出しがラッパーに戻ります。
      • 接続が閉じられ ていない場合
        • ソケットがノンブロッキングの場合、負のエラー コード ( -EAGAIN) が戻りコード レジスタに書き込まれ、CPU はユーザー モードに戻り、呼び出しはラッパーに戻ります。
        • ソケットがノンブロッキングでない場合、プロセスは中断されます
  • ラッパー関数は、戻り値が負 (エラー) かどうかをチェックします。
    • 正またはゼロの場合、値を返します。
    • 負の場合、errno を負の値に設定し (のエラーが報告されます)、-1 を返します。
于 2012-04-19T10:58:41.243 に答える