ソケットプログラミング:write
/read
とsend
/recv
の責務と関係
ソケットプログラミングにおいて、write
/read
とsend
/recv
の違いに戸惑う人は多いでしょう。この記事では、Unixの「Everything is a file」の思想による抽象化の背景を踏まえ、これらの関数の責務と関係を説明し、なぜ両方の関数で通信が可能なのかを解説します。
Everything is a fileの思想とソケット
Unixシステムでは、「Everything is a file」という設計思想があります。この思想に基づき、ネットワークソケットもファイルディスクリプタとして抽象化されます。つまり、ソケットに対してファイル操作関数(write
/read
)を使用することができるのです。
この抽象化により、プログラマはファイルI/Oと同じインターフェースでネットワークI/Oを扱うことができ、コードの統一性と可読性が向上します。
ユーザー空間とカーネル空間の責務
ソケット通信では、ユーザー空間とカーネル空間が異なる責務を担っています。
ユーザー空間の責務:
- ソケットの作成とファイルディスクリプタの管理
-
write
/read
やsend
/recv
関数を使ってソケットにデータを書き込み、読み取る
カーネル空間の責務:
- ソケットバッファの管理
- プロトコルスタックの処理
- ネットワークデバイスドライバの制御
- 実際のデータ転送
write
/read
関数の責務
write
関数は、ファイルディスクリプタに対してデータを書き込みます。ソケットの場合、write
関数を呼び出すと、データはカーネル空間のソケットバッファにコピーされます。
read
関数は、ファイルディスクリプタからデータを読み取ります。ソケットの場合、read
関数を呼び出すと、カーネル空間のソケットバッファからデータがユーザー空間にコピーされます。
send
/recv
関数の責務
send
関数とrecv
関数は、ソケット通信に特化した関数です。これらの関数は、write
/read
関数と同様に、データをソケットバッファにコピーしたり、ソケットバッファからデータを読み取ったりします。
ただし、send
/recv
関数は追加のフラグ引数を受け取ることができ、送受信動作をより細かく制御できます。
データ転送の責務はカーネルにある
重要なのは、実際のデータ転送はカーネルが担っているということです。ユーザー空間のwrite
/read
やsend
/recv
関数は、データをソケットバッファにコピーしたり、ソケットバッファからデータを読み取ったりするだけです。
データがソケットバッファに書き込まれると、カーネルが以下の処理を行います:
- プロトコルスタックでのデータ処理
- ルーティングとARP
- ネットワークデバイスドライバへのデータ受け渡し
- ネットワークインターフェイスカード(NIC)からの送信
まとめ
Unixの「Everything is a file」の思想により、ネットワークソケットはファイルディスクリプタとして抽象化されています。これにより、write
/read
関数を使ってソケット通信を行うことができます。
ソケット通信では、ユーザー空間の責務はソケットへのデータの書き込みと読み取りであり、実際のデータ転送はカーネル空間の責務です。write
/read
やsend
/recv
関数は、データをソケットバッファにコピーしたり、ソケットバッファからデータを読み取ったりするだけで、直接的なデータ転送は行いません。
したがって、write
/read
関数でもソケット通信が可能なのは、カーネルがソケットバッファに書き込まれたデータを元に通信を行うからです。send
/recv
関数は、送受信動作をより細かく制御できる点でwrite
/read
関数と異なります。