- 论坛徽章:
- 0
|
回复 77# 思一克
这个问题结论差不多是pipe和普通磁盘文件有时可能必须加锁.
先说说之前对77楼这段代码理解有错误.
2.6中上面普通文件一般先调用 fs\read_write.c : do_sync_write- for (;;) {
- ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
- if (ret != -EIOCBRETRY)
- break;
- wait_on_retry_sync_kiocb(&kiocb);
- }
复制代码 其中aio_write中一般是通过 mm\filemap.c : generic_file_aio_write完成真正的写入- mutex_lock(&inode-
- >i_mutex);
- ret = __generic_file_aio_write_nolock(iocb, iov, nr_segs,
- &iocb->ki_pos);
- mutex_unlock(&inode->i_mutex);
复制代码 这个函数中的这个代码保证了一个原子性,但是通过第一段代码可想而知道一个不完全的
write导致整个write变成了非原子的.并且结合O_APPEND的方式就形成了交错写.但是数据不会因覆盖而丢失.
然后是基本结论和前一个精华贴子一楼的差不多
http://bbs2.chinaunix.net/viewth ... p;extra=&page=1
关于pipe已经有结论.POSIX标准也是这样描述,写入数据小于等于PIPE_BUF保证写入是原子的.否则会出现交错.对于O_NONBLOCK 方式.这种原子
性的含义是要么都写进去.要么一个都写不进去.不是这样方式倒没有严格定义.即有可能写一部分而返回.
关于4K是否就是PIPE_BUF?是的.但是不能假定从最开始写的4K里面是原子.而是保证在每次写时传入的参数. 因为pipe在内存中的数据的方式,
不同于普通文件,普通文件是静态的文件地址偏移空间,可以用确定的文件页.所以既不能按总字节数去确定边界也不能用pipe中数据长算边界.
2.4的pipe只有一个页的内存.所以其按其规则一个大于4K的write必定不能一次执行完.必然写入4K后返回.O_NONBLOCK方式允许交叉.并在等待
前释放了SEM旗语,因而交错
2.6有16个页,一次可以写多个页,只要总容量允许.如果容量不够也会阻塞并释放旗语从而导致交错
然后是74楼提到的问题不影响一次__generic_file_aio_write_nolock的原子性. 理由是write是针对内存中的文件映像操作.从读或写的层面考
虑不管文件内容在不在磁盘中都必须先在内存中对应的文件的中的页.然后是对这个页的写.一但写入完成则对其他read write都是可见的.所以
一致性在这里已经得到保障.不过从mutex_unlock(&inode->i_mutex); 这段代码下面的几行可以看出O_SYNC的同步并不受保护.也就是一次原子
性的写入和写得结果存入磁盘不是原子的.其实也不必要保证,反正同步到磁盘的是较新的文件映像就是了.绝不会用旧的覆盖新的.
在加上O_APPEND后的写由以上分析不会产生覆盖和文件空洞.因为2个真实的原子的写文件印象绝不可能同时操作,即使他们交错执行了.
但是对于上面的返回-EIOCBRETRY的错误非常少见。目前还没确定具体是什么地方发生。而返回非-EIOCBRETRY的时候多半就是内存耗尽反而不用加锁了因为只执行一次原子写。
一个普通磁盘文件write总是可以阻塞的,而这个阻塞发生在fs\buffer.c中 也就是发生在写文件的未调入内存的部分时从磁盘获取内容的时候发生。但是这个阻塞不会破坏上面的原子性。 |
|