頁高速緩存和頁回寫[2]

 

   由於頁高速緩存的緩存作用,寫操作實際上會被延遲。當頁高速緩存中的數據比後臺存儲的數據更新時,那麼該數據就被稱爲髒數據。在內存中積累起來的頁最終必須被寫回磁盤。在以下兩種情況發生時,髒頁被寫回磁盤:

1. 當空閒的內存低於一個特定的閾值時,內核必須將髒頁寫回磁盤,以便釋放內存。

2. 當髒頁在內存中駐留時間超過一個特定的閾值時,內核必須將超時的髒頁寫回磁盤,以確保髒頁不會無限期地駐留在內存。

   在老內核中,這是由兩個獨立的內核線程分別完成。但是在2.6版本中,由一組內核線程統一執行這兩種工作---pdflush後回寫線程。這兩個目標是如何實現的?

   首先,pdflush線程在系統中的空閒內存低於一個閾值時,將髒頁刷新回磁盤。該後臺回寫例程的目的在於在可用物理內存過低時,釋放髒頁以重新獲得內存。特定的內存與之可以通過dirty_background_ratio sysctl系統調用設置(看kernel/Sysctl.c)。

  

  1. 在mm/Page_writeback.c中
  2. /* Start background writeback (via pdflush) at this percentage
  3.  */
  4. int dirty_background_ratio = 10;

   當空閒內存比閾值dirty_background_ratio還低時,內核便會調用函數wakeup_bdflush()(我沒找到該函數)喚醒一個pdflush線程,隨後pdflush線程進一步調用函數background_writeout()開始將髒頁寫回磁盤。該函數的參數指定試圖寫回的頁面數目,該函數會連續的寫出數據,直到滿足一下兩個條件:

1. 已經有指定的最小數據的頁被寫出到磁盤

2. 空閒內存數已經回升,超過閾值dirty_background_ratio

  1. 在mm/Page-writeback.c中
  2. /*
  3.  * writeback at least _min_pages, and keep writing until the amount of dirty
  4.  * memory is less than the background threshold, or until we're all clean.
  5.  */
  6. static void background_writeout(unsigned long _min_pages)
  7. {
  8.     long min_pages = _min_pages;
  9.     struct writeback_control wbc = {
  10.         .bdi        = NULL,
  11.         .sync_mode  = WB_SYNC_NONE,
  12.         .older_than_this = NULL,
  13.         .nr_to_write    = 0,
  14.         .nonblocking    = 1,
  15.         .range_cyclic   = 1,
  16.     };
  17.     for ( ; ; ) {
  18.         long background_thresh;
  19.         long dirty_thresh;
  20.         get_dirty_limits(&background_thresh, &dirty_thresh, NULL);
  21.         if (global_page_state(NR_FILE_DIRTY) +
  22.             global_page_state(NR_UNSTABLE_NFS) < background_thresh
  23.                 && min_pages <= 0)
  24.             break;
  25.         wbc.encountered_congestion = 0;
  26.         wbc.nr_to_write = MAX_WRITEBACK_PAGES;
  27.         wbc.pages_skipped = 0;
  28.         writeback_inodes(&wbc);
  29.         min_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
  30.         if (wbc.nr_to_write > 0 || wbc.pages_skipped > 0) {
  31.             /* Wrote less than expected */
  32.             congestion_wait(WRITE, HZ/10);
  33.             if (!wbc.encountered_congestion)
  34.                 break;
  35.         }
  36.     }
  37. }

   pdflush後臺例程會被週期喚醒,將那些在內存中駐留時間過長的髒頁寫出,確保內存中不會有長期存在的髒頁。如果系統發生崩潰,由於內存處於混亂中,所以那些在內存中還沒來得及寫回磁盤的髒頁就會丟失,所以週期性同步頁高速緩存和磁盤非常重要。

  在系統啓動時,內核初始化一個定時器,讓它週期地喚醒pdflush線程,隨後使其運行函數wb_kupdate()。該函數將把所有駐留時間超過百分之dirty_expire_centisecs秒的髒頁寫回。然後定時器將再次被初始化爲百分之dirty_expire_centisecs秒後喚醒pdflush線程。

  1. 在mm/Page-writeback.c中
  2. /*
  3.  * Periodic writeback of "old" data.
  4.  *
  5.  * Define "old": the first time one of an inode's pages is dirtied, we mark the
  6.  * dirtying-time in the inode's address_space.  So this periodic writeback code
  7.  * just walks the superblock inode list, writing back any inodes which are
  8.  * older than a specific point in time.
  9.  *
  10.  * Try to run once per dirty_writeback_interval.  But if a writeback event
  11.  * takes longer than a dirty_writeback_interval interval, then leave a
  12.  * one-second gap.
  13.  *
  14.  * older_than_this takes precedence over nr_to_write.  So we'll only write back
  15.  * all dirty pages if they are all attached to "old" mappings.
  16.  */
  17. static void wb_kupdate(unsigned long arg)
  18. {
  19.     unsigned long oldest_jif;
  20.     unsigned long start_jif;
  21.     unsigned long next_jif;
  22.     long nr_to_write;
  23.     struct writeback_control wbc = {
  24.         .bdi        = NULL,
  25.         .sync_mode  = WB_SYNC_NONE,
  26.         .older_than_this = &oldest_jif,
  27.         .nr_to_write    = 0,
  28.         .nonblocking    = 1,
  29.         .for_kupdate    = 1,
  30.         .range_cyclic   = 1,
  31.     };
  32.     sync_supers();
  33.     oldest_jif = jiffies - dirty_expire_interval;
  34.     start_jif = jiffies;
  35.     next_jif = start_jif + dirty_writeback_interval;
  36.     nr_to_write = global_page_state(NR_FILE_DIRTY) +
  37.             global_page_state(NR_UNSTABLE_NFS) +
  38.             (inodes_stat.nr_inodes - inodes_stat.nr_unused);
  39.     while (nr_to_write > 0) {
  40.         wbc.encountered_congestion = 0;
  41.         wbc.nr_to_write = MAX_WRITEBACK_PAGES;
  42.         writeback_inodes(&wbc);
  43.         if (wbc.nr_to_write > 0) {
  44.             if (wbc.encountered_congestion)
  45.                 congestion_wait(WRITE, HZ/10);
  46.             else
  47.                 break;  /* All the old data is written */
  48.         }
  49.         nr_to_write -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
  50.     }
  51.     if (time_before(next_jif, jiffies + HZ))
  52.         next_jif = jiffies + HZ;
  53.     if (dirty_writeback_interval)
  54.         mod_timer(&wb_timer, next_jif);
  55. }

   總而言之,pdflush線程週期地被喚醒並把超過特定期限的髒頁寫回磁盤。

   系統管理員可以在/proc/sys/vm中設置回寫的相關參數,也可以通過sysctl系統調用設置它們。

  pdflush線程的實現代碼在文件mm/pdflush.c中,回寫機制的實現代碼在文件mm/page-writeback.c和fs-writeback.c中。

 pdflush設置

變量 描述
dirty_expire_centisecs 該數值以百分之一秒爲單位,它描述超時多久的數據將被週期性執行的pdflush線程寫出
dirty_ratio 佔全部內存百分比,當一個進程產生的髒頁達到這個比例時,就開始被寫出
dirty_writeback_centisecs 該數值以百分之一秒爲單位,它描述pdflush線程的運行頻率
laptop_mode 一個布爾值,用於控制膝上型電腦模式
dirty_background_ratio 佔全部內存的百分比。當內存中空閒頁到達這個比例時,pdflush線程開始回寫髒頁

 

  • 膝上型電腦模式

  膝上型電腦模式是一種特殊的頁回寫策略,該策略主要意圖是將硬盤轉動的機械行爲最小化,允許硬盤儘可能長時間的停滯,以此延長電池供電時間。該模式可通過/proc/sys/vm/laptop_mode文件進行配置。通常,該配置文件內容爲0,即膝上型電腦模式關閉,寫1則啓用該模式。

  該模式的頁寫回行爲與傳統方式相比只有一處變化。除了當緩存中的頁面太舊要執行回寫髒頁以外,pdflush還好找準磁盤運轉的實際,把所有其他的物理磁盤IO、刷新髒換成等統統寫回磁盤,以便保證不會專門爲了寫磁盤而去主動激活磁盤運行。

   所述Linux發佈版會在電腦接上或拔下電池時,自動開啓或禁止膝上型電腦模式及其需要的pdflush可調節開關。因此機器可在使用電池電源時自動進入膝上型電腦模式,而在插上交流電源時恢復到常規的頁回寫模式。

  • 避免擁塞的方法:使用多線程

  因爲磁盤的吞吐量有限,如果只有惟一線程執行回寫操作,那麼這個線程很容易等待對一個磁盤上的操作。爲了避免出現這樣的情況,內核需要多個回寫線程併發執行,這樣單個設備隊列的擁塞就不會稱爲系統的瓶頸了。

   2.6內核使用多個pdflush線程,每個線程可以相互獨立地將髒頁刷新回磁盤,而且不同的pdflush線程處理不同的設備隊列。線程的數目可以根據系統的運行時間進行調整。pdflush線程數量取決於頁回寫的數量和擁塞情況,動態調整。如果所有存在的pdflush線程都忙着寫回數據,那麼一個新線程就會被創建,確保不會出現一個設備隊列處於擁塞狀態,而其他設備隊列卻在等待---不是接收--回寫數據的情況。如果堵塞,pdflush線程的數量便會自動減少,以便節約內存。

   爲了避免每一pdflush線程都掛起在同一個堵塞的隊列上,pdflush線程利用了擁塞避免策略,它們會積極的試圖寫回那些不屬於擁塞隊列的頁面。這樣一來,pdflush線程通過分派回寫工作,阻止多個線程在同一個忙設備上糾纏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章