关于文件读写互斥的问题
各位好,版主好,请教大家一个问题:===============================================================
一方面,在xfs文件系统的读操作回调函数filp->f_op->aio_read,即函数xfs_file_aio_read()中
有下面的代码:
xfs_rw_ilock(ip, XFS_IOLOCK_SHARED);
…………………………
ret = generic_file_aio_read(iocb, iovp, nr_segs, iocb->ki_pos);
…………………………
xfs_rw_iunlock(ip, XFS_IOLOCK_SHARED);
个人的理解是为了对进程读文件操作和写文件操作进行互斥。
另一方面,page cache具有PG_locked和PG_writeback标志,也具有互斥作用。
问题是:
(1) 如果不考虑DIRECT IO,利用page cache的PG_locked和PG_writeback标志就可以做到
进程在VFS层对page cache的读操作和写操作、块层将page cache回写到磁盘和从磁盘
读数据到page cache这些操作的互斥,上面的xfs_rw_ilock是否是多余的。
(2) 包括ext4在内的很多文件系统的filp->f_op->aio_read回调函数实际上就是函数
generic_file_aio_read(),它为什么没有类似xfs文件系统的xfs_rw_ilock()。
那么如何保证对进程读文件操作和写文件操作进行互斥。
本帖最后由 镇水铁牛 于 2014-11-21 19:37 编辑
看了xfs_rw_ilock(ip, XFS_IOLOCK_SHARED);的实现是个读写锁,对xfs不熟,谈谈我的观点。
如果xfs对于数据一致性要求很高的话,假设这种场景:
当用户态的IO要写page前,会调用generic_file_buffered_write,调用它前page还未被lock。
但此时系统同时下发一个读同地址的数据的IO请求,有可能就从磁盘上或cache中返回了,数据无法保持一致性,虽然这种概率是很低的。
所以用个读写锁搞定inode,是最保险的,对于读多写少的场景,性能应该还可以。 本帖最后由 yangPSO 于 2014-11-22 11:57 编辑
回复 2# 镇水铁牛
想了想,page cache的PG_locked和PG_writeback标志确实无法保证进程对
page cache的读操作和写操作的互斥。实际上内核代码并不保证这种互斥,
像ext4就没有提供这种保护,而xfs提供了。但是如果考虑到mmap的情况,
这种互斥根本没法保证。这需要用户态应用程序通过文件锁或者其它措施
来保证。不知道说的对不对?一次read或者write系统调用到底有没有原子性?
另外说到mmap,会不会出现底层磁盘驱动正在以DMA的方式将page cache中
数据写往磁盘,与此同时,用户态应用程序正在通过 memcpy方式修改page cache?
如果会出现这种情况,会带来什么影响?
另外说到mmap,会不会出现底层磁盘驱动正在以DMA的方式将page cache中数据写往磁盘,与此同时,用户态应用程序正在通过 memcpy方式修改page cache?如果会出现这种情况,会带来什么影响?
【回答】当磁盘输出从page cache往磁盘DMA方式写入时,此时page是write back状态,bh是被lock的,此时上层的应用是不能修改page中的数据的;
系统也有类似wait_on_page_writeback(page);的保障的。 本帖最后由 yangPSO 于 2014-11-22 20:18 编辑
回复 4# 镇水铁牛
【以linux-2.32为例】
个人理解:
xfs文件系统在回写某个page cache到块层时设置了page cache的PG_writeback标志,然后释放了PG_locked标志,具体来说就是:
通过如下的函数关系
mapping->a_ops->writepages(mapping, wbc);
xfs_vm_writepages
generic_writepages
write_cache_pages
调用到函数write_cache_pages(),其中包含如下的代码:while (!done && (index <= end)) {
int i;
nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, tag,
min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1);
if (nr_pages == 0)
break;
for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages;
..................
lock_page(page);
..................
if (PageWriteback(page)) {
if (wbc->sync_mode != WB_SYNC_NONE)
wait_on_page_writeback(page);
else
goto continue_unlock;
}
BUG_ON(PageWriteback(page));
..................
ret = (*writepage)(page, wbc, data);
..................
}对于每个待写(刷新)的page cache,首先调用函数lock_page()进行加锁,如果page正在回写过程中并且wbc->sync_mode为WB_SYNC_NONE,
将调用函数wait_on_page_writeback()等待回写过程结束。然后调用writepage回调函数,即函数xfs_vm_writepage()执行写操作。
同时,函数xfs_vm_writepage()通过如下的函数关系
xfs_vm_writepage
xfs_start_page_writeback
set_page_writeback
test_set_page_writeback
ret = TestSetPageWriteback(page);
设置page的PG_writeback标志,表明page处于回写过程中。
同时,函数xfs_start_page_writeback()在设置完page的PG_writeback标志后将调用函数unlock_page()将page解锁,在解锁前一刻将page设置为Uptodate的。
那么某个进程就可能在在该page cache 的PG_locked标志释放后成功设置其PG_locked标志后去写这个page cache,而该page cache可能正在以DMA的方式将page cache中数据写往磁盘。
进程写page cache并不需要判断PG_writeback标志,只会判断PG_locked标志。
另外mmap方式写内存根本不执行系统调用,更不会判断什么锁状态。
回复 5# yangPSO
文件系统在回写某个page cache到块层时设置了page cache的PG_writeback标志,然后释放了PG_locked标志是对的,在IO写操作的整个流程中,在两个地方会对page进行lock操作,一个是标脏(prepare write附近),一个是set writeback(过滤dirty page并准备submit)那里。
但是page lock这个锁粒度太大,而且灵活性不足,可以看看buffer head的lock,io提交就是基于bio的,这个锁就十分灵活。
回复 6# 镇水铁牛
我在函数generic_perform_write()看不到lock_buffer()。
lock_buffer()是不是只对回写同一个page cache的不同上下文起到互斥作用,但是进程从系统调用写pagecache不受它的影响啊
像mmap方式只是memcpy写page cache,没有进入内核,根本没有什么锁啊。 本帖最后由 镇水铁牛 于 2014-11-22 21:00 编辑
另外mmap方式写内存根本不执行系统调用,更不会判断什么锁状态。
【回复】
没使用过mmap,但mmap是映射磁盘上的文件,也就是说它应该是把磁盘上的文件映射到进程的某段vma中。
对该地址空间的访问,和访问真实文件是一样的,
如果是读操作,通过访问fd对应的struct file,对磁盘上的文件进行操作,我感觉mmap和普通vfs接口好像没啥大的差别(不知道mmap函数返回的地址有没有分配实际物理内存)。
如果是写的话,mmap写的是进程用户态的虚拟地址,在内核可以通过该虚拟地址找到其物理页框,然后进行数据拷贝,这个好像和direct io很相似,那mmap和mmap间应该能保证互斥,如果此时如果有别的vfs入口访问该文件,好像还真的无法保证数据一致性了。
这些是我的猜测哈。 本帖最后由 yangPSO 于 2014-11-22 21:17 编辑
回复 8# 镇水铁牛
如果暂时不考虑mmap,常规的写分2步,首先是系统调用写pagecache,然后在之后某个时刻将pagecache回写。
lock_buffer()是不是只对回写同一个page cache的不同路径起到互斥作用,而进程从系统调用写pagecache不受它的影响,
也就是说,lock_buffer()做不到回写page cache和系统调用写pagecache的互斥???
你可以细看下__block_write_full_page。这里搞成双簧了哈,:lol刷屏实在是不好啊。:dizzy:
页:
[1]
2