同時にwrite(2)すると混ざるかどうか

Linuxシステムコールである write(2) の ドキュメントを読むと

Atomic/non-atomic: A write is atomic if the whole amount written in one operation is not interleaved with data from any other process. This is useful when there are multiple writers sending data to a single reader. Applications need to know how large a write request can be expected to be performed atomically. This maximum is called {PIPE_BUF}. This volume of IEEE Std 1003.1-2001 does not say whether write requests for more than {PIPE_BUF} bytes are atomic, but requires that writes of {PIPE_BUF} or fewer bytes shall be atomic.

との記載があり、PIPE_BUF(Linux では 4KB)のサイズより小さい場合は atomic なのでログが混ざることはないが、それより大きい場合は混ざることがある、というように読めます。

RubyのLoggerはスレッドセーフ(&プロセスセーフ)かどうか調べてみた - sonots:blog

この件、引用されたこともあって確認した。古い linux kernel (2.6.35.13) だけど。

まず、vfs レイヤの動作として、 vfs_write -> do_sync_write -> f_op->aio_write の順で呼び出される。

そして、各fs実装 (f_op) の呼び出しが以下のようになる。

ext2/ext3の場合、aio_write は generic_file_aio_write なので、以下のように追記モード云々に関係なく、常にロックをとる*1

/**
 * generic_file_aio_write - write data to a file
 * @iocb:       IO state structure
 * @iov:        vector with data to write
 * @nr_segs:    number of segments in the vector
 * @pos:        position in file where to write
 *
 * This is a wrapper around __generic_file_aio_write() to be used by most
 * filesystems. It takes care of syncing the file in case of O_SYNC file
 * and acquires i_mutex as needed.
 */
ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                unsigned long nr_segs, loff_t pos)
{
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
        ssize_t ret;

        BUG_ON(iocb->ki_pos != pos);

        mutex_lock(&inode->i_mutex);
        ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
        mutex_unlock(&inode->i_mutex);

        if (ret > 0 || ret == -EIOCBQUEUED) {
                ssize_t err;

                err = generic_write_sync(file, pos, ret);
                if (err < 0 && ret > 0)
                        ret = err;
        }
        return ret;
}
EXPORT_SYMBOL(generic_file_aio_write);

xfsの場合はxfs_file_aio_writeの中でdirect I/Oでなければブロックする。

PS. POSIX的にはOSの自由だろとか、nfsだったらどうだろという議論はあると思うので用法用量を守ってお使いください。

*1:そして、だから MySQLext2/ext3 は遅いとか言われるんですねー