Membuka regular file dengan O_DIRECT dapat membuat alamat user-space buffer, file offset, dan panjang transfer menjadi bagian yang terlihat dari antarmuka file. read() atau write() yang secara lain valid dapat gagal dengan EINVAL ketika salah satu nilai tersebut melanggar constraint direct I/O untuk file itu. Pada kombinasi filesystem dan perilaku kernel tertentu, operasi yang misaligned dapat beralih ke buffered I/O.
Batas ini mudah terlewat karena buffered file I/O biasa sebagian besar menyembunyikan geometri transfer fisik. Page cache dan filesystem dapat menerima application buffer pada alamat arbitrer lalu memediasi transfer secara internal. Direct I/O mengurangi mediasi tersebut, sehingga constraint yang biasanya berada di bawah batas system call dapat menjadi requirement untuk memory aplikasi dan bentuk request.
O_DIRECT karena itu bukan sinonim portabel untuk file descriptor tanpa cache. Di Linux, ia adalah file status flag dengan perilaku yang bergantung filesystem, aturan alignment yang dapat berbeda per file dan versi kernel, serta tanpa jaminan durability tersendiri.
Alignment adalah properti jalur I/O yang aktif
Linux mendokumentasikan tiga nilai yang dapat dibatasi untuk direct I/O: alamat memory setiap user-space buffer, file offset, dan panjang setiap segmen I/O. Kelipatan yang diwajibkan tidak ditentukan oleh flag O_DIRECT itu sendiri.
Program yang menanam konstanta seperti 4096 ke dalam logika allocation dan request sedang mengodekan asumsi lingkungan, bukan aturan Linux universal. Perilaku filesystem historis, logical block size, implementasi filesystem, dan pelaporan per-file yang lebih baru dapat menghasilkan requirement berbeda. Sebagian file juga dapat sama sekali tidak mendukung direct I/O.
Kontrak tersebut dapat digambarkan sebagai tiga predicate independen:
buffer address % memory_alignment == 0
offset % offset_alignment == 0
length % offset_alignment == 0Nilai tepatnya harus berasal dari interface yang berlaku, bukan dari diagram tersebut. Buffer dapat memenuhi requirement memory sementara offset request tidak, atau offset yang aligned dapat dipasangkan dengan panjang transfer yang melanggar batas offset/length yang sama.
Alignment karena itu lebih dari sekadar masalah allocator. Request slicing, retry logic, layout format file, dan penanganan tail semuanya dapat memengaruhi apakah operasi direct tetap valid.
statx() dapat mengekspos kontrak per-file
Sejak Linux 6.1, request STATX_DIOALIGN dapat meminta informasi alignment direct I/O melalui statx(). Ketika filesystem menyediakannya, stx_dio_mem_align melaporkan alignment yang diwajibkan untuk user memory dan stx_dio_offset_align melaporkan alignment untuk file offset dan panjang segmen I/O.
Nilai direct-I/O memory alignment nol menunjukkan bahwa direct I/O tidak didukung untuk file melalui kontrak pelaporan ini. Dukungan untuk field tersebut sendiri bergantung filesystem, sehingga tidak adanya data yang dilaporkan bukan izin untuk mengarang kelipatan fallback.
Interface ini mengubah bentuk setup direct I/O yang robust. Aplikasi dapat memperlakukan alignment sebagai metadata file yang ditemukan saat runtime ketika filesystem mengeksposnya, alih-alih mengikat buffer allocator pada konstanta global mesin.
Ada juga pembedaan yang lebih baru untuk read. Filesystem yang mendukung STATX_DIO_READ_ALIGN dapat melaporkan stx_dio_read_offset_align, sehingga requirement offset dan length untuk direct read dapat lebih longgar daripada alignment offset direct I/O umum. Nilai nol pada field tersebut berarti alignment offset umum tetap berlaku untuk read.
Batas pentingnya adalah capability reporting. Program harus membedakan constraint yang benar-benar dilaporkan dari constraint yang diasumsikan karena kernel tidak mendefinisikan satu alignment direct I/O universal untuk semua regular file.
Aligned allocation tidak membuat setiap slice ikut aligned
Mengalokasikan region dengan alignment yang sesuai hanya menetapkan properti pada base address. Properti itu tidak otomatis dipertahankan oleh setiap pointer turunan dari region tersebut.
Misalkan allocator mengembalikan buffer dengan alamat yang aligned ke 4096 byte:
void *base = NULL;
int rc = posix_memalign(&base, 4096, 8192);Jika 4096 memang merupakan memory alignment yang diwajibkan file target, base memenuhi requirement. Pointer (char *)base + 1 tidak. Queue atau parser yang memajukan pointer buffer dengan jumlah byte arbitrer karena itu dapat mengubah direct-I/O buffer yang valid menjadi misaligned tanpa mengubah allocation dasarnya.
Masalah yang sama muncul ketika satu aligned arena besar dibagi untuk beberapa request concurrent. Awal setiap request harus mempertahankan alignment yang diwajibkan. Arena dengan base aligned tidak membuat record variable-size yang dipadatkan cocok sebagai direct-I/O buffer.
Scatter/gather interface membawa constraint serupa pada granularitas segmen ketika direct I/O mensyaratkan segmen I/O aligned. Buffer ownership dan geometri buffer menjadi saling terkait: kode yang bebas melakukan slicing pada memory biasa mungkin membutuhkan representasi lebih ketat untuk buffer yang memasuki jalur direct I/O.
Tail file memperlihatkan perbedaan logical size dan geometri transfer
Panjang logis file tidak harus merupakan kelipatan direct-I/O alignment. Ini menciptakan batas pada region parsial terakhir.
Untuk read, aplikasi tidak dapat mengasumsikan bahwa setiap request yang berakhir tepat di end-of-file dapat diekspresikan sebagai short direct read arbitrer. Offset dan panjang segmen yang diminta tetap harus memenuhi aturan direct I/O yang berlaku. Perilaku filesystem yang didokumentasikan dan alignment yang dilaporkan menentukan request mana yang valid.
Untuk write, menambahkan padding hanya demi memenuhi alignment dapat mengubah isi atau logical length file jika aplikasi tidak memisahkan bentuk transfer fisik dari data model-nya. Format storage yang mengendalikan block layout sendiri dapat menangani hal ini secara eksplisit. General file editor dan jalur update berbasis byte biasanya kurang cocok.
Desain yang memerlukan mutasi byte-range arbitrer mungkin membutuhkan buffered path untuk region boundary, format dengan extent yang kompatibel dengan alignment, atau policy eksplisit lain. Pilihan tersebut adalah bagian dari data interface, bukan sekadar detail system-call flag.
Direct I/O dan durability adalah kontrak terpisah
O_DIRECT berusaha meminimalkan efek cache pada file I/O dan memindahkan data langsung antara jalur kernel yang menghadap storage dan user-space buffer ketika filesystem mendukung model itu. Ia tidak memberikan persistence guarantee milik O_SYNC hanya karena page cache dilewati atau dikurangi.
Perbedaan ini penting untuk write protocol. Direct write yang berhasil tidak boleh dianggap sebagai klaim crash durability kecuali aplikasi juga memakai synchronization semantics yang diperlukan. Dokumentasi Linux memisahkan O_DIRECT dari O_SYNC; aplikasi yang membutuhkan persistence sinkron memerlukan kontrak sinkronisasi terkait selain direct I/O.
Kedua flag menjawab pertanyaan berbeda:
O_DIRECT -> interaksi cache dan jalur transfer
O_SYNC -> semantik completion tersinkronisasiMenggabungkannya dapat tepat untuk desain storage tertentu, tetapi satu flag tidak menggantikan yang lain.
Mencampur buffered dan direct access menciptakan coherence boundary
Dokumentasi Linux menyarankan agar O_DIRECT tidak dicampur dengan buffered I/O biasa pada file yang sama, terutama untuk byte range yang overlap. Filesystem harus mengoordinasikan direct transfer dengan cached state, dan jalur yang dihasilkan dapat kehilangan kesederhanaan yang menjadi alasan memakai direct I/O.
File-backed mapping menciptakan batas terkait. Mencampur akses mmap() dengan direct I/O pada file yang sama memerlukan coherence antara mapped cached page dan direct transfer. Bahkan ketika filesystem menjaga coherence dengan benar, pola aksesnya tidak lagi setara dengan stream direct-I/O yang terisolasi.
Ini adalah persoalan semantik sebelum menjadi persoalan performa. Jika dua komponen mengakses byte yang sama melalui caching path berbeda, asumsi masing-masing tentang visibility harus cocok dengan perilaku coherence filesystem. Direct-I/O descriptor tidak menciptakan versi file privat.
Batas abstraksi yang paling aman sering kali berupa ownership atas byte range atau seluruh file. Ketika satu subsystem mengendalikan direct access, policy alignment, caching, dan synchronization dapat tetap konsisten secara internal. Shared access membuat policy tersebut menjadi bagian dari interface antar-subsystem.
Network filesystem dapat memindahkan batas cache
Nama O_DIRECT dapat memberi kesan jalur langsung menuju media fisik, tetapi model itu tidak berlaku pada semua filesystem. Untuk NFS, Linux client dapat melewati page cache miliknya sementara remote server masih dapat melakukan caching terhadap request. Flag tersebut tidak dapat dikirim sebagai instruksi universal kepada storage device melalui protocol.
Hal ini menunjukkan batas abstraksi yang lebih luas. Direct I/O mendeskripsikan perilaku pada boundary filesystem dan kernel tertentu. Ia tidak menetapkan arsitektur cache setiap lapisan di bawahnya, termasuk remote server, storage controller, atau device.
Klaim tentang cache bypass karena itu harus menyebut lapisan yang dibahas. Properti page cache di sisi client tidak otomatis menjadi properti cache server, dan keduanya tidak dengan sendirinya menetapkan persistence pada stable media.
Failure handling harus mempertahankan perbedaan mode
Misalignment sangat berbahaya ketika aplikasi memperlakukan direct I/O sebagai correctness property, bukan optimization. Perilaku Linux untuk direct I/O yang misaligned tidak seragam sebagai satu error path pada semua filesystem dan versi kernel: operasi dapat gagal dengan EINVAL, sementara sebagian kasus dapat jatuh kembali ke buffered I/O.
Desain yang membutuhkan invariant tanpa buffered I/O tidak dapat menyimpulkan invariant itu hanya dari completion yang sukses. Ia memerlukan kontrak environment dan filesystem yang menyediakan semantik tersebut, ditambah validasi yang sesuai.
Sebaliknya, aplikasi yang memakai direct I/O hanya sebagai mode performa opsional dapat memperlakukan capability yang tidak didukung atau alignment yang tidak sesuai sebagai alasan memilih buffered path. Policy ini sebaiknya eksplisit karena kedua mode memiliki constraint allocation, slicing, dan cache interaction yang berbeda.
Batas engineering yang tahan lama bukan ejaan O_DIRECT, melainkan sekumpulan kondisi ketika file, filesystem, kernel, buffer layout, offset, dan request length tertentu membentuk operasi direct I/O yang valid. Setelah kondisi itu dibuat eksplisit, alignment failure dan perubahan mode menjadi state interface, bukan storage error yang misterius.