Sebuah pipe Linux dapat memiliki kapasitas buffer puluhan kilobyte sementara jaminan atomic write hanya berlaku hingga PIPE_BUF. Kedua batas ini menjelaskan properti yang berbeda: kapasitas mengatur jumlah data yang dapat ditampung buffer, sedangkan PIPE_BUF mengatur apakah satu write request dapat diselingi data dari writer lain.

Mencampuradukkan keduanya menghasilkan framing protocol yang rapuh. Sebuah message dapat muat dalam storage pipe yang tersedia tetapi tetap melampaui batas atomicity.

Atomicity berlaku pada satu write request

POSIX mendefinisikan write ke pipe dengan ukuran paling banyak PIPE_BUF byte sebagai atomic terhadap writer lain. Pada Linux, PIPE_BUF bernilai 4096 byte.

Atomicity di sini berarti byte dari write() tersebut tidak diselingi byte dari proses lain yang menulis ke pipe yang sama. Ini tidak berarti call bersifat nonblocking, durable, transactional dengan write berikutnya, atau dipertahankan sebagai message untuk reader.

Untuk descriptor blocking, request yang tidak lebih besar dari PIPE_BUF akan menunggu bila perlu sampai kernel dapat menerima seluruh request secara atomic. Untuk descriptor nonblocking, request dengan ukuran yang sama akan diterima seluruhnya atau gagal dengan EAGAIN; request tidak diterima sebagian.

Batas ini membuat satu write() kecil berguna sebagai primitive untuk mengirim record ketika beberapa proses berbagi satu pipe. Memecah satu logical record ke dua call menghilangkan properti tersebut pada batas antar-call.

Write yang lebih besar memiliki semantik berbeda

Ketika write request melampaui PIPE_BUF, jaminan atomicity tidak lagi berlaku.

Dengan blocking I/O, call dapat block sampai seluruh byte yang diminta telah ditulis, tetapi data dari writer lain dapat muncul di antara bagian-bagian request. Selesainya call tidak memulihkan atomic framing.

Dengan O_NONBLOCK, request yang lebih besar dapat menghasilkan partial write ketika sebagian ruang tersedia. Return value kemudian menunjukkan prefix yang diterima. Jika pipe penuh, call gagal dengan EAGAIN.

Ukuran yang diberikan ke write() menentukan klasifikasi ini. Request yang lebih besar dari PIPE_BUF tidak memperoleh jaminan small-write hanya karena invocation tertentu kebetulan mengembalikan 4096 byte atau kurang.

Kapasitas mengatur backpressure

Kapasitas pipe adalah jumlah data yang dapat ditampung kernel sebelum write tambahan menghadapi backpressure. Linux mengekspos kapasitas saat ini melalui fcntl() dengan F_GETPIPE_SZ dan mengizinkan perubahan kapasitas melalui F_SETPIPE_SZ, sesuai batas kernel dan permission.

Kapasitas bukan ukuran atomic record. Menaikkan kapasitas pipe dari 64 KiB menjadi 1 MiB dapat membuat lebih banyak data menumpuk sebelum writer block, tetapi tidak menaikkan PIPE_BUF Linux dari 4096 byte.

Perbedaannya juga berlaku ke arah sebaliknya. Small atomic write dapat block pada descriptor blocking ketika free space tidak mencukupi. Atomic berarti tidak dapat dibagi relatif terhadap writer lain, bukan jaminan bahwa data langsung diterima.

Linux juga mencatat bahwa writable space efektif dapat lebih kecil daripada kapasitas nominal pipe untuk pola write tertentu karena storage pipe berbasis page. Kapasitas karena itu merupakan batas buffering, bukan janji bahwa setiap byte di bawah angka tersebut selalu dapat diterima dalam satu operasi.

Batas reader bersifat independen

Pipe biasa adalah byte stream. Perilaku atomic pada writer mencegah interleaving di dalam write request yang memenuhi syarat, tetapi reader tidak diwajibkan menerima satu write untuk setiap read().

Read buffer dapat mengembalikan byte yang berasal dari beberapa write, atau hanya mengambil sebagian byte dari satu write. Aplikasi yang mengenkode record dalam pipe tetap memerlukan framing yang dapat diparse reader, seperti fixed-size record atau length field.

Aturan atomicity melindungi framing tersebut dari interleaving antar-writer hanya ketika setiap record yang dilindungi dikirim melalui satu write() dengan panjang request yang tidak melampaui PIPE_BUF.

Linux juga mendukung packet-mode pipe melalui pipe2() dengan O_DIRECT. Mode tersebut mengubah batas read dan write dan merupakan mekanisme terpisah dari atomicity pada pipe biasa.

Tuning buffer tidak mengubah framing contract

Meningkatkan kapasitas pipe dapat mengurangi stall pada writer saat burst dan dapat mengubah karakteristik scheduling atau throughput. Ini merupakan penyesuaian queue depth.

Mengubah framing contract memerlukan keputusan berbeda. Jika record harus tetap utuh di antara beberapa writer pada pipe biasa, setiap record yang bergantung pada atomicity pipe harus muat di dalam PIPE_BUF dan dikirim dalam satu write request.

Record yang lebih besar memerlukan mekanisme serialization lain, framing protocol yang dapat menangani interleaving, atau transport IPC dengan message semantics yang sesuai dengan ukuran record yang dibutuhkan.

PIPE_BUF dan kapasitas pipe karena itu berada pada lapisan antarmuka yang berbeda. Yang satu membatasi jaminan atomicity. Yang lain membatasi buffered occupancy dan backpressure. Memperlakukan salah satunya sebagai pengganti yang lain mengubah perilaku yang dapat diamati saat terjadi contention.