Open7

ネットワークパケットはどのようにNICやCPUを経由してアプリまで届いているのか?を調べる

riddle_tecriddle_tec

ネットワークはなぜつながるのか 第2版 知っておきたいTCP/IP、LAN、光ファイバの基礎知識 | 戸根 勤, 日経NETWORK |本 | 通販 | Amazon をまず読み解く


アプリが通信を確立するとき

p79

  1. アプリが socket を呼び出すと、ソケットが作成される。アプリがソケットを識別するためのfd(ファイルディスクリプタ)が返ってくる。
  2. 続いて bind(fd, port, ...) をよんでソケットにポート番号を記録する
  3. 続いて listen(fd, ...) を呼んでソケットに接続待ち状態であるという制御情報を記録する
  4. 続いて acceptを実行し、ソケットをコピーしクライアントからの通信を待ち受ける
  5. 以後、新しい通信がくるたびにソケットをコピーして対応する

この時ソケットをコピーするが、クライアントからすると常に同じポート番号でアクセスできないと困るため、コピー先のソケットも同じポート番号を持つことになる。

そうなると区分けができなくなるので、クライアントのIP・ポート、サーバのIP・ポートの4つを key としてソケットを value とするハッシュテーブルで対応する。

サーバIP サーバポート クライアントIP クライアントポート ソケット
a b c d ソケットA
a b c e ソケットB

アプリがデータを送受信するとき

送信側

  1. アプリは送信データを自身のメモリに保存しておく
  2. write(fd, data, data_length, ...) を呼び出し、OSのプロトコルスタックに依頼する
  3. プロトコルスタックが、任意のプロトコル(ex: TCP -> IP) でラップし、LANドライバに渡す
  4. LAN ドライバは LAN アダプタの buffer memory にコピーする(p 165)
  5. コピーしたら、LAN アダプタ内の MAC 回路にパケットを送信するようにコマンドを投げる
  6. MAC 回路は buffer memory から送信パケットを取り出して、L2(ex: Ethernet) でラップする
  7. 続いて、MAC 回路は電気信号に変換し、PHY(Physical Layer Unit) or MAU(Medium Attachment Unit) と呼ぶ信号送受信部分に送る
    • デジタルデータを信号に変換する速度 = 伝送速度 (ex: 10Mbps)
  8. PHY(MAU) 回路がケーブルに送り出す形式に変換して、ケーブルに送信する

受信側

p 174 / p 402

  1. ケーブルからPHY(MAU) 回路が受け取った信号を共通形式に変換して、MAC回路に送る
  2. MAC回路は信号を頭からデジタルデータに変換し Buffer Memory に貯める
    • この時FCSを使って破損チェックを行う
  3. Ethernet ヘッダの宛先MACアドレスを見て、自分のMACアドレスと一致していれば Buffer Memory に保存する
  4. パケットを受信したことを 「割り込み」 として通知する。
    • LANアダプタが拡張バススロット部分にある割り込み用の信号線に信号を送る
    • 信号線はコンピュータ本体の割り込みコントローラを通じて CPU につながっている
    • CPU は信号が流れてくると実行していた、OS内部の割り込み処理用のプログラムに切り替える
    • LAN ドライバが呼び出される
  5. 割り込みによって LAN ドライバが動き、LANアダプタの Buffer Memory から保存されたパケットを取得する
  6. LAN ドライバはMAC ヘッダの type field からプロトコルを判別し、対応するプロトコルスタックにパケットを渡す(ここでは IP -> TCP とする)
  7. プロトコルスタックの IP 担当は、IP ヘッダから宛先 IP を取得し LAN アダプタに割り当てた IP と一致することを確認する。問題なければ TCP 担当に渡す
  8. TCP 担当は、IPヘッダの宛先IP、送信元IP、TCPヘッダの宛先ポート、送信元ポートを調べ該当するソケットを探す。
  9. ソケットの状態をチェックして、飛んできたパケットが制御系であれば応答用のパケットを TCP 担当が作って返したり、切断する。そうでなければ、データを受信バッファに貯める。
    • 接続の場合、TCP担当は該当する接続待ちのソケットをコピーして新しいソケットを作成する
  10. アプリが、Socket ライブラリの read(fd, receive_buffer, ...) を介してOS のプロトコルスタックに受信動作を依頼する(ここは既に実施されているのが普通)
  11. アプリにデータを渡し、受信バッファを空にする
riddle_tecriddle_tec

今抜けているところ


これ読む

Linuxデバイスドライバの開発 (I/O BOOKS) | 平田 豊 |本 | 通販 | Amazon

riddle_tecriddle_tec

read の話

サーバー入門、非同期処理入門、epoll 入門 | blog.ojisan.io

これを見ると

  1. シングルスレッドで、for - loop 内で read() を毎回叩きパケット飛んできた時に処理
  2. マルチスレッドで、read() を専門でやるスレッドを作る
  3. シングルスレッドで、epoll などのシステムコールを使ったイベント駆動

が紹介されてた。

epoll(7) は Linux 系の OS に備わっているイベント通知インターフェース

epoll では監視したいイベントを登録しておけば、そのイベントが完了した時に通知を受け取れます。一般的には epoll instance を作り、そのファイルディスクリプタに監視対象を紐付け、そのファイルディスクリプタを監視することでイベントの発生を検知します。

うーーん。わからないのは、epoll システムコールを使うとカーネルからアプリに push してくれるということなんかな?

riddle_tecriddle_tec

ちょっと風呂敷が広がってきた。
当初の目的の 「ネットワークパケットはどのようにNICやCPUを経由してアプリまで届いているのか?」は概ね理解できたので、デバイスドライバの本パラパラめくって終わりにする。