Linux頁快速緩衝與回寫機制分析

來源:互聯網
上載者:User

標籤:

參考


《Linux核心設計與實現》


*******************************************


頁快速緩衝是linux核心實現的一種主要磁碟緩衝,它主要用來降低對磁碟的IO操作,詳細地講,是通過把磁碟中的資料緩衝到實體記憶體中,把對磁碟的訪問變為對實體記憶體的訪問。為什麼要這麼做呢?一,速度;二暫時局部原理。有關這兩個概念,相信熟悉作業系統的我們不會太陌生。頁快速緩衝是由RAM中的物理頁組成的,緩衝中的每一頁都相應著磁碟中的多個塊。每當核心開始運行一個頁IO操作時,就先到快速緩衝中找。這樣就能夠大大降低磁碟操作。
      一個物理頁可能由多個不連續的物理磁碟塊組成。也正是因為頁面中映射的磁碟塊不一定連續,所以在頁快速緩衝中檢測特定資料是否已被緩衝就變得不那麼easy了。另外linux頁快速緩衝對被快取頁面的範圍定義的很寬。緩衝的目標是不論什麼基於頁的對象,這包括各種類型的檔案和各種類型的記憶體映射。為了滿足普遍性要求,linux使用定義在linux/fs.h中的結構體address_space結構體描寫敘述頁快速緩衝中的頁面,例如以下:

struct address_space {        struct inode            *host;              /* owning inode */        struct radix_tree_root  page_tree;          /* radix tree of all pages */        spinlock_t              tree_lock;          /* page_tree lock */        unsigned int            i_mmap_writable;    /* VM_SHARED ma count */        struct prio_tree_root   i_mmap;             /* list of all mappings */        struct list_head        i_mmap_nonlinear;   /* VM_NONLINEAR ma list */        spinlock_t              i_mmap_lock;        /* i_mmap lock */        atomic_t                truncate_count;     /* truncate re count */        unsigned long           nrpages;            /* total number of pages */        pgoff_t                 writeback_index;    /* writeback start offset */        struct address_space_operations   *a_ops;   /* operations table */        unsigned long           flags;              /* gfp_mask and error flags */        struct backing_dev_info *backing_dev_info;  /* read-ahead information */        spinlock_t              private_lock;       /* private lock */        struct list_head        private_list;       /* private list */        struct address_space    *assoc_mapping;     /* associated buffers */};

 當中的i_mmap欄位是一個優先搜尋樹,它的搜尋範圍包括了在address_sapce中私人的和共用的頁面。nrpages反應了address_space空間的大小。address_space結構往往會和某些核心對象關聯。通常情況下,會與一個索引節點(inode)關聯,這時host域就會指向該索引節點。假設關聯對象不是一個索引節點的話,比方address_space和swapper關聯時,這是host域會被置為NULL。a_ops域指向地址空間對象中的操作函數表,這與VFS對象及其操作函數表關係類似,操作函數表定義在linux/fs.h中,由address_space_operations表示,例如以下:
struct address_space_operations {        int (*writepage)(struct page *, struct writeback_control *);        int (*readpage) (struct file *, struct page *);        int (*sync_page) (struct page *);        int (*writepages) (struct address_space *, struct writeback_control *);        int (*set_page_dirty) (struct page *);        int (*readpages) (struct file *, struct address_space *,struct list_head *, unsigned);        int (*prepare_write) (struct file *, struct page *, unsigned, unsigned);        int (*commit_write) (struct file *, struct page *, unsigned, unsigned);        sector_t (*bmap)(struct address_space *, sector_t);        int (*invalidatepage) (struct page *, unsigned long);        int (*releasepage) (struct page *, int);        int (*direct_IO) (int, struct kiocb *, const struct iovec *,loff_t, unsigned long);};

  background-color: rgb(255, 255, 255);">這裡面最重要的兩個就是readpage()與writepage()了。對於readpage()方法而言,首先,一個address_space對象和一個位移量會被傳給該方法,這兩個參數用來在頁快速緩衝中搜素須要的資料:

page = find_get_page(mapping, index);mapping是指定的地址空間,index是檔案裡的指定位置。假設要搜尋的頁並沒在快速緩衝中,那麼核心將分配一個新頁面,然後將其增加到頁快速緩衝中,例如以下int error;cached_page = page_cache_alloc_cold(mapping);if (!cached_page)        /* error allocating memory */error = add_to_page_cache_lru(cached_page, mapping, index, GFP_KERNEL);if (error)        /* error adding page to page cache */


      最後,須要的資料從磁碟被讀入,再被增加頁快速緩衝,然後返回給使用者:error = mapping->a_ops->readpage(file,page);
      寫操作和讀操作有少許不同。對於檔案對應來說,當頁被改動了,VM只須要調用:setPageDirty(page);核心晚些時候通過writepage()方法把頁寫出。對特定檔案的寫操作會比較複雜----它的代碼在檔案mm/filemap.c中,通常寫操作路徑基本上要包括一下各步:
page = __grab_cache_page(mapping, index, &cached_page, &lru_pvec);status = a_ops->prepare_write(file, page, offset, offset+bytes);page_fault = filemap_copy_from_user(page, offset, buf, bytes);status = a_ops->commit_write(file, page, offset, offset+bytes);

      首先,在頁快速緩衝中搜尋須要的頁,假設須要的頁不在快速緩衝中,那麼核心在快速緩衝中新分配一空暇項;下一步,prepare_write()方法被調用,建立一個寫請求;接著資料被從使用者空間複製到核心緩衝;最後通過commit_write()函數將資料寫入磁碟。


由於在不論什麼頁IO操作前核心都要檢查頁是否已經在頁快速緩衝中了,所以這樣的檢查必須迅速,高效。否則得不償失了。前邊已經說過,也快速緩衝通過兩個參數address_space對象和一個位移量進行搜尋。每一個address_space對象都有唯一的基樹(radix tree),它保證在page_tree結構體中。基樹是一個二叉樹,僅僅要指定了檔案位移量,就能夠在基樹中迅速檢索到希望的資料,頁快速緩衝的搜尋函數find_get_
page()要調用函數radix_tree_lookup(),該函數會在指定基樹中搜尋指定頁面。基樹核心代碼的通用形式能夠在檔案lib/radix-tree.c中找到,另外想要使用基樹,須要包括標頭檔linux/radix_tree.h.


      在記憶體中累積起來的髒頁必須被寫回到磁碟,在一下兩種情況下,髒頁會被寫會到磁碟:


1.在空暇記憶體低於一個特定的閾值時,核心必須將髒頁寫回磁碟,以便釋放記憶體。
2.當髒頁在記憶體中駐留超過一定的閾值時,核心必須將逾時的髒頁寫會磁碟,以確保髒頁不會無限期地駐留在記憶體中。
      如今你僅僅需知道,2.6核心中,使用pdflush後台回寫常式來完畢這個工作。那麼詳細是怎麼實現的呢:


首先,pdflush線程在系統中的空暇記憶體低於一個特定的閾值時,將髒頁重新整理回磁碟。該後台回寫常式的目的在於在可用實體記憶體過低時,釋放髒頁以又一次獲得記憶體。上面提到的特定的記憶體閾值能夠通過dirty_background_ratio系統調用設定。一旦空暇記憶體比這個指小時,核心便會調用函數wakeup_bdflush() 喚醒一個pdflush線程,隨後pdflush線程進一步調用函數background_writeout()開始將髒頁寫會到磁碟,函數background_writeout()須要一個長整型參數,該參數指定試圖寫回的頁面數目。函數background_writeout會連續地寫會資料,直到滿足一下兩個條件:


1.已經有指定的最小數目的頁被寫回到磁碟。
2.空暇記憶體頁已經回升,超過了閾值dirty_background_ration.
      pdflush線程(實如今mm/pdflush.c中,回寫機制的實現代碼在檔案mm/page-writeback.c和fs/fs-writeback.c中)周期地被喚醒而且把超過特定期限的髒頁寫回磁碟。系統管理員能夠在/proc/sys/vm中設定回寫相關的參數,也能夠通過sysctl系統調用來設定它們。下表給出了能夠設定的量:


Linux頁快速緩衝與回寫機制分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.