天天看點

Linux緩存機制之頁緩存

        Linux運用一個功能廣泛的緩沖和緩存架構來提高系統的速度。緩沖和緩存利用一部分系統實體記憶體,確定最重要、最常使用的塊裝置資料在操作時可直接從主記憶體擷取,而無需從低速裝置讀取。實體記憶體還用于存儲從快裝置讀取的資料,使得随後對該資料的通路可直接在實體記憶體進行,而無需從外部裝置再次取用。考慮系統中多種因素然後延遲寫回在總體上改進了系統的性能。前面分析的部分,例如記憶體管理的slab緩存是一個記憶體到記憶體的緩存,其目地不是加速對低速裝置的操作,而是對現有資源進行更簡單、更高效的使用。檔案系統的Dentry緩存也用于減少對低速塊裝置的通路,但他無法推廣到通用場合,因為他是專門用于處理單一資料類型的。

核心為塊裝置提供了兩種通用的緩存方案:

1) 頁緩存,針對以頁為機關的所有操作,并考慮了特定體系結構上的頁長度。一個主要的例子是記憶體映射技術。因為其他類型的檔案通路也是基于核心中的這一技術實作的。是以頁緩存實際上負責了塊裝置的大部分緩存工作。

2) 塊緩存,以塊為操作機關。在進行I/O操作時,存取的機關是裝置的各個塊,而不是整個記憶體頁。盡管頁長度對所有檔案系統都是相同的,但塊長度取決于特定的檔案系統或其設定。因而,塊緩存必須能夠處理不同長度的塊。

       目前用于塊傳輸的标準資料結構已經演變為struct bio。用這種方式進行塊傳輸更為高效,因為他可以合并同一請求中後續的塊,加速處理的進行。在許多場合下,頁緩存和塊緩存是聯合使用的。例如,一個緩存的頁在寫操作期間可以劃分為不同的緩沖區,這樣可以在更細的力度下,識别出頁被修改的部分。好處在于,在将資料寫回時,隻需要回寫被修改的部分,無需将這個頁面傳輸回底層的塊裝置。

頁面緩存結構

[cpp]  view plain copy print ?

  1. struct address_space {  
  2.     struct inode        *host;        
  3.     struct radix_tree_root  page_tree;    
  4.     spinlock_t      tree_lock;    
  5.     unsigned int        i_mmap_writable;  
  6.     struct prio_tree_root   i_mmap;       
  7.     struct list_head    i_mmap_nonlinear;  
  8.     spinlock_t      i_mmap_lock;      
  9.     unsigned int        truncate_count;   
  10.     unsigned long       nrpages;      
  11.     pgoff_t         writeback_index;  
  12.     const struct address_space_operations *a_ops;     
  13.     unsigned long       flags;        
  14.     struct backing_dev_info *backing_dev_info;   
  15.     spinlock_t      private_lock;     
  16.     struct list_head    private_list;     
  17.     struct address_space    *assoc_mapping;   
  18. } __attribute__((aligned(sizeof(long))));  

後備存儲資訊

[cpp]  view plain copy print ?

  1. struct backing_dev_info {  
  2.     struct list_head bdi_list;  
  3.     struct rcu_head rcu_head;  
  4.     unsigned long ra_pages;   
  5.     unsigned long state;      
  6.     unsigned int capabilities;   
  7.     congested_fn *congested_fn;   
  8.     void *congested_data;     
  9.     void (*unplug_io_fn)(struct backing_dev_info *, struct page *);  
  10.     void *unplug_io_data;  
  11.     char *name;  
  12.     struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];  
  13.     struct prop_local_percpu completions;  
  14.     int dirty_exceeded;  
  15.     unsigned int min_ratio;  
  16.     unsigned int max_ratio, max_prop_frac;  
  17.     struct bdi_writeback wb;    
  18.     spinlock_t wb_lock;     
  19.     struct list_head wb_list;   
  20.     unsigned long wb_mask;      
  21.     unsigned int wb_cnt;        
  22.     struct list_head work_list;  
  23.     struct device *dev;  
  24. #ifdef CONFIG_DEBUG_FS  
  25.     struct dentry *debug_dir;  
  26.     struct dentry *debug_stats;  
  27. #endif  
  28. };  

下圖為位址空間與核心其他部分的關聯。

Linux緩存機制之頁緩存

核心采用一種通用的位址空間方案,來建立緩存資料與其來源之間的關聯。

1)  記憶體中的頁配置設定到每個位址空間。這些頁的内容可以由使用者程序或核心本身使用各式各樣的方法操作。這些資料表示了緩存中的内容;

2)  後備存儲器struct backing_dev_info指定了填充位址空間中頁的資料的來源。位址空間關聯到處理器的虛拟位址空間,是由處理器在虛拟記憶體中管理的一個區域到裝置device上對應位置之間的一個映射。

如果通路了虛拟記憶體中的某個位置,該位置沒有關聯到實體記憶體頁,核心可根據位址空間結構來找到讀取資料的來源。

為支援資料傳輸,每個位址空間都提供了一組操作,以容許位址空間所涉及雙方面的互動。

位址空間是核心中最關鍵的資料結構之一,對該資料結構的管理,已經演變為核心面對的最關鍵的問題之一。 頁緩存的任務在于,獲得一些實體記憶體頁,以加速在塊裝置上按頁為機關執行的操作。

核心使用了基數樹來管理與一個位址空間相關的所有頁,以便盡可能降低開銷。對于基數樹的了解在這裡就不分析了,後面有空的時候再做分析。

 位址空間操作

[cpp]  view plain copy print ?

  1. struct address_space_operations {  
  2.     int (*writepage)(struct page *page, struct writeback_control *wbc);  
  3.     int (*readpage)(struct file *, struct page *);  
  4.     void (*sync_page)(struct page *);  
  5.     int (*writepages)(struct address_space *, struct writeback_control *);  
  6.     int (*set_page_dirty)(struct page *page);  
  7.     int (*readpages)(struct file *filp, struct address_space *mapping,  
  8.             struct list_head *pages, unsigned nr_pages);  
  9.     int (*write_begin)(struct file *, struct address_space *mapping,  
  10.                 loff_t pos, unsigned len, unsigned flags,  
  11.                 struct page **pagep, void **fsdata);  
  12.     int (*write_end)(struct file *, struct address_space *mapping,  
  13.                 loff_t pos, unsigned len, unsigned copied,  
  14.                 struct page *page, void *fsdata);  
  15.     sector_t (*bmap)(struct address_space *, sector_t);  
  16.     void (*invalidatepage) (struct page *, unsigned long);  
  17.     int (*releasepage) (struct page *, gfp_t);  
  18.     ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,  
  19.             loff_t offset, unsigned long nr_segs);  
  20.     int (*get_xip_mem)(struct address_space *, pgoff_t, int,  
  21.                         void **, unsigned long *);  
  22.     int (*migratepage) (struct address_space *,  
  23.             struct page *, struct page *);  
  24.     int (*launder_page) (struct page *);  
  25.     int (*is_partially_uptodate) (struct page *, read_descriptor_t *,  
  26.                     unsigned long);  
  27.     int (*error_remove_page)(struct address_space *, struct page *);  
  28. };  

頁面緩存的實作基于基數樹,緩存屬于核心中性能要求最苛刻的部分之一,而且廣泛用于核心的所有子系統,實作也比較簡單。舉兩個例子,其他的暫時不做分析了。

配置設定頁面用于加入位址空間

[cpp]  view plain copy print ?

  1. static inline struct page *page_cache_alloc(struct address_space *x)  
  2. {  
  3.     return __page_cache_alloc(mapping_gfp_mask(x));  
  4. }  

配置設定完了添加到基數樹中

[cpp]  view plain copy print ?

  1. static inline int add_to_page_cache(struct page *page,  
  2.         struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask)  
  3. {  
  4.     int error;  
  5.     __set_page_locked(page);  
  6.     error = add_to_page_cache_locked(page, mapping, offset, gfp_mask);  
  7.     if (unlikely(error))  
  8.         __clear_page_locked(page);  
  9.     return error;  
  10. }  

[cpp]  view plain copy print ?

  1. int add_to_page_cache_locked(struct page *page, struct address_space *mapping,  
  2.         pgoff_t offset, gfp_t gfp_mask)  
  3. {  
  4.     int error;  
  5.     VM_BUG_ON(!PageLocked(page));  
  6.     error = mem_cgroup_cache_charge(page, current->mm,  
  7.                     gfp_mask & GFP_RECLAIM_MASK);  
  8.     if (error)  
  9.         goto out;  
  10.     error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);  
  11.     if (error == 0) {  
  12.         page_cache_get(page);  
  13.         page->mapping = mapping;  
  14.         page->index = offset;  
  15.         spin_lock_irq(&mapping->tree_lock);  
  16.         error = radix_tree_insert(&mapping->page_tree, offset, page);  
  17.         if (likely(!error)) {  
  18.             mapping->nrpages++;  
  19.             __inc_zone_page_state(page, NR_FILE_PAGES);  
  20.             if (PageSwapBacked(page))  
  21.                 __inc_zone_page_state(page, NR_SHMEM);  
  22.             spin_unlock_irq(&mapping->tree_lock);  
  23.         } else {  
  24.             page->mapping = NULL;  
  25.             spin_unlock_irq(&mapping->tree_lock);  
  26.             mem_cgroup_uncharge_cache_page(page);  
  27.             page_cache_release(page);  
  28.         }  
  29.         radix_tree_preload_end();  
  30.     } else  
  31.         mem_cgroup_uncharge_cache_page(page);  
  32. out:  
  33.     return error;  
  34. }  

來源:

        Linux運用一個功能廣泛的緩沖和緩存架構來提高系統的速度。緩沖和緩存利用一部分系統實體記憶體,確定最重要、最常使用的塊裝置資料在操作時可直接從主記憶體擷取,而無需從低速裝置讀取。實體記憶體還用于存儲從快裝置讀取的資料,使得随後對該資料的通路可直接在實體記憶體進行,而無需從外部裝置再次取用。考慮系統中多種因素然後延遲寫回在總體上改進了系統的性能。前面分析的部分,例如記憶體管理的slab緩存是一個記憶體到記憶體的緩存,其目地不是加速對低速裝置的操作,而是對現有資源進行更簡單、更高效的使用。檔案系統的Dentry緩存也用于減少對低速塊裝置的通路,但他無法推廣到通用場合,因為他是專門用于處理單一資料類型的。

核心為塊裝置提供了兩種通用的緩存方案:

1) 頁緩存,針對以頁為機關的所有操作,并考慮了特定體系結構上的頁長度。一個主要的例子是記憶體映射技術。因為其他類型的檔案通路也是基于核心中的這一技術實作的。是以頁緩存實際上負責了塊裝置的大部分緩存工作。

2) 塊緩存,以塊為操作機關。在進行I/O操作時,存取的機關是裝置的各個塊,而不是整個記憶體頁。盡管頁長度對所有檔案系統都是相同的,但塊長度取決于特定的檔案系統或其設定。因而,塊緩存必須能夠處理不同長度的塊。

       目前用于塊傳輸的标準資料結構已經演變為struct bio。用這種方式進行塊傳輸更為高效,因為他可以合并同一請求中後續的塊,加速處理的進行。在許多場合下,頁緩存和塊緩存是聯合使用的。例如,一個緩存的頁在寫操作期間可以劃分為不同的緩沖區,這樣可以在更細的力度下,識别出頁被修改的部分。好處在于,在将資料寫回時,隻需要回寫被修改的部分,無需将這個頁面傳輸回底層的塊裝置。

頁面緩存結構

[cpp]  view plain copy print ?

  1. struct address_space {  
  2.     struct inode        *host;        
  3.     struct radix_tree_root  page_tree;    
  4.     spinlock_t      tree_lock;    
  5.     unsigned int        i_mmap_writable;  
  6.     struct prio_tree_root   i_mmap;       
  7.     struct list_head    i_mmap_nonlinear;  
  8.     spinlock_t      i_mmap_lock;      
  9.     unsigned int        truncate_count;   
  10.     unsigned long       nrpages;      
  11.     pgoff_t         writeback_index;  
  12.     const struct address_space_operations *a_ops;     
  13.     unsigned long       flags;        
  14.     struct backing_dev_info *backing_dev_info;   
  15.     spinlock_t      private_lock;     
  16.     struct list_head    private_list;     
  17.     struct address_space    *assoc_mapping;   
  18. } __attribute__((aligned(sizeof(long))));  

後備存儲資訊

[cpp]  view plain copy print ?

  1. struct backing_dev_info {  
  2.     struct list_head bdi_list;  
  3.     struct rcu_head rcu_head;  
  4.     unsigned long ra_pages;   
  5.     unsigned long state;      
  6.     unsigned int capabilities;   
  7.     congested_fn *congested_fn;   
  8.     void *congested_data;     
  9.     void (*unplug_io_fn)(struct backing_dev_info *, struct page *);  
  10.     void *unplug_io_data;  
  11.     char *name;  
  12.     struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];  
  13.     struct prop_local_percpu completions;  
  14.     int dirty_exceeded;  
  15.     unsigned int min_ratio;  
  16.     unsigned int max_ratio, max_prop_frac;  
  17.     struct bdi_writeback wb;    
  18.     spinlock_t wb_lock;     
  19.     struct list_head wb_list;   
  20.     unsigned long wb_mask;      
  21.     unsigned int wb_cnt;        
  22.     struct list_head work_list;  
  23.     struct device *dev;  
  24. #ifdef CONFIG_DEBUG_FS  
  25.     struct dentry *debug_dir;  
  26.     struct dentry *debug_stats;  
  27. #endif  
  28. };  

下圖為位址空間與核心其他部分的關聯。

Linux緩存機制之頁緩存

核心采用一種通用的位址空間方案,來建立緩存資料與其來源之間的關聯。

1)  記憶體中的頁配置設定到每個位址空間。這些頁的内容可以由使用者程序或核心本身使用各式各樣的方法操作。這些資料表示了緩存中的内容;

2)  後備存儲器struct backing_dev_info指定了填充位址空間中頁的資料的來源。位址空間關聯到處理器的虛拟位址空間,是由處理器在虛拟記憶體中管理的一個區域到裝置device上對應位置之間的一個映射。

如果通路了虛拟記憶體中的某個位置,該位置沒有關聯到實體記憶體頁,核心可根據位址空間結構來找到讀取資料的來源。

為支援資料傳輸,每個位址空間都提供了一組操作,以容許位址空間所涉及雙方面的互動。

位址空間是核心中最關鍵的資料結構之一,對該資料結構的管理,已經演變為核心面對的最關鍵的問題之一。 頁緩存的任務在于,獲得一些實體記憶體頁,以加速在塊裝置上按頁為機關執行的操作。

核心使用了基數樹來管理與一個位址空間相關的所有頁,以便盡可能降低開銷。對于基數樹的了解在這裡就不分析了,後面有空的時候再做分析。

 位址空間操作

[cpp]  view plain copy print ?

  1. struct address_space_operations {  
  2.     int (*writepage)(struct page *page, struct writeback_control *wbc);  
  3.     int (*readpage)(struct file *, struct page *);  
  4.     void (*sync_page)(struct page *);  
  5.     int (*writepages)(struct address_space *, struct writeback_control *);  
  6.     int (*set_page_dirty)(struct page *page);  
  7.     int (*readpages)(struct file *filp, struct address_space *mapping,  
  8.             struct list_head *pages, unsigned nr_pages);  
  9.     int (*write_begin)(struct file *, struct address_space *mapping,  
  10.                 loff_t pos, unsigned len, unsigned flags,  
  11.                 struct page **pagep, void **fsdata);  
  12.     int (*write_end)(struct file *, struct address_space *mapping,  
  13.                 loff_t pos, unsigned len, unsigned copied,  
  14.                 struct page *page, void *fsdata);  
  15.     sector_t (*bmap)(struct address_space *, sector_t);  
  16.     void (*invalidatepage) (struct page *, unsigned long);  
  17.     int (*releasepage) (struct page *, gfp_t);  
  18.     ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,  
  19.             loff_t offset, unsigned long nr_segs);  
  20.     int (*get_xip_mem)(struct address_space *, pgoff_t, int,  
  21.                         void **, unsigned long *);  
  22.     int (*migratepage) (struct address_space *,  
  23.             struct page *, struct page *);  
  24.     int (*launder_page) (struct page *);  
  25.     int (*is_partially_uptodate) (struct page *, read_descriptor_t *,  
  26.                     unsigned long);  
  27.     int (*error_remove_page)(struct address_space *, struct page *);  
  28. };  

頁面緩存的實作基于基數樹,緩存屬于核心中性能要求最苛刻的部分之一,而且廣泛用于核心的所有子系統,實作也比較簡單。舉兩個例子,其他的暫時不做分析了。

配置設定頁面用于加入位址空間

[cpp]  view plain copy print ?

  1. static inline struct page *page_cache_alloc(struct address_space *x)  
  2. {  
  3.     return __page_cache_alloc(mapping_gfp_mask(x));  
  4. }  

配置設定完了添加到基數樹中

[cpp]  view plain copy print ?

  1. static inline int add_to_page_cache(struct page *page,  
  2.         struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask)  
  3. {  
  4.     int error;  
  5.     __set_page_locked(page);  
  6.     error = add_to_page_cache_locked(page, mapping, offset, gfp_mask);  
  7.     if (unlikely(error))  
  8.         __clear_page_locked(page);  
  9.     return error;  
  10. }  

[cpp]  view plain copy print ?

  1. int add_to_page_cache_locked(struct page *page, struct address_space *mapping,  
  2.         pgoff_t offset, gfp_t gfp_mask)  
  3. {  
  4.     int error;  
  5.     VM_BUG_ON(!PageLocked(page));  
  6.     error = mem_cgroup_cache_charge(page, current->mm,  
  7.                     gfp_mask & GFP_RECLAIM_MASK);  
  8.     if (error)  
  9.         goto out;  
  10.     error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);  
  11.     if (error == 0) {  
  12.         page_cache_get(page);  
  13.         page->mapping = mapping;  
  14.         page->index = offset;  
  15.         spin_lock_irq(&mapping->tree_lock);  
  16.         error = radix_tree_insert(&mapping->page_tree, offset, page);  
  17.         if (likely(!error)) {  
  18.             mapping->nrpages++;  
  19.             __inc_zone_page_state(page, NR_FILE_PAGES);  
  20.             if (PageSwapBacked(page))  
  21.                 __inc_zone_page_state(page, NR_SHMEM);  
  22.             spin_unlock_irq(&mapping->tree_lock);  
  23.         } else {  
  24.             page->mapping = NULL;  
  25.             spin_unlock_irq(&mapping->tree_lock);  
  26.             mem_cgroup_uncharge_cache_page(page);  
  27.             page_cache_release(page);  
  28.         }  
  29.         radix_tree_preload_end();  
  30.     } else  
  31.         mem_cgroup_uncharge_cache_page(page);  
  32. out:  
  33.     return error;  
  34. }  

來源:http://blog.csdn.net/bullbat/article/details/7296988

        Linux運用一個功能廣泛的緩沖和緩存架構來提高系統的速度。緩沖和緩存利用一部分系統實體記憶體,確定最重要、最常使用的塊裝置資料在操作時可直接從主記憶體擷取,而無需從低速裝置讀取。實體記憶體還用于存儲從快裝置讀取的資料,使得随後對該資料的通路可直接在實體記憶體進行,而無需從外部裝置再次取用。考慮系統中多種因素然後延遲寫回在總體上改進了系統的性能。前面分析的部分,例如記憶體管理的slab緩存是一個記憶體到記憶體的緩存,其目地不是加速對低速裝置的操作,而是對現有資源進行更簡單、更高效的使用。檔案系統的Dentry緩存也用于減少對低速塊裝置的通路,但他無法推廣到通用場合,因為他是專門用于處理單一資料類型的。

核心為塊裝置提供了兩種通用的緩存方案:

1) 頁緩存,針對以頁為機關的所有操作,并考慮了特定體系結構上的頁長度。一個主要的例子是記憶體映射技術。因為其他類型的檔案通路也是基于核心中的這一技術實作的。是以頁緩存實際上負責了塊裝置的大部分緩存工作。

2) 塊緩存,以塊為操作機關。在進行I/O操作時,存取的機關是裝置的各個塊,而不是整個記憶體頁。盡管頁長度對所有檔案系統都是相同的,但塊長度取決于特定的檔案系統或其設定。因而,塊緩存必須能夠處理不同長度的塊。

       目前用于塊傳輸的标準資料結構已經演變為struct bio。用這種方式進行塊傳輸更為高效,因為他可以合并同一請求中後續的塊,加速處理的進行。在許多場合下,頁緩存和塊緩存是聯合使用的。例如,一個緩存的頁在寫操作期間可以劃分為不同的緩沖區,這樣可以在更細的力度下,識别出頁被修改的部分。好處在于,在将資料寫回時,隻需要回寫被修改的部分,無需将這個頁面傳輸回底層的塊裝置。

頁面緩存結構

[cpp]  view plain copy print ?

  1. struct address_space {  
  2.     struct inode        *host;        
  3.     struct radix_tree_root  page_tree;    
  4.     spinlock_t      tree_lock;    
  5.     unsigned int        i_mmap_writable;  
  6.     struct prio_tree_root   i_mmap;       
  7.     struct list_head    i_mmap_nonlinear;  
  8.     spinlock_t      i_mmap_lock;      
  9.     unsigned int        truncate_count;   
  10.     unsigned long       nrpages;      
  11.     pgoff_t         writeback_index;  
  12.     const struct address_space_operations *a_ops;     
  13.     unsigned long       flags;        
  14.     struct backing_dev_info *backing_dev_info;   
  15.     spinlock_t      private_lock;     
  16.     struct list_head    private_list;     
  17.     struct address_space    *assoc_mapping;   
  18. } __attribute__((aligned(sizeof(long))));  

後備存儲資訊

[cpp]  view plain copy print ?

  1. struct backing_dev_info {  
  2.     struct list_head bdi_list;  
  3.     struct rcu_head rcu_head;  
  4.     unsigned long ra_pages;   
  5.     unsigned long state;      
  6.     unsigned int capabilities;   
  7.     congested_fn *congested_fn;   
  8.     void *congested_data;     
  9.     void (*unplug_io_fn)(struct backing_dev_info *, struct page *);  
  10.     void *unplug_io_data;  
  11.     char *name;  
  12.     struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];  
  13.     struct prop_local_percpu completions;  
  14.     int dirty_exceeded;  
  15.     unsigned int min_ratio;  
  16.     unsigned int max_ratio, max_prop_frac;  
  17.     struct bdi_writeback wb;    
  18.     spinlock_t wb_lock;     
  19.     struct list_head wb_list;   
  20.     unsigned long wb_mask;      
  21.     unsigned int wb_cnt;        
  22.     struct list_head work_list;  
  23.     struct device *dev;  
  24. #ifdef CONFIG_DEBUG_FS  
  25.     struct dentry *debug_dir;  
  26.     struct dentry *debug_stats;  
  27. #endif  
  28. };  

下圖為位址空間與核心其他部分的關聯。

Linux緩存機制之頁緩存

核心采用一種通用的位址空間方案,來建立緩存資料與其來源之間的關聯。

1)  記憶體中的頁配置設定到每個位址空間。這些頁的内容可以由使用者程序或核心本身使用各式各樣的方法操作。這些資料表示了緩存中的内容;

2)  後備存儲器struct backing_dev_info指定了填充位址空間中頁的資料的來源。位址空間關聯到處理器的虛拟位址空間,是由處理器在虛拟記憶體中管理的一個區域到裝置device上對應位置之間的一個映射。

如果通路了虛拟記憶體中的某個位置,該位置沒有關聯到實體記憶體頁,核心可根據位址空間結構來找到讀取資料的來源。

為支援資料傳輸,每個位址空間都提供了一組操作,以容許位址空間所涉及雙方面的互動。

位址空間是核心中最關鍵的資料結構之一,對該資料結構的管理,已經演變為核心面對的最關鍵的問題之一。 頁緩存的任務在于,獲得一些實體記憶體頁,以加速在塊裝置上按頁為機關執行的操作。

核心使用了基數樹來管理與一個位址空間相關的所有頁,以便盡可能降低開銷。對于基數樹的了解在這裡就不分析了,後面有空的時候再做分析。

 位址空間操作

[cpp]  view plain copy print ?

  1. struct address_space_operations {  
  2.     int (*writepage)(struct page *page, struct writeback_control *wbc);  
  3.     int (*readpage)(struct file *, struct page *);  
  4.     void (*sync_page)(struct page *);  
  5.     int (*writepages)(struct address_space *, struct writeback_control *);  
  6.     int (*set_page_dirty)(struct page *page);  
  7.     int (*readpages)(struct file *filp, struct address_space *mapping,  
  8.             struct list_head *pages, unsigned nr_pages);  
  9.     int (*write_begin)(struct file *, struct address_space *mapping,  
  10.                 loff_t pos, unsigned len, unsigned flags,  
  11.                 struct page **pagep, void **fsdata);  
  12.     int (*write_end)(struct file *, struct address_space *mapping,  
  13.                 loff_t pos, unsigned len, unsigned copied,  
  14.                 struct page *page, void *fsdata);  
  15.     sector_t (*bmap)(struct address_space *, sector_t);  
  16.     void (*invalidatepage) (struct page *, unsigned long);  
  17.     int (*releasepage) (struct page *, gfp_t);  
  18.     ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,  
  19.             loff_t offset, unsigned long nr_segs);  
  20.     int (*get_xip_mem)(struct address_space *, pgoff_t, int,  
  21.                         void **, unsigned long *);  
  22.     int (*migratepage) (struct address_space *,  
  23.             struct page *, struct page *);  
  24.     int (*launder_page) (struct page *);  
  25.     int (*is_partially_uptodate) (struct page *, read_descriptor_t *,  
  26.                     unsigned long);  
  27.     int (*error_remove_page)(struct address_space *, struct page *);  
  28. };  

頁面緩存的實作基于基數樹,緩存屬于核心中性能要求最苛刻的部分之一,而且廣泛用于核心的所有子系統,實作也比較簡單。舉兩個例子,其他的暫時不做分析了。

配置設定頁面用于加入位址空間

[cpp]  view plain copy print ?

  1. static inline struct page *page_cache_alloc(struct address_space *x)  
  2. {  
  3.     return __page_cache_alloc(mapping_gfp_mask(x));  
  4. }  

配置設定完了添加到基數樹中

[cpp]  view plain copy print ?

  1. static inline int add_to_page_cache(struct page *page,  
  2.         struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask)  
  3. {  
  4.     int error;  
  5.     __set_page_locked(page);  
  6.     error = add_to_page_cache_locked(page, mapping, offset, gfp_mask);  
  7.     if (unlikely(error))  
  8.         __clear_page_locked(page);  
  9.     return error;  
  10. }  

[cpp]  view plain copy print ?

  1. int add_to_page_cache_locked(struct page *page, struct address_space *mapping,  
  2.         pgoff_t offset, gfp_t gfp_mask)  
  3. {  
  4.     int error;  
  5.     VM_BUG_ON(!PageLocked(page));  
  6.     error = mem_cgroup_cache_charge(page, current->mm,  
  7.                     gfp_mask & GFP_RECLAIM_MASK);  
  8.     if (error)  
  9.         goto out;  
  10.     error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);  
  11.     if (error == 0) {  
  12.         page_cache_get(page);  
  13.         page->mapping = mapping;  
  14.         page->index = offset;  
  15.         spin_lock_irq(&mapping->tree_lock);  
  16.         error = radix_tree_insert(&mapping->page_tree, offset, page);  
  17.         if (likely(!error)) {  
  18.             mapping->nrpages++;  
  19.             __inc_zone_page_state(page, NR_FILE_PAGES);  
  20.             if (PageSwapBacked(page))  
  21.                 __inc_zone_page_state(page, NR_SHMEM);  
  22.             spin_unlock_irq(&mapping->tree_lock);  
  23.         } else {  
  24.             page->mapping = NULL;  
  25.             spin_unlock_irq(&mapping->tree_lock);  
  26.             mem_cgroup_uncharge_cache_page(page);  
  27.             page_cache_release(page);  
  28.         }  
  29.         radix_tree_preload_end();  
  30.     } else  
  31.         mem_cgroup_uncharge_cache_page(page);  
  32. out:  
  33.     return error;  
  34. }  

繼續閱讀