linux系统编程——文件IO——并发写的冲突问题
1. 写文件的互斥
不论是 多线程,多进程,同时写一个文件,在数据写入 页缓存时,都是原子的,
如进程a 写入 "aaaa" 进程b 写入 "bbbb",则内核可能会将 "aaaa" 完整的写入 页缓存,在将完整的 "bbbb" 写入页缓存,
不会出现 "aa" "bbbb" "aa" 的情况。
相关代码如下
对于普通文件,write() 底层会调用 aio_write()
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) {
ssize_t err;
err = generic_write_sync(file, pos, ret);
if (err < 0 && ret > 0)
ret = err;
}
return ret;
}
2. 导致冲突的是 f_pos
aio_write() 的参数 pos,是从 f_pos来的,也就说,在 真正的原子写 前,确定的 pos,则pos没有原子性,
若 两个进程 写同个文件,由于 两个 进程的 struct file不同,所以 f_pos不同,导致内容覆盖。
若 父子进程 写同个文件,由于 struct file 相同,所以 f_pos 相同,但是 pos 的设置没有原子性,所以 也可能内容覆盖。
若 多线程 写同个文件,情况同上。
ksys_write() 中 获得 pos 和 设置 pos 没有上锁,所以 A线程获得pos为0,B线程获得pos为0,A线程写"aa",设置pos为2,B线程的pos仍然为0,导致A的数据被覆盖。
static inline loff_t file_pos_read(struct file *file)
{
return file->f_pos;
}
static inline void file_pos_write(struct file *file, loff_t pos)
{
file->f_pos = pos;
}
ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
{
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos = file_pos_read(f.file);
ret = vfs_write(f.file, buf, count, &pos);
if (ret >= 0)
file_pos_write(f.file, pos);
fdput_pos(f);
}
return ret;
}
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
size_t, count)
{
return ksys_write(fd, buf, count);
}
3. 用O_APPEND解决冲突
使用O_APPEND时,generic_file_aio_write()使用的pos在用的时候根据 inode大小 获得的,
所以 使用 O_APPEND 时,无论是 多线程,多进程 都能保证同步。
但是 若文件是 pipe,则写大小必须小于 PIPE_SIZE 才能保证写的完整性
4. 启发
由于 多线程 write() 最后会成 串行 write,所以使用mmap 直接 并发写页缓存更合适
5. 标准IO
标准IO能保证原子性,只要 对一个 FILE 写,则不会有冲突
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?