ソケットプログラミング: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関数と異なります。