O_APPEND到底保证了什么O_APPEND解决的是**seek 到末尾 → write之间的竞态** 不用O_APPEND时两个进程竞争 进程A:lseek(end100)进程B:lseek(end100)← 踩到同一位置 进程A:write(10字节)→ 写到100~110进程B:write(10字节)→ 写到100~110← 互相覆盖 用O_APPEND 内核把移到末尾 写入打包成一个原子操作 进程A:append → 写到100~110进程B:append → 写到110~120← 自动追加不覆盖 结论O_APPEND防的是位置覆盖不是内容交叉---write()原子上限 ┌──────────────────────┬───────────────────────────────┬──────────────────────────────────┐ │ 场景 │ 保证 │ 上限 │ ├──────────────────────┼───────────────────────────────┼──────────────────────────────────┤ │ 管道/FIFO│POSIX强制保证原子 │PIPE_BUF4096字节Linux │ ├──────────────────────┼───────────────────────────────┼──────────────────────────────────┤ │ 本地文件ext4/xfs │ Linux 实现上靠 inode 锁串行化 │ 理论无上限但实际看单次write()│ ├──────────────────────┼───────────────────────────────┼──────────────────────────────────┤ │NFS/网络文件系统 │ 不保证 │ — │ └──────────────────────┴───────────────────────────────┴──────────────────────────────────┘ Linux 本地文件系统写入流程write()系统调用 └─vfs_write()└─ 获取 inode-i_rwsem(写锁)└─ 移到文件末尾写数据 └─ 释放锁 所以本地文件O_APPEND的并发写在 Linux 上是串行化的两个进程的内容不会交叉拼接——但这是 Linux 实现细节不是POSIX承诺。---什么情况下仍然会坏 单次write()写入内核单次处理能力通常 writev 分块 → 内核可能拆成多次写锁释放再加锁 → 内容交叉 实际危险线单次write()超过几十KB时要小心日志行通常几百字节安全---PHP实际写法?php// 方案1O_APPEND 小日志行 4KB够用$fpfopen(/var/log/app.log,a);// a 模式自动带 O_APPENDfwrite($fp,date(c). [INFO] user login\n);// 一次 write()原子fclose($fp);// 方案2多进程写大块内容加 flock 兜底$fpfopen(/var/log/app.log,a);flock($fp,LOCK_EX);// 进程级互斥锁fwrite($fp,$bigChunk);flock($fp,LOCK_UN);fclose($fp);// 方案3FFI 直调精确控制 flags$ffiFFI::cdef(Cintopen(constchar*path,intflags,intmode);ssize_twrite(intfd,constvoid*buf,size_t count);intclose(intfd);C,libc.so.6);// O_WRONLY1 O_CREAT64 O_APPEND1024$fd$ffi-open(/var/log/app.log,1|64|1024,0644);$msgdate(c). log line\n;$ffi-write($fd,$msg,strlen($msg));// 一次 syscall原子追加$ffi-close($fd);---一句话总结 ▎O_APPEND保证不覆盖位置Linux 本地文件靠 inode 锁保证不交叉内容单次write()保持在4KB 以内最稳跨机器/NFS必须上 flock。
php方案 日志文件追加(Append-Only)语义: 在多进程 PHP 环境中
O_APPEND到底保证了什么O_APPEND解决的是**seek 到末尾 → write之间的竞态** 不用O_APPEND时两个进程竞争 进程A:lseek(end100)进程B:lseek(end100)← 踩到同一位置 进程A:write(10字节)→ 写到100~110进程B:write(10字节)→ 写到100~110← 互相覆盖 用O_APPEND 内核把移到末尾 写入打包成一个原子操作 进程A:append → 写到100~110进程B:append → 写到110~120← 自动追加不覆盖 结论O_APPEND防的是位置覆盖不是内容交叉---write()原子上限 ┌──────────────────────┬───────────────────────────────┬──────────────────────────────────┐ │ 场景 │ 保证 │ 上限 │ ├──────────────────────┼───────────────────────────────┼──────────────────────────────────┤ │ 管道/FIFO│POSIX强制保证原子 │PIPE_BUF4096字节Linux │ ├──────────────────────┼───────────────────────────────┼──────────────────────────────────┤ │ 本地文件ext4/xfs │ Linux 实现上靠 inode 锁串行化 │ 理论无上限但实际看单次write()│ ├──────────────────────┼───────────────────────────────┼──────────────────────────────────┤ │NFS/网络文件系统 │ 不保证 │ — │ └──────────────────────┴───────────────────────────────┴──────────────────────────────────┘ Linux 本地文件系统写入流程write()系统调用 └─vfs_write()└─ 获取 inode-i_rwsem(写锁)└─ 移到文件末尾写数据 └─ 释放锁 所以本地文件O_APPEND的并发写在 Linux 上是串行化的两个进程的内容不会交叉拼接——但这是 Linux 实现细节不是POSIX承诺。---什么情况下仍然会坏 单次write()写入内核单次处理能力通常 writev 分块 → 内核可能拆成多次写锁释放再加锁 → 内容交叉 实际危险线单次write()超过几十KB时要小心日志行通常几百字节安全---PHP实际写法?php// 方案1O_APPEND 小日志行 4KB够用$fpfopen(/var/log/app.log,a);// a 模式自动带 O_APPENDfwrite($fp,date(c). [INFO] user login\n);// 一次 write()原子fclose($fp);// 方案2多进程写大块内容加 flock 兜底$fpfopen(/var/log/app.log,a);flock($fp,LOCK_EX);// 进程级互斥锁fwrite($fp,$bigChunk);flock($fp,LOCK_UN);fclose($fp);// 方案3FFI 直调精确控制 flags$ffiFFI::cdef(Cintopen(constchar*path,intflags,intmode);ssize_twrite(intfd,constvoid*buf,size_t count);intclose(intfd);C,libc.so.6);// O_WRONLY1 O_CREAT64 O_APPEND1024$fd$ffi-open(/var/log/app.log,1|64|1024,0644);$msgdate(c). log line\n;$ffi-write($fd,$msg,strlen($msg));// 一次 syscall原子追加$ffi-close($fd);---一句话总结 ▎O_APPEND保证不覆盖位置Linux 本地文件靠 inode 锁保证不交叉内容单次write()保持在4KB 以内最稳跨机器/NFS必须上 flock。