Linux內核設計與實現---頁高速緩存和頁回寫

頁高速緩存和頁回寫

  • 1 頁高速緩存
  • 2 基樹
  • 3 緩沖區高速緩存
  • 4 pdflush后臺例程
    • 膝上型電腦模式
    • bdflush和kupdated
    • 避免擁塞的方法:使用多線程

頁高速緩存(cache)是Linux內核實現的一種主要磁盤緩存,通過把磁盤中的數據緩存到物理內存中,把對磁盤的訪問變成對物理內存的訪問,主要用來減少對磁盤的I/O操作。它由RAM中的物理頁組成,緩存中每一項對應著磁盤中的多個塊,每當內核開始執行一個頁I/O操作時,首先會檢查需要的數據是否在高速緩存中,如果在,那么內核就直接使用高速緩存中的數據,從而避免訪問磁盤。

高速緩存的價值在于兩個方面:第一,訪問磁盤的速度要遠遠低于訪問內存的速度,因為,在內存訪問數據比從磁盤訪問速度更快。第二,數據一旦被訪問,就很有可能在短期內再次被訪問到。如果在第一次訪問數據時緩存它,那就極有可能在短期內再次被高速緩存命中。

1 頁高速緩存

頁高速緩存緩存的是頁。Linux頁高速緩存的目標是緩存任何基于頁的對象,這包含各種類型的文件和各種類型的內存映射。為了滿足普遍性的要求,Linux頁高速緩存使用address_space結構體描述頁高速緩存中的頁面。該結構體定義在文件linux/fs.h中。

struct address_space {struct inode		*host;		/* owner: inode, block_device */struct radix_tree_root	page_tree;	/* radix tree of all pages */spinlock_t		tree_lock;	/* and spinlock protecting it */unsigned int		i_mmap_writable;/* count VM_SHARED mappings */struct prio_tree_root	i_mmap;		/* tree of private and shared mappings */struct list_head	i_mmap_nonlinear;/*list VM_NONLINEAR mappings */spinlock_t		i_mmap_lock;	/* protect tree, count, list */atomic_t		truncate_count;	/* Cover race condition with truncate */unsigned long		nrpages;	/* number of total pages */pgoff_t			writeback_index;/* writeback starts here */struct address_space_operations *a_ops;	/* methods */unsigned long		flags;		/* error bits/gfp mask */struct backing_dev_info *backing_dev_info; /* device readahead, etc */spinlock_t		private_lock;	/* for use by the address_space */struct list_head	private_list;	/* ditto */struct address_space	*assoc_mapping;	/* ditto */
} __attribute__((aligned(sizeof(long))));

i_mmap字段是一個優先搜索樹,它的搜索范圍包含了在address_space中所有共享的與私有的映射頁面。優先搜索樹是一種將堆與radix樹結合的快速檢索樹。address space空間大小由nrpages字段描述,表示共有多少頁。
address_space結構往往會和某些內核對象關聯,通常情況下會與一個索引節點關聯,這時host域就會指向該索引節點,如果關聯對象不是一個索引節點的話,host就被設置為NULL。
a_ops域指向地址空間對象中的操作函數表,操作函數表定義在linux/fs.h文件中,由address_space_operations結構體表示:

struct address_space_operations {int (*writepage)(struct page *page, struct writeback_control *wbc);int (*readpage)(struct file *, struct page *);int (*sync_page)(struct page *);/* Write back some dirty pages from this mapping. */int (*writepages)(struct address_space *, struct writeback_control *);/* Set a page dirty */int (*set_page_dirty)(struct page *page);int (*readpages)(struct file *filp, struct address_space *mapping,struct list_head *pages, unsigned nr_pages);/** ext3 requires that a successful prepare_write() call be followed* by a commit_write() call - they must be balanced*/int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);int (*commit_write)(struct file *, struct page *, unsigned, unsigned);/* Unfortunately this kludge is needed for FIBMAP. Don't use it */sector_t (*bmap)(struct address_space *, sector_t);int (*invalidatepage) (struct page *, unsigned long);int (*releasepage) (struct page *, int);ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,loff_t offset, unsigned long nr_segs);
};

2 基樹

每個address_space對象都有唯一的基樹(radix tree),它保存在page_tree結構體中。基樹是一個二叉樹,只要指定了文件偏移量,就可以在基樹中迅速檢索到希望的數據,頁高速緩存的搜索函數find_get_page()要調用函數radix_tree_loopup(),該函數會在指定基樹中搜索指定頁面。

基樹核心代碼的通用形式可以在文件lib/radix-tree.c中找到,要想使用基樹,需要包含頭文件linux/radix_tree.h

3 緩沖區高速緩存

現在的Linux系統中已經不再有獨立的緩沖區高速緩存了。但在2.2版本的內核中,存在兩個獨立的磁盤緩存:頁高速緩存和緩沖區高速緩存。前者緩存頁,后者緩存緩沖。兩種緩存并不同一:一個磁盤塊可以在兩種緩存中同時存在,因此需要對緩存中的同一拷貝進行很麻煩的同步操作。
在2.4版本的內核開始,統一了這兩種緩存,現在Linux只有唯一的頁高速緩存。

4 pdflush后臺例程

由于頁高速緩存的緩存作用,寫操作實際上會被延遲,當頁高速緩存中的數據比磁盤存儲的數據更新時,這時候頁高速緩存中的數據被稱為臟數據,臟數據所在的頁被稱為臟頁,這些臟頁最終必須被寫回磁盤。在以下兩種情況發送時,臟頁被寫回磁盤:

  • 當空閑內存低于一個特定的閾值時,內核必須將臟頁寫回磁盤,以便釋放內存。
  • 當臟頁在內存中駐留時間超過一個特定的閾值時,內核必須將超時的臟頁寫回磁盤,以確保臟頁不會無限期地駐留在內存中。

上面兩種工作的目的完全不同。在老內核中,這是由兩個獨立的內核線程分別完成(bdflush和kupdated兩個線程)的,但是在2.6內核中,由一群內核線程,pdflush后臺回寫線程同一執行兩種工作。首先,pdflush線程在系統中的空閑內存低于一個特定的閾值時,將臟頁刷新回磁盤。此目的是在可用物理內存過低時,釋放臟頁以重新獲得內存。特定的內存閾值可以通過dirty_backgriud_radio sysctl系統調用設置。當空閑內存比閾值dirty_background_ratio低時,內核便會調用wakeup_bdflush()喚醒一個pdflush線程。隨后pdflush線程進一步調用函數background_writeout()開始將臟頁回寫磁盤。函數background_writeout()需要一個長整型參數,該參數指定試圖寫回的頁面數目。函數background_writeout會連續寫出數據,直到滿足以下兩個條件:

  • 已經有指定的最小數目的頁被寫出到磁盤
  • 空閑內存數已經回升,超過了閾值dirty_background_ratio

上述條件確保了pdflush操作可以減輕系統中內存不足的壓力,回寫操作不會在達到這兩個條件前停止,除非pdflush寫回了所有的臟頁,沒有剩下的臟頁可以寫回了。

為了滿足第二個目標,pdflush后臺例程會被周期性喚醒,將那些在內存駐留時間過長的臟頁寫出,確保內存中不會有長期存在的臟頁。在系統啟動時,內核初始化一個定時器,讓它周期地喚醒pdflush線程,隨后使其運行函數wb_kupdate()。該函數將把所有駐留時間超過百分之drity_expire_centisece秒的臟頁寫回。然后定時器將再次被初始化為百分之drity_expire_centisece秒后喚醒pdflush線程。

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

膝上型電腦模式

膝上型電腦模式是一種特殊的頁回寫策略,該策略主要目的是將磁盤轉動的機械行為最小化,允許磁盤盡可能長時間停滯,以此延長電池供電時間。該模式可通過/proc/sys/vm/laptop_mode文件進行配置,通常,該文件內容為0,膝上電腦模式關閉,如果需要啟動,則向配置文件寫入1.

bdflush和kupdated

在2.6內核版本前,pdflush線程的工作是分別由bdflush和kupdated兩個線程共同完成。當可用內存過低時,bdflush內核線程在后臺執行臟頁回寫操作,與pdflush一樣,它也有一組閾值參數,當系統中空閑內存消耗到特定內存閾值以下時,bdflush線程就被wakeup_bdflush函數喚醒。

bdflush和pdflush之間主要有兩個區別。第一個是系統中只有一個bdflush線程,而pdflush線程的數目可以動態改變;第二個是bdflush線程基于緩沖,它將臟緩沖寫回磁盤,pdflush基于頁,它將整個臟頁寫回磁盤。

因為只有在內存過低和緩沖數量過大時,bdflush才刷新緩沖,所以kupdate線程被引入,以便周期性地寫回臟頁。

bdflush和kupdate內核線程現在完全被pdflush線程取代了。

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

bdflush僅僅只有一個線程,因此很有可能在頁回寫任務很重時,造成阻塞,這是因為單一的線程很可能堵塞在某個設備的已阻塞請求隊列上,而其他設備的請求隊列卻沒法得到處理。

2.6內核通過使用多個pdflush線程來解決上述問題。每個線程可以相互獨立地將臟頁刷新回磁盤,而且不同的pdflush線程處理不同的設備隊列。

通過一個簡單的算法,pdflush線程的數目可以根據系統的運行時間進行調整,如果所有已存在的pdflush線程都已經持續工作1秒以上,內核就會創建一個新的pdflush線程。線程數量最多不能超過MAX_PDFLUSH_THREADS,默認值是8.如果一個pdflush線程睡眠超過1秒,內核就會終止該線程,線程的數量最少不得小于MIN_PDFLUSH_THREADS,默認值是2.pdflush線程數量取決于頁回寫的數量和阻塞情況,動態調整。

這種方式看起來很理想,但是如果每一個pdflush線程都掛起在同一個阻塞的隊列上會怎么樣?在這種情況下,多個pdflush線程的性能并不會比單個線程提高多少,反而會造成嚴重的內存浪費。為了克服這種負面影響,pdflush線程利用阻塞避免策略,它們會積極地試圖寫回那些不屬于阻塞隊列的頁面。這樣一來,pdflush通過分派回寫工作,阻止多個線程在同一個忙設備糾纏。所以pdflush線程很忙,此時會有一個新的pdflush線程被創建,它們才是真正的繁忙。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379612.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379612.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379612.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

EL技術

1&#xff0e;EL 表達式概述 EL&#xff08;Express Lanuage&#xff09;表達式可以嵌入在jsp頁面內部&#xff0c;減少jsp腳本的編寫&#xff0c;EL 出現的目的是要替代jsp頁面中腳本的編寫。 2&#xff0e;EL從域中取出數據(EL最重要的作用) jsp腳本&#xff1a;<%requ…

math.trunc_JavaScript中帶有示例的Math.trunc()方法

math.truncJavaScript | Math.trunc()方法 (JavaScript | Math.trunc() Method) Math.trunc() is a function in math library of JavaScript that is used to extract the integer part of the given floating-point number. It removes the decimal and all the digits after…

.NET程序員的書單

zz from sjtu bbs: http://bbs.sjtu.edu.cn/bbscon?boardDotNET&fileM.1126188158.A 發信人: luckySeven(lucky為這位mm默哀), 信區: DotNET 標 題: .NET程序員的書單 發信站: 飲水思源 (2005年09月08日22:02:45 星期四), 轉信 發信人: AtomAndBit (原子與比特), 信區: D…

SVN+AnkhSVN端配置

對于ankhSVN我想很多人不陌生&#xff0c;因為經常使用&#xff0c;但是我還是發現很多人并不怎么會配置&#xff0c;或者完全不知道其需要配置&#xff0c;如果不配置的話&#xff0c;當兩個人同時需要修改某個文件的時候就容易中彈了。SVN默認是不支持“鎖定-編輯-解鎖”的&a…

Linux內核設計與實現---模塊

模塊1 構建模塊放在內核源代碼樹中放在內核代碼外2 安裝模塊3 產生模塊依賴性4 載入模塊5 管理配置選項6 模塊參數7 導出符號表Linux內核是模塊化組成的&#xff0c;它允許內核在運行時動態地向其中插入或從中刪除代碼。 與開發的內核核心子系統不同&#xff0c;模塊開發更接近…

JSTL技術

1&#xff0e;JSTL概述 JSTL&#xff08;JSP Standard Tag Library)&#xff0c;JSP標準標簽庫&#xff0c;可以嵌入在jsp頁面中使用標簽的形式完成業務邏輯等功能。jstl出現的目的同el一樣也是要代替jsp頁面中的腳本代碼。JSTL標準標準標簽庫有5個子庫&#xff0c;但隨著發展…

asinh函數_JavaScript中帶有示例的Math.asinh()方法

asinh函數JavaScript | Math.asinh()方法 (JavaScript | Math.asinh() Method) Math.asinh() is a function in math library of JavaScript that is used to find the value of hyperbolic arc-sine of a number. Math.asinh()是JavaScript數學庫中的函數&#xff0c;用于查找…

使用PHP創建一個REST API(Create a REST API with PHP)

譯者前言&#xff1a; 首先這是一篇國外的英文文章&#xff0c;非常系統、詳盡的介紹了如何使用PHP創建REST API&#xff0c;國內這方面的資料非常非常的有限&#xff0c;而且基本沒有可操作性。這篇文章寫的非常好&#xff0c;只要對PHP稍有了解的程序員&#xff0c;看完本文基…

old-

大數問題:求用一段C或C程序寫求 f(x)100! 的完整程序大數問題&#xff0c; 我用數組作的&#xff0c;輸出格式應該是是222,222,222 #include "stdafx.h" #include<stdio.h> #include<stdlib.h> int a[1000]{0}; in…

javaEE的開發模式

1&#xff0e;什么是模式 模式在開發過程中總結出的“套路”&#xff0c;總結出的一套約定俗成的設計模式 2&#xff0e;javaEE經歷的模式 model1模式&#xff1a; 技術組成&#xff1a;jspjavaBean model1的弊端&#xff1a;隨著業務復雜性 導致jsp頁面比較混亂 model2模式…

Linux內核設計與實現---kobject sysfs

kobject sysfs1 kobject2 ktype3 kset4 subsystem5 別混淆了這些結構體6 管理和操作kobject7 引用計數kref8 sysfssysfs中添加和刪除kobject向sysfs添加文件9 內核事件層2.6內核增加了一個引人注目的新特性—同一設備模型。設備模型提供了獨立的機制專門表示設備&#xff0c;并…

開發Windows Mobile今日插件 -- 內存電量,桌面便箋,桌面記單詞

本篇文章講解的是開發 Windows Mobile 上的今日插件。關于是今日插件&#xff0c;在 PPC 或者 SP SDK 的幫助文檔中有相關的章節介紹&#xff0c;在網絡上也有一些帖子和資源講解。在這里簡要回顧一下。今日插件就是在windows mobile的桌面上顯示的條目&#xff0c;例如系統提供…

c語言中將函數指針作為形參_在C中將有效指針作為NULL指針

c語言中將函數指針作為形參Prerequisite: An Example of Null pointer in C 先決條件&#xff1a; C中的空指針示例 Any pointer that contains a valid memory address can be made as a NULL pointer by assigning 0. 通過分配0&#xff0c;可以將包含有效內存地址的任何指…

[轉]一個清華計算機博士生的退學申請

偶然間在網上看到這篇帖子&#xff0c;回想起自己的求學經歷&#xff0c;思索良久。。。 本想找到原帖及作者&#xff0c;但是幾經搜索&#xff0c;發現原帖出自科學網&#xff0c;已被刪除。對此&#xff0c;我還能說啥&#xff1f;&#xff01; http://www.sciencenet.cn/m/u…

算法---遞歸

遞歸結題三部曲 何為遞歸&#xff1f;程序反復調用自身即是遞歸。 我自己在剛開始解決遞歸問題的時候&#xff0c;總是會去糾結這一層函數做了什么&#xff0c;它調用自身后的下一層函數又做了什么…然后就會覺得實現一個遞歸解法十分復雜&#xff0c;根本就無從下手。 相信…

給定條件找最小值c語言程序_根據給定條件最小化n的最小步驟

給定條件找最小值c語言程序Problem statement: 問題陳述&#xff1a; Given a number n, count minimum steps to minimize it to 1 performing the following operations: 給定數字n &#xff0c;執行以下操作&#xff0c;計算最少的步驟以將其最小化為1&#xff1a; Operat…

提高C#編程水平不可不讀的50個要訣

提高C#編程水平的50個要點 1.總是用屬性 (Property) 來代替可訪問的數據成員 2.在 readonly 和 const 之間&#xff0c;優先使用 readonly 3.在 as 和 強制類型轉換之間&#xff0c;優先使用 as 操作符 4.使用條件屬性 (Conditional Attributes) 來代替條件編譯語句 #if 5.總是…

那個年代的蘇聯歌曲

小時候&#xff0c;不時聽父親提起電影《這里的黎明靜悄悄》&#xff0c;怎么也想不到如此美麗的名字為什么要和戰爭聯系起來。后來在大學看了這部電影之后&#xff0c;開始認為這名字是合適的&#xff0c;因為電影講的是女性——戰場中的女性&#xff0c;各自都懷揣著愛情去保…

linux系統編程---進程總結

進程控制總結1 進程創建的三種方式forkvfrokclone2 進程終止進程正常退出returnexit_exit進程異常退出進程收到某個信號&#xff0c;而該信號使進程終止abort3 進程等待進程等待的方法waitwaitpid4 進程替換替換原理替換函數制作一個簡單的shell1 進程創建的三種方式 參考文章…

銀行賬務轉賬系統(事務處理)

流程如下&#xff1a; 創建項目工程如下&#xff1a; transfer包下的代碼如下&#xff1a; package beyond.transfer.dao;import java.sql.Connection; import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import beyond.utils.DataSourceUtils;pu…