天天看點

記憶體基礎小知識(arm)

記憶體基礎小知識(arm)
記憶體基礎小知識(arm)
記憶體基礎小知識(arm)

處理器的cr0寄存器Pcd标志用來啟用或者禁用高速緩存電路。

pcd标志是通路葉匡中的資料時高速緩存是否開啟,pwt表示将資料寫到葉匡的時候采用的回寫政策是通寫還是回寫。

TLB用于緩存剛剛被通路的實體位址。每個cpu都有一個TLB。

常用宏功能:

PAGE_SHIFT:指定offset字段的位數,頁框大小。

PMD_SHIFT指定線性位址的offset字段與table字段的總位數。32位當PAE被禁用的時候是22位offset(12)+table(10),PMD_MASK為0xffe00000。

PUD_SHIFT:頁上級目錄項所映射的區域大小的對數。32位的環境上由于是兩級頁表是以總是等于PMD_SHIF。

PGDIR_SHIFT:頁全局目錄項所能映射的區域大小的對數。PGDIR_MASK宏用于屏蔽offset、table、middle、upper字段的所有位。

PTRS_PER_PTE、OTRS_PED_PMD、PTRS_PER_PUD、PTRS_PER_PGD用于計算頁表、頁中間目錄、頁上級目錄和頁全局目錄表中的表項個數。

pte_t、pmd_t、pud_t、pgd_t分别描述頁表項、頁中間目錄項、頁上級目錄和頁全局目錄定義。

__pte\__pmd\__pud\__pgd\__pgprot和pte_val\pmd_val\pud_val\pgd_val\pgprot_val是将相應表項和無符号整數互相轉換。

pte_none\pmd_none\pud_none\pgd_none表示相應的表項值為0則傳回1否則傳回0。

pte_clear\pmd_clear\pud_clear\pgd_clear清除相應的表項,set_pte\set_pmd\set_pud\set_pgd向一頁表中寫入指定的值。

pte_same表示兩個頁表項指向相同的頁且有相同的通路優先級則傳回1否則傳回0。

pmd_large(e)當e指向大型頁則傳回1。

pmd_bad用于檢查傳入的頁中間目錄參數,如果頁中間所指向的頁表滿足下列之一傳回1。

a頁不存在(present标志被清除)b頁隻允許讀通路(read\write标志被清除) c access或者 dirty标志被清除。pud_bad和pgd_bad總是傳回0,沒有定義pte_bad。

pte_user\pte_read讀取user/supervisor标志

pte_write\pte_exec讀取read/write标志

pte_dirty 讀取dirty标志

pte_young 讀取accessed标志

pte_file讀取dirty标志

設定頁标志函數:

mk_pte_huge設定頁表中的page size和present标志

pte_wrprotect 清除read/write标志

pte_rdprotect/pte_exprotect 清除user/supervisor标志

pte_mkwrite 設定read/write标志

pte_mkread/pte_mkexec  設定user/supervisor标志

pte_mkclean 清除dirty标志

pte_mkdirty 設定dirty标志

pte_mkold/pte_mkyoung  清除/清除accessd标志

pte_modify(p,v) 把頁表p所有通路權限設定為v

ptep_set_wrprotect 與pet_protect類似但是隻是針對頁表項。

petp_set_access_flags 如果dirty為1則将頁的存取權限設定為指定的值,并且調用flush_tlb_page函數重新整理tlb。

ptep_test_and_clear_dirty 隻是針對頁表項

petp_test_and_clear_young 隻是針對頁表項

頁表操作宏:

pgd_index(addr) 找到現行位址addr對應的目錄項在頁全局目錄中的索引。

pgd_offset(mm,addr)根據addr找到頁全局目錄内的相應頁目錄項的線性位址。

pgd_offset_k(addr) 産生核心頁全局目錄包含addr的目錄項。

pgd_page(pgd) 産生頁上級目錄所在頁框的頁描述符位址。

pud_offset(pgd,addr) 頁上級目錄中目錄項addr對應的線性位址

pud_page(pud) 通過頁上級目錄pud産生相應的頁中間目錄的線性位址。

pmd_index(addr) 産生線性位址addr在頁中間目錄的目錄項的索引

pmd_page(pmd) 頁中間目錄項pmd産生的頁表描述符位址。

mk_pte(p,prot) 接收頁描述符位址p和存取權限prot作為參數

pte_index(addr) 産生線性位址addr對應的表項在頁表中的索引

pte_offdset_kernel(dir,addr),dir是頁中間目錄,線性位址addr在頁中間目錄dir中的對應的項即頁表的線性位址。

pte_offset_map(dir,addr)接收指向一個頁中間目錄項的指針dir和線性位址addr作為參數,産生與線性位址addr相對應的頁表項的線性位址。

pte_page(x) 傳回頁表項x所引用的描述符位址

pte_to_pgoff(pte) 從一個頁表項的pte字段中提取檔案偏移量,這個檔案偏移量對應着一個非線性檔案記憶體映射所在的頁。

pgoff_to_pte(offset)為非線性檔案記憶體所映射的頁建立對應的頁表項的内容

pgd_alloc(mm) \pgd_free配置設定一個新的頁全局目錄

pud_alloc(mm,pgd,addr)/pud_free配置設定pud

pmd_alloc(mm,pud,addr)/pmd_free,為addr配置設定個新的頁中間目錄

pte_alloc_(mm,pmd,addr) 如果頁中間目錄為空則配置設定個新的頁表,addr對應的項目被建立且user/supervisor标志被設定為1。如果頁表在高端記憶體則建立一個臨時映射

pte_alloc_kernel(mm,pmd,addr)配置設定一個新的頁表然後傳回與addr相關的線性位址。

pte_free(pte)釋放與頁描述符指針相關的頁表

pte_free_kernel(pte) 核心首頁表使用類似與pte_free

clear_page_range(mmu,start,end)從線性位址start到end通過反複釋放頁表和清除頁中間目錄項來清除程序頁表内容。

set_fixmap(idx,phyaddr)将idx指定的頁表設定為phyaddr指定的實體位址

set_fixmap_nocache(idx,phyaddr)類似set_fixmap 隻是多設定了pcd标志表示不使用cache緩存。

L1_CACHE_BYTES 表示高速緩存行的L1的大小以位元組為機關

flush_tlb_all   重新整理所有的TLB表項。

flush_tlb_kernel_range重新整理給定範圍内的所有的TLB表

flush_tlb 重新整理目前程序擁有的非全局頁相關的所有TLB

flush_tlb_mm 重新整理指定程序擁有的TLB非頁全局相關的所有TLB

flush_tlb_range 重新整理指定程序的線性位址間隔對應的TLB表項

flush_tlb_pgtables 重新整理指定程序中特定的相鄰頁表集相關的TLB

flush_tlb_page 重新整理指定程序中單個頁表項相關的TLB表項

任何程序切換都會更新活動頁表集,本地tlb必須重新整理

_count 頁的引用計數  page_count()函數傳回_count+1後的值

flags  頁框狀态的标志

記憶體基礎小知識(arm)
記憶體基礎小知識(arm)

使用GFP_KERNEL的标志配置設定記憶體頁面即使沒有足夠的頁面也不會被阻塞,僅僅是配置設定失敗而已,,一般情況下核心會保留一個頁框池,在記憶體不足時供GFP_KERNEL使用,保留記憶體數量存放在min_free_kbytes(KB);

請求頁框标志: 

記憶體基礎小知識(arm)

組合标志:

記憶體基礎小知識(arm)

page_address(mm/highmem.c):傳回頁框對應的線性位址

1 根據PG_highmem标志判斷頁框是否在高端位址,不在則直接根據頁框下标轉換成實體位址再轉換成線性位址。

2 是高端位址,則到page_address_htable散清單查找,找到對應的頁框則傳回線性位址否則傳回NULL;

永久映射:

高端記憶體映射函數(arm arch/arm/mm/highmem.c):

kamp

1 首先判斷頁框是否是高端位址通過PG_highmem标志。不是則通過page_address直接傳回對應的線性位址。

2 是高端位址調用kmap_high

  1. 調用page_address檢查是否已經被映射,沒有則調用map_new_virtual
  2. 增加引用計數
  3. 如果重新整理完所有的kamp_count依舊沒有找到空閑的則進入睡眠阻塞,喚醒之後調用page_address檢視是否别人已經映射了,如果沒有則重新查找。

 map_new_virtual:

  1. 周遊pkmap_count查找一個空項,儲存查找到的索引值
  2. 設定特殊頁表pkmap_page_table的索引值對應的項,設定                    

pkmap_count表示已經使用。

對應的函數kunmap

  1 如果不是高端位址頁框則直接傳回,因為永遠線性映射區不需要釋放

2 調用 kunmap_high去釋放

kunmap_high:

  1. 将pkmap_count對應的項減一
  2. 如果減一之後為1則判斷是否存在睡眠等待的程序,存在則喚醒

struct vm_struct {

    struct vm_struct    *next;//指向下一個vm,連結清單第一個元素被vmlist變量指向

    void            *addr;//記憶體區的第一個記憶體單元線性位址

    unsigned long       size;//記憶體區大小+4096

    unsigned long       flags;//非連續記憶體區映射的記憶體類型

    struct page     **pages;//指向nr_pages數組指針,該數組由指向頁描述符的指針組成

    unsigned int        nr_pages;//記憶體區填充的頁面的個數

    phys_addr_t     phys_addr;//該字段為0,除非記憶體已經被建立來映射一個硬體裝置的io共享記憶體

    const void      *caller;

};

flags:

VM_ALLOC表示vmalloc得到的頁

VM_MAP 使用vmap映射的已經被配置設定的頁

IO_IOREMAP 表示使用ioremap映射的硬體裝置的闆上記憶體

get_vm_area如果flags沒有指定VM_NO_GUARD則添加vmalloc的間隙。

vmalloc函數的解析:

{

return __vmalloc_node_flags(size, NUMA_NO_NODE,GFP_KERNEL);

}

return __vmalloc_node(size, 1, flags, PAGE_KERNEL,node, __builtin_return_address(0));

  1    擷取vm且設定va(首先查找全局free_vmap_cache紅黑樹找到開始位址最小的va,然後從此vm開始周遊查找兩個va之間最小的且合适的間隙如果沒有找到則在va末尾添加一個,然後将va資訊填充到vm裡)

2    調用__vmalloc_area_node去擷取實體頁面并且設定頁表。

臨時映射:可以用在中斷或者可延遲函數内部不會阻塞上下文。注意是在kmap_atomic關搶占在__kunmap_atomic才會打開搶占。pagefault也一樣。

kmap_atomic:建立臨時映射

1 判斷是否是高端位址,如果不是直接調用page_address傳回線性位址

2 對于高端位址則設定pte頁表為page構造的pte。

__kunmap_atomic:解除臨時映射

    1 重新整理dcache 。

2 清除對應的頁表。

__zone_watermark_ok:檢查水位是否符合指定的值

1 首先判斷是否設定了ALLOC_HARDER或者ALLOC_OOM值,如果設定了則認為是alloc_harder申請。如果沒有指定則将free_pages減去nr_reserved_highatomic,去掉高端保留記憶體

2 指定了則繼續判斷是否指定了ALLOC_OOM,如果指定了則設定min減半否則min減少1/4。

3 判斷free_pages是否小于min+z->lowmem_reserve,如果小于則傳回false

4 判斷傳入參數order為0 則立即傳回真

5 從order開始周遊存在非空的則傳回true,如果是alloc_harder,則MIGRATE_HIGHATOMIC不空則也傳回true。

總結就是判斷在mark和保留記憶體水位之上且存在一個大于等于order的連續記憶體。

記憶體基礎小知識(arm)
記憶體基礎小知識(arm)
記憶體基礎小知識(arm)

繼續閱讀