一つの大きなファイルを、複数の処理で分担して読みたいとします。各処理が「読みたい位置へ移動してから読む」という二段階を使うと、位置を動かしてから読むまでの間に、別の処理がその位置を変えることがあります。Linuxのpreadは、読み取り位置を呼び出しの引数として渡し、共有のファイルオフセットを動かさずに読むための仕組みです。
簡単にまとめると?
- preadは、先頭から何バイト目を読むかを直接指定します。同じファイル記述子を使う別の処理が共有位置を動かしても、その位置変更に読み始めを左右されません。
- 位置を指定することと、要求した全バイトを取得することは別です。短い読み取りでは、実際に読めた長さだけ次の指定位置を進めます。
- preadはファイル内容のスナップショットやロックを作りません。書き換えとの整合性は別に設計し、書き込み側のpwriteではLinuxのO_APPENDとの組合せにも注意します。
lseekとreadの間にある隙間
ファイルオフセットは、通常ファイルの「次に読み書きする位置」です。先頭を0とするバイト単位の位置で、オープンファイル記述というカーネル側の状態に保持されます。同じファイル記述子を複数のスレッドが使う場合や、dupで番号を複製した場合、この位置を共有します。
lseek(fd, offset, SEEK_SET)は位置を先頭からoffsetバイト目へ移し、その後のread()は共有位置から読みます。この二つは別の呼び出しです。処理Aが位置を0へ戻した直後、処理Bが位置を4へ移すと、Aのreadも4から読み始め得ます。個々の呼び出しが正常に終わっても、Aの意図した一連の操作にはなりません。
内容がABCDEFGHで、Aが先頭2バイトのAB、Bが位置4から2バイトのEFを読みたい場合を考えます。Aのlseek、Bのlseek、Aのread、Bのreadの順に動き、各readが2バイトを返せば、AにはEF、BにはGHが渡ります。問題はファイルの中身ではなく、読み始めを決める共有状態の取り合いにあります。[1、2]
読み始めを引数で渡すpread
pread(fd, buf, count, offset)は、offsetで指定した位置から最大countバイトをbufへ読みます。この呼び出しによって共有のファイルオフセットは変わりません。Aがoffsetを0、Bが4として呼べば、先ほどの共有位置変更の隙間を使わずに、それぞれの読み始めを指定できます。[1]

例えば、先頭にヘッダー、位置を計算できる場所に固定長レコードがあるファイルなら、レコード番号からバイト位置を計算して読む設計に向きます。順番どおりに読んでいる処理があっても、preadそのものはその共有位置を進めないため、位置の更新を避けた参照ができます。
ただし、すべての入出力先に使えるわけではありません。対象は位置を指定できる必要があります。通常のファイルと違い、パイプやソケットには「先頭から100バイト目へ移動する」という操作はできません。名前にreadが入っていても、バイトの流れから次のデータを読むreadの完全な代用品ではありません。
短く読めたときは、実際の長さだけ進める
preadのcountは最大の要求量です。成功しても、それより少ないバイト数が返ることがあります。戻り値が正なら実際に読めたバイト数、0なら終端、失敗なら−1とエラー情報を確認します。位置を直接指定したことを、全量取得の保証に読み替えないことが大切です。[1、RETURN VALUE]
位置100から合計8バイトが必要で、最初に3バイトを取得したとします。残りを読むなら、次の指定位置は103、残りの要求量は5です。もう一度位置100を指定すると先頭側を読み直し、8進めて108から読むと未取得の5バイトを飛ばします。短い読み取りを繰り返す処理では、バッファの書込み位置も同じ実取得量だけ進めます。
終端に達したのに必要量がそろわない場合は、形式上の不足として扱うのか、ファイルの成長を待つのかを用途に合わせて決めます。戻り値が0のまま同じ処理を無条件に繰り返しても、必要なデータが得られる保証はありません。エラーの扱いと、どこまでを一つの読取り単位として完成とするかを組み合わせて考えます。
位置が安定しても、中身は固定されない
preadが取り除くのは、共有オフセットの移動によって読取り開始位置がずれる問題です。別の処理がファイルを書き換えること、切り詰めること、複数の読み取りの間に内容の版が変わることまでは防ぎません。
たとえば、ヘッダーを一回目、本文を二回目のpreadで読む間に更新が入ると、二つの読取りが同じ版を見たとは、この仕組みだけでは判断できません。変更されないファイルを読む設計、関係する書き手も従うロック、版番号の検証など、内容の一貫性を確保する方法が別に必要になります。単に二つの位置を正しく指定できた、という事実と分けて扱います。
また、preadが複数スレッドの位置指定に便利であることは、必ず高速になるという意味ではありません。ストレージ、キャッシュ、アクセスの局所性、処理単位によって速度は変わります。今回の図は呼び出しの意味を確かめるモデルで、Linuxカーネルやストレージの性能測定ではありません。
pwriteと追記モードを組み合わせる前に
書き込み側の対応する操作はpwrite()です。指定位置へ最大countバイトを書き込み、共有オフセットは変更しない、という役割を持ちます。こちらも戻り値は実際の書込み量であり、一回で要求量が完了するとは限りません。[1]
特にLinux man-pages 6.19は、O_APPENDで開いたファイルへのpwriteに仕様上の注意点を記しています。POSIXではO_APPENDがpwriteの書込み位置に影響しないことを求める一方、同マニュアルが説明するLinuxの挙動では、offsetの値によらずファイル末尾へ追記します。指定位置へ更新するつもりで追記用の記述子を使わないよう、開くときのフラグまで確認します。[1、BUGS]
分担読取りを設計するなら、まず共有位置を使う必要があるか、位置を呼び出しごとに指定できるかを決めます。そのうえで、短い読取りの続き、終端とエラー、更新中の内容の一貫性を別々に点検します。preadはそのうち「どこから読むか」を明確にする道具です。
出典・参考資料
- [1] Linux man-pages 6.19 — pread(2), pwrite(2):DESCRIPTION、RETURN VALUE、NOTES、BUGS。2026年2月8日表記、2026年10月22日取得。
- [2] Linux man-pages 6.19 — lseek(2):ファイルオフセットとSEEK_SET、位置指定できない対象。並行順序と短い読取りの数値例は独自の説明モデルです。
