Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6087686
  • 博文数量: 2759
  • 博客积分: 1021
  • 博客等级: 中士
  • 技术积分: 4091
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-11 14:14
文章分类

全部博文(2759)

文章存档

2019年(1)

2017年(84)

2016年(196)

2015年(204)

2014年(636)

2013年(1176)

2012年(463)

分类: LINUX

2014-12-21 10:26:04

原文地址:VFS之write调用的理解 作者:镇水铁牛

在vfs层:
    首先还是统一的vfs_write入口,调用统一的aio接口,如果是direct io则直接下发,如果是buffer io则要走vfs中的chche机制。
在blcok层:
     以buffer io为例,常规的写需要执行3步:write_begin---copy_data---write_end
     write_begin主要是在address_space查找该page,如果找到则lock,没找到则分配一个page,并lock
     copy_data为常规的用户态数据copy到内核中,访问page时需要kmap。
     write_end则
目的是更新buffer_head,page, inode的状态(主要是标脏),并unlock该page.
     然后唤醒pdflush,此时,异步IO就返回了。
在scsi层:
对于pdflush:
     当dirty page数量大于阈值、或者dirty page在内存中存在过长时间,pdflush会被激活。
     首先遍历所有dirty的inode,然后依次回写mapping中的数据到设备中。
     在write_cache_pages中,lock page后,直到调用__block_write_full_page,遍历出脏缓冲区,set_page_writeback。
     之后submit_bh,unlock page,但此时bh已经被lock设置回调end_bio_bh_io_sync。
     之后submit_bio,将bio提交到磁盘驱动维护的请求队列中,现在重点关注__generic_make_request
__generic_make_request:
     获取bio对应的块设备文件对应的磁盘对象的请求队列,如md设备,或者vdisk设备等,实现bio的多次转发,直到最终的物理磁盘。
     块设备初始化时,注册make_request_fn为__make_request,如果__make_request返回0,则说明bio走到物理磁盘了。
     看此IO能否和queue合并,如果不能merge,则分配新的request(可能会睡眠,做流控限制),加入队列中等待merge和调度。
     题外话:常说的direct io就是此bio不进入队列合并,直接触发unplug机制向底层设备下发,barrier io类似,只是多了sync操作。
unplug机制:
     IO超时激活,或者IO超过4个激活,或者对实时性要求很高的IO激活unplug机制,其request_fn实现以此处理queue中的requset。
request_fn机制(Main strategy routine for SCSI):
     在scsi_alloc_queue时,注册了scsi_request_fn,执行elv_next_request,翻译request为scsi cmd,核心为prep_rq_fn,直到dispatch_cmd.
prep_rq_fn的两次绑定:
     scsi_prep_fn当scsi设备在scsi层被初次识别时,由于此时SCSI设备只能处理来自SCSI公共服务层的请求,如inq和report lun等命令。
     sd_prep_fn当该scsi设备被进一步识别后,preq_rq_fn被再次重新绑定,它即能处理公共服务层请求,也能处理上层的bio请求。
dispatch_cmd:
     将scsi cmd下发给具体的scsi host(即hba),并注册回调scsi_done,queuecomand函数的实现由scsi host driver完成
在scsi的LLDD层:
     lsi将scsi cmd加入到自己内部的queue进行维护,timeout处理走scsi系统默认流程(某个设备的timeout处理,会导致整个host的io被挂起)。
     mv用状态机维护scsi cmd,timeout处理走自己内部的实现。
callback:
        blk_softirq_init注册blk_done_softirq
        rq->q->softirq_done_fn(rq);    //scsi_softirq_done
        scsi_finish_command
        scsi_end_request
        blk_end_request
        req_bio_endio
        blk_end_io
        end_that_request_data
        __end_that_request_first
        req_bio_endio
        bio_endio
        bio->bi_end_io(bio, error);   //end_bio_bh_io_sync
        bh->b_end_io = end_buffer_async_write;   //这里unlock bh
        end_page_writeback(page);
附言:
     当某个page已经为writeback,此时上层的再次写该页,其只有等上次的writeback完成后,才能进行本次操作。
     因此就有了
write_cache_pages中
    
if (PageWriteback(page)) {
            if (wbc->sync_mode != WB_SYNC_NONE)
                    wait_on_page_writeback(page);
    }
     在end_page_writeback(page)中就有wake_up_page(page, PG_writeback);
阅读(2678) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~