1. 程式人生 > >linux 記憶體管理---頁框回收(十)

linux 記憶體管理---頁框回收(十)

  • 為什麼需要頁回收?
linux的設計哲學之一:儘可能多的使用記憶體,比如儘可能的多使用memory cache,disk cache,因為這在系統負載比較小時,能夠提升系統性能,但是隨著cache越來越多,這遲早會消耗完所有可用的記憶體,所以只有在系統記憶體不足時才會觸發頁回收。
  • 觸發頁回收的時機
  1. 記憶體緊缺
grow_buffers(),alloc_page_buffers(),_ _alloc_pages()等函式分配記憶體失敗後,呼叫try_to_free_pages()函式直接回收頁框
  1. suspend-to-disk
  2. 週期性 (vmscan.c)
kswapd核心執行緒週期性的檢查各zone中watermark的情況

  • 哪些頁可以被回收?
只有分配給使用者態地址空間的頁,disk caches和memory caches頁才是可以回收的。
  • 頁是如何被回收的?
首先通過一定的演算法選出要回收的頁,然後找到要回收的頁對映的所有線性地址,即找到所有的包含要回收也得頁表項(這就是逆向對映),然後修改所有的頁表項,將要回收的頁放入夥伴系統的freelist中。
  • Reverse Mapping (Rmap.c)
逆向對映,或叫反向對映,即如何通過頁描述符,找到哪些頁表映射了該頁,page->_mapcount>0, 說明該頁被多個頁表所對映,但究竟是哪些頁表,該怎麼得到呢?首先對頁進行分類:不可回收頁,可交換頁,可同步頁,可丟棄頁
對映頁:指該頁映射了檔案的某一部分,比如基於檔案記憶體對映的使用者態地址空間中的所有頁都是對映頁,頁快取記憶體中的頁也都是對映頁。對映頁差不多都是可同步的,可回收的。匿名頁: 指屬於某一程序的某個匿名線性區,匿名線性區是指該線性區沒有與之對應的檔案,比如使用者態的堆和棧都為匿名線性區,為回收頁框, 核心必須將頁中內容儲存到一個專門的磁碟分割槽或磁碟檔案,叫做“交換區“.因此,所有匿名頁都是可交換的.頁描述符欄位:page->_mapcount: 頁對映的次數,即出現在頁表中的次數, -1表示該頁沒有被對映(可能為高階記憶體),0表示被對映一次1.匿名頁逆向對映匿名頁描述符中的mapping欄位指向anon_vma資料結構,anon_vma形成的雙向連結串列指出對映該頁的所有線性區

static inline unsigned longvma_address(struct page *page, struct vm_area_struct *vma){    pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);    unsigned long address;    address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);    if (unlikely(address < vma->vm_start || address >= vma->vm_end)) {        /* page should be within any vma from prio_tree_next */        BUG_ON(!PageAnon(page));        return -EFAULT;    }    return address;}通過頁描述符可以得到對應線性區的該頁的對應線性地址,通過線性地址就很容易得到頁表項了。static int try_to_unmap_anon(struct page *page){    struct anon_vma *anon_vma;    struct vm_area_struct *vma;    int ret = SWAP_AGAIN;    anon_vma = page_lock_anon_vma(page); //page->mapping    if (!anon_vma)        return ret;    list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {   //得到所有的線性區        ret = try_to_unmap_one(page, vma);        if (ret == SWAP_FAIL || !page_mapped(page))            break;    }    spin_unlock(&anon_vma->lock);    return ret;}2.檔案對映的頁逆向對映檔案對映頁描述符中的mapping欄位指向address_space資料結構它的思路也和匿名頁逆向對映相似,首先指出哪些線性區包含了給定頁,然後通過線性區找出對應的頁表項。但是和匿名對映不同的是,檔案對映的頁被共享的程序更多,比如標準c庫,幾乎被所有程序共享,所以包含給定頁的線性區連結串列就會很長,所以檔案對映的逆向對映採用了不同的實現方式:priority search trees, quickly locate all thememory regions that refer to the same page frame.每個檔案對應個優先搜尋樹,它存放在address_space 物件的i_mmap 欄位中:struct address_space {    struct inode        *host;        /* owner: inode, block_device */    struct radix_tree_root    page_tree;    /* radix tree of all pages */    ...
    struct prio_tree_root    i_mmap;        /* tree of private and shared mappings */    ...
}prio_tree_node 資料結構表示的個PST 節點,該資料結構在每個符的shared.prio_tree_node欄位中static int try_to_unmap_file(struct page *page){    struct address_space *mapping = page->mapping;    pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);    struct vm_area_struct *vma;    struct prio_tree_iter iter;    int ret = SWAP_AGAIN;    unsigned long cursor;    unsigned long max_nl_cursor = 0;    unsigned long max_nl_size = 0;    unsigned int mapcount;    spin_lock(&mapping->i_mmap_lock);    vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {   //page->mapping->i_mmap存放搜尋樹的根,得到樹上的每個節點,然後container of()得到包含節點的線性區        ret = try_to_unmap_one(page, vma);        if (ret == SWAP_FAIL || !page_mapped(page))            goto out;    }    ...
}
  • 頁回收演算法
linux採用LRU,即least recently used演算法獲得要回收的頁,如果頁屬LRU 連結串列,則設定頁描述符中的PG_lru標志.此外,如果頁屬於活動連結串列,則設定PG__active 標誌.而如果頁屬於非活動連結串列,則清PG_act ive標誌.頁描述符的lru 欄位存放指向LRU 連結串列中下個元素和前一個元素的指標,active_list和inactive_list統稱為lru list.當從zone中分配頁時,只有可回收的頁才會放在zone描述符的active_list和inactive_list連結串列中:
  1. 當按需裝入進程的匿名頁時,do_anonymous_page ( )函數執行
  2. 按需裝入記憶體對映檔案的個頁時,由filemap_nopage ()函數執
  3. 當按需裝入IPC 共享記憶體區的一個頁時shme凡nopage () 函式執行
  4. 從檔案讀取資料頁時,由do_generic_file_read () 函式執行
  5. 當換入個頁時,do_swap_page ( 函式執
  6. 當在頁快取記憶體中搜索一個緩衝區頁時, __f ind_get_block {) 函式
在上述這些函式中將新分配到的頁通過呼叫lru_cache_add ( )加入inactive_list或呼叫lru_cache_add_active()加入active_list中。也就是說要回收的頁都是從active_list和inactive_list連結串列這兩個連結串列中得到的。頁在inactive_list和active_list之間的移動:根據 least recently used,頁在active_list和inactive_list之間進行移動, 在頁描述符中的PG_referenced標誌用來把一個頁從非活動連結串列移到活動連結串列所需的訪問次數加倍,也把一個頁從活動連結串列移到非活動連結串列所需的“丟失訪問”次數加倍.例如,假定在非活動連結串列中的一個頁其PG_re ferenced 標誌置為0. 第次訪問把這個標誌置為I ,但是這一頁仍然留在非活動連結串列中.第二次對該頁訪問時發現這一標誌被設定, 才把頁移到活動連結串列。但是,如果第次訪問之後在給定的時間間隔內第二次訪問沒有發生,那麼頁框回收演算法就可能重置PG__referenced 標誌。
當核心必須把一個頁標記為訪問過時 就呼叫mark_page_accessed()數。PFRA 掃描一頁呼叫一次page_referenced()函式,如果PG_referenced標誌或頁表項中的某些Accessed標誌位置位.則該函式返回1,否則返回0。該函式首先檢查頁描述符的PG_referenced標誌。如果標誌置位則清0。從活動連結串列到非活動連結串列移動頁不是由page_referenced()函式,而是由refill_inactive_zone()函式實施的。refill_inactive_zone()函式由shrink_zone()呼叫.而shrink_zone ()函式對頁快取記憶體和使用者態地址空間進行頁回收,refill_inactive_zone()函式的工作至關重要,因為,從活動連結串列將頁移到非活動鏈表就意味著頁遲早要被PFRA 捕獲。如果函式的掠奪性過強,就會有過多的頁從活動鏈表被移動到非活動連結串列.因此, PFRA 就會回收大量的頁框,系統性能會受到影響.反過來,如果函式太懶惰 就沒有足夠的採用頁來補充非活動連結串列, PFRA 就不能回收內存。為此,該函式可以調整自己的行為:開始時.對每次呼叫,掃描非活動連結串列中少量的頁,但是PFRA 很難回收記憶體時,refill_inactive_zone()在每次呼叫時就逐漸增加掃描的活動頁數, scan_control 資料結構中priority字段的值控制該函式的行(低值表示更緊迫的優先順序)。LRU 連結串列中有兩類頁:屬於使用者態地址空間的頁、不屬於任何使用者態程序且在頁快取記憶體中的頁.PFRA 傾向於壓縮頁高速快取,而將使用者態程序的頁留RAM 中.然而,每一種策略中都沒有個固定的黃金法則保證在每個場景中系統的高效能,所以refill_inactive_zone()函式使用交換傾向( swap tendency 經驗值,由它確定函式是移動所有的頁還是隻移動不屬於使用者態地址空間的頁。函式按如下公式計算交換傾向值:                        交換傾向值=對映比率/2 +負荷值+交換值對映比率( mapped ratio ) 是使用者態地址空間所有記憶體管理區的頁(sc->nr_mapped)佔所有可分配頁框數的百分比。mapped_ratio 的值大表示動態記憶體大部分用於使用者態進程.而值小則表示大部分用於頁快取記憶體.                        mapped_ratio = (sc->nr_mapped * 100) / total_memory;負荷值( distress ) 用於表示PFRA 在管理區中回收頁框的效率, 其依據是前次PFRA執行時管理區的掃描優先順序,這個優先順序存放在管理區描述符的prev_priority 欄位                        distress = 100 >> zone->prev_priority;
最後,交換值( swappiness ) 是個使用者定義常數.預設值為60. 系統管理員可以在/proc/sys/vm/swappiness 檔案內修改這個值,或用相應的sysctl ()系統呼叫調整這個值。只有當管理區交換傾向值大於等於1 00 時.頁才從程序地址空間回收.那麼當系統管理員將交換值設為0 時, PFRA 就不會從使用者態地址空間回收頁,除非管理區的前一次先級為0 (這不大可能發生).如果系統管理員將交換值設為1 00 ,那麼PFRA 每次呼叫該函式時都會從使用者態地址空間回收頁.if (swap_tendency >= 100)        reclaim_mapped = 1;    while (!list_empty(&l_hold)) {        cond_resched();        page = lru_to_page(&l_hold);        list_del(&page->lru);        if (page_mapped(page)) {            if (!reclaim_mapped ||                (total_swap_pages == 0 && PageAnon(page)) ||                page_referenced(page, 0, sc->priority <= 0)) {                list_add(&page->lru, &l_active);                continue;            }        }        list_add(&page->lru, &l_inactive);    }try_to_free_pages() shrink_cache( ),這些函式目的是找到要回收的頁,shrink_list() 函式才是真正執行頁回收,將page list放入夥伴系統中。shrink_slab()函式用於釋放obj cache到slab分配器中,比如dentry cache, inode cachecache_reap()函式用於釋放slab分配器中空閒的slab,即歸還slab佔據的頁到夥伴系統中
  • OOM(out of memory)
當空閒記憶體十分緊缺且PFRA 又無法成功回收任何頁時,__ alloc_pages()呼叫out_of_memory()函式刪除程式,該過程通過select_bad_process()選擇系統中的一個程序,然後呼叫oom_kill_process()強行刪除它並釋放佔用的頁框
  • Swapping
Swapping has been introduced to offer a backup on disk for unmapped pages. 交換是頁框回收的個最高階特性。如果我們要確保程序的所有頁框都能被PFRA隨意回收,而不僅僅是回收有磁碟映像的頁,那麼就必須使用交換。• Pages that belong to an anonymous memory region of a process (User Modestack or heap)• Dirty pages that belong to a private memory mapping of a process• Pages that belong to an IPC shared memory region我們知道每個頁表項包含個Present軒u志。核心利用這個標誌來通知屬於某個程序地址空間的頁已披換出。在這個標誌之外, Linux還利用頁表中的其他位存放換出頁識別符號(swapped-out page identifier ) 。該識別符號用於編碼換出頁在磁碟上的位置.當缺頁異常發生時,相應的異常處理程式可以檢測到該頁不在RAM 中,然後呼叫函式從磁碟換入該缺頁。交換區從記憶體中換出的頁存放在交換區( swap area ) 中.交換的實現可以使用自己的磁碟分割槽,也可以使用包含在大型分割槽中的檔案.可以定義幾種不同的交換區,最大個數由MAX_ SWAPFILES 巨集( 通常被設定成32 )確定.每個交換區都由組頁槽( page slot ) 組成.也就是說.由組4096 位元組大小的塊組成,每塊中包含個換出的頁.交換區的第一個頁槽用來永久存放有關交換區的資訊,其格式由swap_header 聯合體(由兩個結構info和magic 組成)來描述. magic 結構提供了個字串,用來把磁碟某部分明確地標記成交換區,它只含有個欄位magic.magic ,這個欄位含有一個10 字元的 mag i c ”字串. magic 結構從根本上允許核心明確地把個檔案或分割槽標記成交換區.這個字串的內容就是“SWAP-SPACE ”orSWAPSPACE2”. 該欄位通常位於第一個頁懵的末尾。union swap_header {    struct {        char reserved[PAGE_SIZE - 10];        char magic[10];            /* SWAP-SPACE or SWAPSPACE2 */    } magic;    struct {        char         bootbits[1024];    /* Space for disklabel etc. */        unsigned int version;        unsigned int last_page;        unsigned int nr_badpages;        unsigned int padding[125];        unsigned int badpages[1];    } info;};交換區描述符每個活動的交換區在記憶體中都有自己的swap_info_struct描述符:struct swap_info_struct {unsigned int flags;                //交換區標誌    spinlock_t sdev_lock;    struct file *swap_file;            //指標.指向存放交挨區的普通檔案或裝置文件的檔案物件    struct block_device *bdev;   //存放交換區的塊裝置描述符    struct list_head extent_list;   //組成交換區的子區鏈袤的頭部    int nr_extents;                      //組成交煥區的子區數量    struct swap_extent *curr_swap_extent;       //指向最近使用的子區描述符的指標    unsigned old_block_size;                 //存放交換區的磁碟分割槽自然塊大小    unsigned short * swap_map;   //指向計數器陣列的指標,交換區的每個頁槽對應個數組元素    unsigned int lowest_bit;       //在搜尋個空閒頁槽時要掃描的第一個頁槽    unsigned int highest_bit;    unsigned int cluster_next;    unsigned int cluster_nr;    int prio;            /* swap priority */          // 交換區優先順序    int pages;    unsigned long max;    unsigned long inuse_pages;    int next;            /* next entry on swap list */}flags 欄位:SWP USED:如果交換區是活動的,該值就是1,如果交換區不是活動的,該值就是0.SWP_WRITEOK:如果可以寫入交換區.該值就是1; 如果交換區只讀,該值就是0 (可以是活動的或不是活動的)。SWP_ACTIVE:這個兩位的欄位實際上是SWP_USED 和SWP_WRITEOK 的組合。如果前面兩個標志置位,那麼SWP_ACTIVE標誌置位.swap_map字段指向個計數器陣列,交換區的每個頁槽對應一個元素.如果計數器值等於0 ,那麼這個頁情就是空閒的;如果計數器為正數.那麼換出頁就填充了這個頁槽.實際上,頁槽計數器的值就表示共享換出頁的程序數.prio字段是一個有符號的整數,表示交換子系統依據這個值考慮每個交換區的次序。swap_info陣列包括MAX _SWAPFI LES 個交換區描述符.只有那些設定了SW P_ USED的交換區才被使用,因為它們是活動區域.
換出頁識別符號可以很簡單地而又唯地標識個換出頁.這是通過在swap_info陣列中指定交換區的索引和在交換區內指定頁槽的索引實現的.換出頁識別符號的格式如下:
swp_entry ( type,offfset)巨集負責從交換區索引type 和頁槽索引offset中構造換出頁識別符號。swp_typeswp_offset 巨集正好相反,它們分別從換出頁識別符號中提取出交換區索引和頁槽索引.頁被換出時,其識別符號就作為頁的表項插入頁表中,這樣在需要時就可以再找到這個頁。要注意這種識別符號的最低位與Present 標誌對應,通常被清除來說明該頁目前不在RAM 中。啟用和禁用交換區旦交換區被初始化,超級使用者(或者更確切地說是任何具有CAP_ SYS_ADM

相關推薦

linux 記憶體管理---回收

為什麼需要頁回收?linux的設計哲學之一:儘可能多的使用記憶體,比如儘可能的多使用memory cache,disk cache,因為這在系統負載比較小時,能夠提升系統性能,但是隨著cache越來越多

深入淺出記憶體管理--描述符Page

核心中採用struct page來描述實體記憶體頁,它的主要成員如下(非全部成員): unsigned long flags; 標誌位,核心中每個page的狀態可以由此標誌位來表示,列舉幾個標誌位: PG_locked 頁被鎖定,比如在磁碟I/O操作中涉及到的頁,不

Linux記憶體管理之slab機制概述

通過前面所有程式碼的分析和總結,已經把各個部分熟悉了一遍,在此對Linux核心中slab機制做最後的總結。 夥伴系統演算法採用頁作為基本記憶體區,這適合於大塊記憶體的請求。對於小記憶體區的申請,比如說幾十或幾百個位元組,我們用slab機制。 Slab分配器把物件分組放進快取

Linux內核線程kernel thread詳解--Linux進程的管理與調度

block 完成 lag 工作原理 並行 kernel png 定義 define 內核線程 為什麽需要內核線程 Linux內核可以看作一個服務進程(管理軟硬件資源,響應用戶進程的種種合理以及不合理的請求)。 內核需要多個執行流並行,為了防止可能的阻塞,支持多線程是必要的。

Linux記憶體管理之slab機制初始化

 一、核心啟動早期初始化 start_kernel()->mm_init()->kmem_cache_init() 執行流程: 1,初始化靜態initkmem_list3三鏈; 2,初始化cache_cache的nodelists欄位為1中的三鏈; 3,根據記憶

Linux記憶體管理之slab機制釋放物件

 Linux核心中將物件釋放到slab中上層所用函式為kfree()或kmem_cache_free()。兩個函式都會呼叫__cache_free()函式。 程式碼執行流程: 1,當本地CPU cache中空閒物件數小於規定上限時,只需將物件放入本地CPU cache中;

linux程序管理之輕量級程序

在Linux中,輕量級程序可以是程序,也可以是執行緒。我們所說的執行緒,在Linux中,其實是輕量級程序之間共享程式碼段,檔案描述符,訊號處理,全域性變數時; 如果不共享,就是我們所說的程序。 程序是資源管理的最小單位,執行緒是程式執行的最小單位。在作業系統設計上,從程序演化出執行緒,最主要的目的就是減小

linux程序管理之程序建立

在linux系統中,許多程序在誕生之初都與其父程序共同用一個儲存空間。但是子程序又可以建立自己的儲存空間,並與父程序“分道揚鑣”,成為與父程序一樣真正意義上的程序。  linux系統執行的第一個程序是在初始化階段“捏造出來的”。而此後的執行緒或程序都是由一個已存在的程序像細胞分裂一樣通過系統呼叫複

Linux電源管理_autosleep--【轉】

本文轉載自:https://blog.csdn.net/wlsfling/article/details/46005409 1. 前言 Autosleep也是從Android wakelocks補丁集中演化而來的(Linux電源管理(9)_wakelocks),用於取代Android wakelocks中

Linux學習筆記CentOS 6.5---Mysql防火牆開放3306埠

防火牆開放3306埠 1、開啟防火牆配置檔案 vi  /etc/sysconfig/iptables   2、增加下面一行 -A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT  

Python 下字串的連線、簡單替換與unicode字串- 千月的python linux 系統管理指南學習筆記12

Python 下字串的連線、簡單替換與unicode字串 繼續上一章的內容,看一看字串的連線和替換 字串的連線 join() 將多個字串連線起來的”膠水“ 字元物件.join(字串或者列表) #連線字串,或者與列表裡的字元分別連線。 光是將2個字串相連。其實意義不大,一個

Linux核心--網路棧實現分析--網路層之IP協議

本文分析基於Linux Kernel 1.2.13作者:閆明注:標題中的”(上)“,”(下)“表示分析過程基於資料包的傳遞方向:”(上)“表示分析是從底層向上分析、”(下)“表示分析是從上向下分析。上篇博文分析傳輸層最終會呼叫函式ip_queue_xmit()函式,將傳送資料

Python 下字串的提取、分割與刪除- 千月的python linux 系統管理指南學習筆記11

Python 下字串的提取、分割與刪除 對於文字來講,提取、分割和刪除是我們用的較多的操作。 文字我們可以看成是字串物件。首先說到的是 in 和 not in操作。 字串的提取 in 和 not i

linux 記憶體定址 學習筆記

ARM:RISC,精簡指令系統集 X86:CISC,複雜指令系統集 CISC:犧牲處理器本身的複雜度,換取高效能 RISC:將複雜度交給了編譯器,犧牲了程式大小和指令頻寬,換取了簡單和低功耗的硬體實現 CPL:current privilege leve

嵌入式Linux C程式設計學習之路——標準IO fopen/fclose

標準IO與檔案IO的對應函式 檔案IO:                                             標準IO open                                                 fopen close

Linux使用者管理全攻略

摘要:本文詳解使用者(User)和使用者組(Group)的配置檔案,本文是《Linux 使用者(User)和使用者組(Group)管理概述》文件的關健部份的細化;通過本文,您至少能明白/etc/passwd /etc/group ,以及什麼是UID和GID 等;其中對UID的

Python 建立、讀取和寫入檔案以及yield關鍵字- 千月的python linux 系統管理指南學習筆記14

無論是日誌檔案還是配置檔案都是我們日常運維中常見的型別,學習處理檔案的關鍵是學會如何處理文字資料。Python 包含一個稱為 file 的內建型別,可以用來處理檔案。 建立檔案物件 為了讀取一個現有的檔案,我們需要建立一個新的檔案物件,以用來對檔案進行互動。 open

[Linux記憶體]slab分配器學習筆記--概念

http://blog.csdn.net/vanbreaker/article/details/76642961,為什麼需要slab分配器:  利用夥伴系統進行分配記憶體只能按照頁的單位進行分配,這樣會造成很多的記憶體浪費,多了很多記憶體碎片,比如只需要申請10位元組的,結

abp(net core)+easyui+efcore實現倉儲管理系統——多語言

abp(net core)+easyui+efcore實現倉儲管理系統目錄 abp(net core)+easyui+efcore實現倉儲管理系統——ABP總體介紹(一) abp(net core)+easyui+efcore實現倉儲管理系統——解決方案介紹

【原創】Linux記憶體管理slub分配器

背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,