天天看點

linux 檔案IO與記憶體映射:page cache頁高速緩存

文章目錄

  • ​​描述​​
  • ​​原理​​
  • ​​作用​​
  • ​​測試​​
  • ​​實作​​
  • ​​緩存控制​​

描述

頁高速緩存(page cache)是從實際實體記憶體中開辟出來一部分記憶體空間,用作作業系統的磁盤讀寫緩存。比如用戶端寫入的資料并不直接寫入磁盤,而是寫入到這一段實體記憶體中即代表已經寫完,這樣由記憶體本身的高速讀寫性能是能夠提升系統整體io性能。

基本管理機關:頁

原理

頁高速緩存的淘汰原理是根據:時間局部原理,空間局部原理;即最長時間未被通路(時間局部性)或者被程序引用次數最少(空間局部性)的頁面最先被淘汰

關于頁高速緩存存在作業系統的哪個層次可以參考如下圖:

linux 檔案IO與記憶體映射:page cache頁高速緩存

作用

我們使用者使用系統調用讀寫(read/write)時,頁高速緩存的基本工作原理如下圖:

linux 檔案IO與記憶體映射:page cache頁高速緩存

即寫請求先寫入page cache,再由page cache落盤

讀請求同樣先從page cache 中讀,入無法讀到,則由page cache從磁盤讀出

落盤方式:

寫請求在資料寫入到page cahe中後會直接傳回用戶端寫入完畢,但是資料本身并未完全寫入到磁盤,而是等到page cache達到作業系統重新整理緩存的比例之後才會将緩存中經過淘汰算法計算的頁髒資料同步到磁盤。

測試

指令測試

dd if=/dev/zero of=./test.dat bs=1M count=10 #向檔案中寫入資料
cat /proc/meminfo |grep Dirty  #檢視記憶體髒頁情況
sync #将記憶體中的髒頁同步到磁盤
cat /proc/meminfo |grep Dirty #再次檢視記憶體髒也情況      

代碼測試

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>

#define NAME "testfile"

int main()
{
  char buf[100];
  memset(buf,0,100);

  //帶有SYNC标記得打開檔案
  int fd=open(NAME,O_CREAT|O_RDWR|O_SYNC, 0666);
  
  fgets(buf,100,stdin);
  int result = write(fd,buf,sizeof(buf));
  if ( -1 == result) {
    printf("write failed\n");
    _exit(-1);
  }
  printf("write size is %d\n",result);
  fsync(fd);
  printf("sync the page cache\n");
  return 0;
}      

實作

每當核心開始執行一個頁IO操作時,就先到高速緩存中找。這樣就可以大大減少磁盤操作。

頁高速緩存的實作中起主要作用的一個對象為​​

​address_space​

​​資料結構

​​

​3.10.0-957.5.1.el7.x86_64/include/linux/fs.h​

struct address_space {
    /*通常情況下,會與一個索引節點(inode)關聯,這時host域就會指向該索引節點
    如果關聯對象不是一個索引節點的話,比如address_space和swapper關聯時,這時host域會被置為NULL*/
        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 */
    
    /*i_mmap字段是一個優先搜尋樹,它的搜尋範圍包含了在address_sapce中私有的和共享的頁面*/
        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 */
    
    /*nrpages反應了address_space空間的大小*/
        unsigned long           nrpages;            /* total number of pages */
        pgoff_t                 writeback_index;    /* writeback start offset */

    /*a_ops域指向位址空間對象中的操作函數表*/
        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 */
};      

操作函數清單​

​address_space_operations​

​​定義如下,其中主要的為​

​writepage​

​​和​

​readpage​

  • readpage根據file即address_spaces的mapping嘗試從page cache中讀緩存頁,如果搜尋頁不在搜尋樹(基數樹radix tree)管理的緩存頁面中,則核心會重新建立一個緩存也加入到搜尋樹中。
  • writepage 則根據目前記憶體頁page是否有回寫标記,即是否為髒頁;如果是則将目前記憶體頁的資料寫入到磁盤,并從搜素樹中删除該頁 ​

    ​mm/filemap.c​

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);
};      

page cache的頁高速緩存管理如下:

linux 檔案IO與記憶體映射:page cache頁高速緩存

根據上圖我們知道

  • 讀檔案流程如下
  1. 資料結構關聯:​

    ​inode -> i_mapping​

    ​​ 指向​

    ​address_space​

    ​​對象,​

    ​address_space->host​

    ​​指向​

    ​inode​

  2. 資料結構關聯:​

    ​page->mapping​

    ​​ 指向頁緩存​

    ​owner​

    ​​的​

    ​address_space​

  3. 系統調用傳參:檔案描述符+檔案偏移位址
  4. 作業系統找到檔案​

    ​address_space​

    ​​,根據偏移量到頁緩存中查找​

    ​page​

  5. 若查找到,傳回資料到使用者空間
  6. 否則,核心建立一個​

    ​page​

    ​并加入到頁緩存,資料從磁盤載入該項
  7. 調用​

    ​readpage​

    ​方法将資料傳回給使用者空間
  • 寫檔案流程如下:
  1. 資料結構關聯:​

    ​inode -> i_mapping​

    ​​ 指向​

    ​address_space​

    ​​對象,​

    ​address_space->host​

    ​​指向​

    ​inode​

  2. 資料結構關聯:​

    ​page->mapping​

    ​​ 指向頁緩存​

    ​owner​

    ​​的​

    ​address_space​

  3. 系統調用傳參:檔案描述符+檔案偏移位址
  4. 作業系統找到檔案​

    ​address_space​

    ​​,根據偏移量到頁緩存中查找​

    ​page​

  5. 若查找到,将資料寫入到該緩存中,該頁成為髒頁
  6. 若沒有查找到,向緩存的計數樹管理的頁面中添加一個新的頁面,并将使用者空間的資料寫入到該頁面
  7. 當資料滿足頁緩存的時間或空間原理時,使用pdflush背景回寫例程來将髒頁資料會寫到磁盤

​pdfush​

​​的實作如下:

pdflush線程在系統中的空閑記憶體低于一個特定的門檻值時,将髒頁重新整理回磁盤;

該背景回寫例程的目的在于在可用實體記憶體過低時,釋放髒頁以重新獲得記憶體

緩存控制

  • 擷取參數配置 ​

    ​sysctl -a | grep dirty​

  • 設定參數

    修改配置檔案​​

    ​/etc/sysctl.conf​

    ​​,增加參數

    ​​

    ​vm.dirty_background_ratio = 5​

    ​​

    ​vm.dirty_ratio = 10​

    ​​ 執行​

    ​sysctl -p​

    ​即可生效

繼續閱讀