write 系統調用耗時長的緣由

 

    前一陣子公司一部門有人叫幫忙調查,說他們write系統調用基本上是個位數微秒就返回,或者說幾十us,但偶爾出現幾回write系統調用達到幾百毫秒和狀況。你們都知道,經過vfs進行write,都是寫寫到page cache中,而後內核線程再按期同步到磁盤。寫到內存應該是很快的纔對。剛開始,我作了許多設想,1)磁盤IO過重,內存中的髒數據達到必定比率後write必須同步寫到磁盤;2)那些耗時長的write是使用direct io,繞過了page cache;三、剛恰好write一個page時,read也在讀同一page,那個page被lock了,write要等它。後來每一種假設又都被本身推翻了。如下是經過vfs進行write系統調用的圖:函數

 

    前幾天,使用systemtap進行一步步的定位,最後把可疑的點鎖定在__block_write_begin這個函數中(fs/buffer.c)。spa

    先說明下這個函數是幹什麼的。generic_perform_write函數是把數據寫入磁盤的主要處理函數。它先調用address_space_operations. write_begin(其中會調用咱們上文提到的__block_write_begin函數),接下來把數據拷貝到上一步分配出來的page中,最後調用address_space_operations.write_end。上面所說的write_begin和write_end會具體根據不一樣的file system調用不一樣的函數,拿ext4來講,它的函數以下(有dellay allocation的話):線程

static const struct address_space_operations ext4_da_aops = {

         .readpage                 = ext4_readpage,

         .readpages               = ext4_readpages,

         .writepage                = ext4_writepage,

         .writepages              = ext4_writepages,

         .write_begin            = ext4_da_write_begin,

         .write_end                = ext4_da_write_end,

         .bmap                        = ext4_bmap,

         .invalidatepage                = ext4_da_invalidatepage,

         .releasepage            = ext4_releasepage,

         .direct_IO                 = ext4_direct_IO,

         .migratepage           = buffer_migrate_page,

         .is_partially_uptodate  = block_is_partially_uptodate,

         .error_remove_page      = generic_error_remove_page,

};

 

    能夠看到,ext4的write_begin是ext4_da_write_begin。Ext4_da_write_begin會先分配一個頁框,而後調用__block_write_begin分配緩衝區(爲何要分配緩衝區這裏就不說明了)。那麼,__block_write_begin分配緩衝區而已,爲何有時候要耗時那麼長呢?先看看代碼:code

int __block_write_begin(struct page *page, loff_t pos, unsigned len,

get_block_t *get_block)

{

  ......

  if (!buffer_uptodate(bh) && !buffer_delay(bh) &&

    !buffer_unwritten(bh) &&

     (block_start < from || block_end > to)) {

  ll_rw_block(READ, 1, &bh); //這裏指定read操做,提交BIO

  *wait_bh++=bh;

  }

......

 

/*

* If we issued read requests - let them complete.

*/

  while(wait_bh > wait) { //這裏等待io操做完成

  wait_on_buffer(*--wait_bh);

  if (!buffer_uptodate(*wait_bh))

  err = -EIO;

  }

}

 

  

    write的基本單位是塊(由磁盤文件系統定義,默認爲4K)。這樣的話,page cache中一個page(恰好也爲4K)恰好就是一個塊。好比說write的地址是512,長度是20,page cache就分配了一個能寫 0~4095的頁page frame,這個page frame恰好對應一個磁盤塊,假如把要write的數據拷貝到這個page frame的 512~531,這樣的話0~511和532~4095是空的。可是下次write back的時候會把整塊數據都寫入磁盤,因此須要把這一整塊的數據都先從磁盤中讀出來,寫入page cache中,以防止0~511和532~4095被誤寫。這個讀操做應該就是耗時長的緣由了。orm

相關文章
相關標籤/搜索