免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12
最近访问板块 发新帖
楼主: yangPSO
打印 上一主题 下一主题

[文件系统] 关于文件读写互斥的问题 [复制链接]

论坛徽章:
1
15-16赛季CBA联赛之北控
日期:2016-03-15 22:53:29
11 [报告]
发表于 2014-11-22 22:00 |只看该作者
回复 10# 镇水铁牛

哈哈!感谢兄弟不厌其烦的回复!
不过还是想再问。
可能我之前描述的不清晰。当用户执行write系统调用时,首先第一步是找到page cache并将用户buffer的内容拷贝到page cache中;
第二步是在之后的某个时刻,比如flush线程刷新块设备时会将该page cache刷新到(回写到)磁盘中。
你说的函数__block_write_full_page()是在第二步时调用到的,所以可以对不同进程在第二步回写同一个page cache进行互斥,
但是假设有2个不同进程,一个在执行第一步,另一个在执行第二步,那么互斥做不到了。这就可能会出现page cache回写过程中又
被用户buffer覆盖的现象,是不是?


   

论坛徽章:
2
寅虎
日期:2014-11-25 21:47:342015小元宵徽章
日期:2015-03-06 15:58:18
12 [报告]
发表于 2014-11-23 03:18 |只看该作者
本帖最后由 镇水铁牛 于 2014-11-23 03:30 编辑

回复 11# yangPSO
  1. static int __block_write_full_page(struct inode *inode, struct page *page,
  2.                         get_block_t *get_block, struct writeback_control *wbc,
  3.                         bh_end_io_t *handler)
  4. {
  5. .......................第一个大循环.......................
  6.         do {
  7.                 if (block > last_block) {
  8.                         /*
  9.                          * mapped buffers outside i_size will occur, because
  10.                          * this page can be outside i_size when there is a
  11.                          * truncate in progress.
  12.                          */
  13.                         /*
  14.                          * The buffer was zeroed by block_write_full_page()
  15.                          */
  16.                         clear_buffer_dirty(bh);
  17.                         set_buffer_uptodate(bh);
  18.                 } else if ((!buffer_mapped(bh) || buffer_delay(bh)) &&
  19.                            buffer_dirty(bh)) {
  20.                         WARN_ON(bh->b_size != blocksize);
  21.                         err = get_block(inode, block, bh, 1);/////////////////////////////核心:建立未映射且脏bh和block设备的映射关系
  22.                         if (err)
  23.                                 goto recover;
  24.                         clear_buffer_delay(bh);
  25.                         if (buffer_new(bh)) {
  26.                                 /* blockdev mappings never come here */
  27.                                 clear_buffer_new(bh);
  28.                                 unmap_underlying_metadata(bh->b_bdev,
  29.                                                         bh->b_blocknr);
  30.                         }
  31.                 }
  32.                 bh = bh->b_this_page;
  33.                 block++;
  34.         } while (bh != head);

  35. .......................第二个大循环.......................过滤所有脏的bh
  36.         do {
  37.                 if (!buffer_mapped(bh))
  38.                         continue;
  39.                 /*
  40.                  * If it's a fully non-blocking write attempt and we cannot
  41.                  * lock the buffer then redirty the page.  Note that this can
  42.                  * potentially cause a busy-wait loop from writeback threads
  43.                  * and kswapd activity, but those code paths have their own
  44.                  * higher-level throttling.
  45.                  */
  46.                 if (wbc->sync_mode != WB_SYNC_NONE) {
  47.                         lock_buffer(bh);
  48.                 } else if (!trylock_buffer(bh)) {//////////////////////////////////////////////////这里会lock,如果失败,redirty page
  49.                         redirty_page_for_writepage(wbc, page);
  50.                         continue;
  51.                 }
  52.                 if (test_clear_buffer_dirty(bh)) {
  53.                         mark_buffer_async_write_endio(bh, handler);  /////lock成功的话,设置bh的回调
  54.                 } else {
  55.                         unlock_buffer(bh);
  56.                 }
  57.         } while ((bh = bh->b_this_page) != head);

  58.         /*
  59.          * The page and its buffers are protected by PageWriteback(), so we can
  60.          * drop the bh refcounts early.
  61.          */
  62.         BUG_ON(PageWriteback(page));
  63.         set_page_writeback(page);

  64. .......................第三个大循环.......................
  65.         do {
  66.                 struct buffer_head *next = bh->b_this_page;
  67.                 if (buffer_async_write(bh)) {
  68.                         submit_bh(write_op, bh);///此时bh是在locked状态,在bh回调中释放,此时也是在page lock中submit bh》submit bio,make request,io只是在scsi queue中,不一定立即下发给LLDD,但也会很快,记得有2个阈值限定,3ms超时和4个request触发。
  69.                         nr_underway++;
  70.                 }
  71.                 bh = next;
  72.         } while (bh != head);
  73.         unlock_page(page);

  74.         ............................................................
复制代码
但是假设有2个不同进程,一个在执行第一步,另一个在执行第二步,那么互斥做不到了。这就可能会出现page cache回写过程中又
被用户buffer覆盖的现象,是不是?
    【回复】看了我对write full page的分析,第一步和第二步是不可能同时执行(同一个page,因为page lock),但是一个page是write back状态后,是可以再次被dirty的,这个不冲突,因为在设置write back前,该该IO已经被加入块设备的queue中了,大不了被第二次访问覆盖写了(很少有这种现象,即使有也没事)。此时可以理解为只有第二次覆盖写后的数据才是最新的。
    page利用bh提交io的灵活性:如果此时page状态是write back,说明它至少已经提交了一个bh_a,假设page内的bh_b是第二次写请求弄脏的,即使bh_a还未执行完(即end bh),此时还是可以继续提交bh_b。只要你能lock这个bh,该io就可以向下层转发。

论坛徽章:
1
15-16赛季CBA联赛之北控
日期:2016-03-15 22:53:29
13 [报告]
发表于 2014-11-24 15:11 |只看该作者
回复 12# 镇水铁牛


感谢回复。
下面是我的一些想法,不知道有没有问题:
(1)        write系统调用之间是互斥的,read系统调用之间以及read系统调用和write系统调用之间则不一定是互斥的。
以ext4为例,它的aio_read回调函数为generic_file_aio_read(),其中没有添加任何的锁;
aio_write回调函数为ext4_file_write(),该函数调用了generic_file_aio_write(),后者在调用函数__generic_file_aio_write()之间加了inode->i_mutex锁。

而xfs文件系统做的更加细化。它通过xfs_rw_ilock()和xfs_rw_iunlock()做到了read、write系统调用之间的各种互斥。

如果考虑到共享mmap,上面的锁都将失效。因为此时文件读写操作是普通的内存读写,不涉及系统调用。

为了保证共享mmap、多次read、write系统调用的原子性,可以借助于文件锁等手段。

(2)        PG_locked标志锁的目的并不是直接用于实现write系统调用之间的互斥,它主要是保证page cache以及相关bh的各种状态位的一系列设置时的原子性。
PG_writeback标志是为了防止page cache重复提交到块层,而buffer lock锁是为了对bh状态操作的原子性,也可以防止将bh重复提交到块层。如果page cache处于writeback状态,writepages回调函数需要等待其变为非writeback状态,即使该page cache的某些相关bh是unlock的。

(3)        假设某个page cache相关的bh已经提交到块层,那么它是lock的。如果此时进程A从用户buffer内容拷贝到该bh中,该bh将再次标识为脏的。但它是lock的,所以不能再重复提交到块层。这里可能出现下面的操作序列,虽然概率不大:
a)        某个page cache相关的某个bh被提交到块层;
b)        进程A将用户buffer内容拷贝到该bh中,bh的内容被更新;
c)        bh最新内容被写入磁盘,然后回调函数end_buffer_async_write();
d)        进程A接着设置bh为dirty的,但是实际上其最新内容已经被写入磁盘;
e)        之后的某个时刻,bh因为是dirty的,将被再次提交到块层,这是多余的。

   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP