Linux記憶體管理:Fixmaps(固定對映地址)和ioremap【轉】
轉自:https://rtoax.blog.csdn.net/article/details/114749083
目錄
Fix-Mapped
地址是一組特殊的編譯時地址,其對應的實體地址不必是線性地址減__START_KERNEL_map;
ioremap 功能是將給定的實體地址對映為虛擬地址。
Fixmaps和ioremap
Fix-Mapped
地址是一組特殊的編譯時地址,其對應的實體地址不必是線性地址減__START_KERNEL_map
。每個固定對映的地址都對映一個頁面框架,核心將其用作永遠不會更改其地址的指標。這就是這些地址的重點。如評論所述:to have a constant address at compile time, but to set the physical address only in the boot process
level2_fixmap_pgt
:
- NEXT_PAGE(level2_fixmap_pgt)
- .fill 506,8,0
- .quad level1_fixmap_pgt - __START_KERNEL_map + _PAGE_TABLE
- .fill 5,8,0
- NEXT_PAGE(level1_fixmap_pgt)
- .fill 512,8,0
如您所見level2_fixmap_pgt
,緊隨其後的level2_kernel_pgt
是核心程式碼+資料+ bss。每個修訂對映的地址都由一個整數索引表示,該整數索引fixed_addresses
VSYSCALL_PAGE
-如果啟用了對舊版vsyscall頁面的模擬,FIX_APIC_BASE
則為本地apic等。在虛擬記憶體中,固定對映區域位於模組區域:
- +-----------+-----------------+---------------+------------------+
- | | | | |
- |kernel text| kernel | | vsyscalls |
-
| mapping | text | Modules | fix-mapped |
- |from phys 0| data | | addresses |
- | | | | |
- +-----------+-----------------+---------------+------------------+
- __START_KERNEL_map __START_KERNEL MODULES_VADDR 0xffffffffffffffff
基本的虛擬地址和fix-mapped
區域的大小由以下兩個巨集表示:
這__end_of_permanent_fixed_addresses
是fixed_addresses
列舉的一個元素,正如我上面所寫:每個固定對映的地址都由一個整數索引表示,該索引在中定義fixed_addresses
。PAGE_SHIFT
確定頁面的大小。例如,我們可以通過1 << PAGE_SHIFT
表示式獲得一頁的大小。
在我們的例子中,我們需要獲取修訂對映區域的大小,而不僅僅是一個page,這就是為什麼我們__end_of_permanent_fixed_addresses
要獲取修訂對映區域的大小的原因。的__end_of_permanent_fixed_addresses
是最後索引fixed_addresses
列舉或換句話說,__end_of_permanent_fixed_addresses
包含在一個固定的對映區域頁面量。因此,如果將__end_of_permanent_fixed_addresses
頁面大小值的乘積,我們將獲得固定對映區域的大小。以我的為例,它超過了536
千位元組。在您的情況下,它可能是一個不同的數字,因為大小取決於修訂對映地址的數量,而修訂對映地址的數量取決於核心的配置。
第二個FIXADDR_START
巨集只是從固定對映區域的最後一個地址中減去固定對映區域的大小,以獲取其基本虛擬地址。FIXADDR_TOP
是從vsyscall空間的基地址開始的四捨五入地址:
#define FIXADDR_TOP (round_up(VSYSCALL_ADDR + PAGE_SIZE, 1<<PMD_SHIFT) - PAGE_SIZE)
該fixed_addresses
列舉被用作指數由獲得虛擬地址fix_to_virt
的功能。該功能的實現很容易:
- static __always_inline unsigned long fix_to_virt(const unsigned int idx)
- {
- BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
- return __fix_to_virt(idx);
- }
首先,它檢查為該fixed_addresses
列舉給出的索引是否大於或等於__end_of_fixed_addresses
該BUILD_BUG_ON
巨集,然後返回該__fix_to_virt
巨集的結果:
#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
在這裡,我們向左移動fix-mapped
區域的給定索引,該索引PAGE_SHIFT
決定了我在上面所寫的頁面大小,並從區域FIXADDR_TOP
的最高地址那裡減去它fix-mapped
:
- +-----------------+
- | PAGE 1 | FIXADDR_TOP (virt address)
- | PAGE 2 |
- | PAGE 3 |
- | PAGE 4 (idx) | x - 4
- | PAGE 5 |
- +-----------------+
有一個反函式,用於獲取與給定虛擬地址相對應的修訂對映區域的索引:
- static inline unsigned long virt_to_fix(const unsigned long vaddr)
- {
- BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START);
- return __virt_to_fix(vaddr);
- }
在virt_to_fix
採用虛擬地址,檢查該地址是間FIXADDR_START
和FIXADDR_TOP
並呼叫__virt_to_fix
其實現為巨集:
#define __virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT)
正如我們可以看到的,__virt_to_fix
巨集清除第一12
在給定的虛擬地址位,從最後地址的減去它fix-mapped
區域(FIXADDR_TOP
),並轉移該結果正確的PAGE_SHIFT
是12
。讓我解釋一下它是如何工作的。
與前面的示例一樣(在__fix_to_virt
巨集中),我們從修復對映區域的頂部開始。我們還從上到下從頭到尾搜尋與給定虛擬地址相對應的固定對映區域的索引。如您所見,首先我們將12
使用x & PAGE_MASK
表示式清除給定虛擬地址中的前幾位。這使我們能夠獲取頁面的基地址。如果給定的虛擬地址指向頁面的開頭/中間或結尾的某個位置,而不指向頁面的基地址,則需要執行此操作。在下一步中,從中減去此值FIXADDR_TOP
,這將為我們提供修訂對映區域中相應頁面的虛擬地址。最後,我們將這個地址的值除以PAGE_SHIFT
。這為我們提供了與給定虛擬地址相對應的固定對映區域的索引。看起來可能很難,但是如果您逐步進行此操作,則可以確保該__virt_to_fix
巨集非常簡單。
就這樣。現在,我們對fix-mapped
地址有所瞭解,但是接下來就足夠了。
Fix-mapped
地址在linux核心中的不同位置使用。IDT
描述符儲存在此處,英特爾可信執行技術UUID儲存在fix-mapped
從FIX_TBOOT_BASE
索引,Xenbootmap等開始的區域中...我們已經在linux核心初始化fix-mapped
的第五部分中看到了一些地址。我們fix-mapped
在早期ioremap
初始化中使用area。讓我們更仔細地看一下它,並嘗試瞭解ioremap
它是什麼,如何在核心中實現它以及它與fix-mapped
地址的關係。
對映
Linux核心提供了許多不同的原語來管理記憶體。此時此刻,提出I/O memory
。每個裝置都通過對其暫存器的讀/寫操作來控制。例如,驅動程式可以通過寫入裝置的暫存器來關閉/開啟裝置,或者通過讀取裝置的暫存器來獲取裝置的狀態。除暫存器外,許多裝置還具有緩衝區,驅動程式可以在其中寫入或讀取內容。眾所周知,目前有兩種訪問裝置暫存器和資料緩衝區的方法:
- 通過I / O埠;
- 將所有暫存器對映到儲存器地址空間;
在第一種情況下,裝置的每個控制暫存器都具有多個輸入和輸出埠。裝置驅動程式可以從一個埠讀取和寫入兩個in
和out
指示,我們已經看到了。如果您想了解當前註冊的埠區域,可以通過訪問來了解它們/proc/ioports
:
- $ cat /proc/ioports
- 0000-0cf7 : PCI Bus 0000:00
- 0000-001f : dma1
- 0020-0021 : pic1
- 0040-0043 : timer0
- 0050-0053 : timer1
- 0060-0060 : keyboard
- 0064-0064 : keyboard
- 0070-0077 : rtc0
- 0080-008f : dma page reg
- 00a0-00a1 : pic2
- 00c0-00df : dma2
- 00f0-00ff : fpu
- 00f0-00f0 : PNP0C04:00
- 03c0-03df : vesafb
- 03f8-03ff : serial
- 04d0-04d1 : pnp 00:06
- 0800-087f : pnp 00:01
- 0a00-0a0f : pnp 00:04
- 0a20-0a2f : pnp 00:04
- 0a30-0a3f : pnp 00:04
- 0cf8-0cff : PCI conf1
- 0d00-ffff : PCI Bus 0000:00
- ...
- ...
- ...
/proc/ioports
提供有關哪個驅動程式使用I/O
埠區域的哪個地址的資訊。所有這些儲存器區域的,例如0000-0cf7
,用所要求保護的request_region
功能從包括/ LINUX / ioport.h。實際上request_region
是一個巨集,定義為:
#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0)
如我們所見,它帶有三個引數:
start
-地區開始;n
-區域的長度;name
-請求者名稱。
request_region
分配I/O
埠區域。通常在check_region
之前呼叫該函式request_region
以檢查給定的地址範圍是否可用以及release_region
釋放儲存區域的函式。request_region
返回指向該resource
結構的指標。該resource
結構表示系統資源的樹狀子集的抽象。我們已經resource
在核心初始化過程的第五部分中看到了該結構,它看起來如下:
- struct resource { //該resource結構表示系統資源的樹狀子集的抽象
- resource_size_t start;
- resource_size_t end;
- const char *name;
- unsigned long flags;
- struct resource *parent, *sibling, *child;
- };
幷包含資源,名稱等的開始和結束地址每一個resource
結構包含指向的parent
,sibling
和child
資源。因為它有一個父母和一個孩子,這意味著資源的每個子集都具有根resource
結構。例如,對於I/O
埠,它是以下ioport_resource
結構:
- struct resource ioport_resource = {
- .name = "PCI IO",
- .start = 0,
- .end = IO_SPACE_LIMIT,
- .flags = IORESOURCE_IO,
- };
- EXPORT_SYMBOL(ioport_resource);
或者iomem
,它是iomem_resource
結構:
- struct resource iomem_resource = {
- .name = "PCI mem",
- .start = 0,
- .end = -1,
- .flags = IORESOURCE_MEM,
- };
正如我之前提到的,request_regions
它用於註冊I / O埠區域,並且該巨集在核心中的許多地方都使用。例如,讓我們看一下drivers/char/rtc.c。此原始碼檔案在linux核心中提供了Real Time Clock介面。作為每個核心模組,rtc
模組包含module_init
定義:
module_init(rtc_init);
這裡rtc_init
是rtc
初始化函式。此功能在同一rtc.c
原始碼檔案中定義。在rtc_init
函式中,我們可以看到對函式的幾個呼叫,例如rtc_request_region
包裝request_region
:
r = rtc_request_region(RTC_IO_EXTENT);
rtc_request_region被呼叫
:
r = request_region(RTC_PORT(0), size, "rtc");
這RTC_IO_EXTENT
是記憶體區域的大小,它是0x8
,"rtc"
是該區域的名稱,並且RTC_PORT
是:
#define RTC_PORT(x) (0x70 + (x))
因此,request_region(RTC_PORT(0), size, "rtc")
我們使用來註冊一個儲存區域,該儲存區域的起始位置為0x70
,大小為0x8
。讓我們看一下/proc/ioports
:
- ~$ sudo cat /proc/ioports | grep rtc
- 0070-0077 : rtc0
所以,我們明白了!好的,就是I / O埠了。與驅動程式通訊的第二種方法是使用I/O
記憶體。如前所述,這是通過將控制暫存器和裝置記憶體對映到記憶體地址空間來實現的。I/O
記憶體是裝置通過匯流排向CPU提供的一組連續地址。核心沒有直接使用任何記憶體對映的I / O地址。有一個特殊ioremap
功能允許我們將總線上的實體地址轉換為核心虛擬地址。換句話說,ioremap
對映I / O實體記憶體區域以使其可從核心訪問。該ioremap
函式有兩個引數:
- 記憶體區域的開始;
- 記憶體區域的大小;
I / O記憶體對映API提供了用於檢查,請求和釋放作為I / O記憶體的記憶體區域的功能。為此,有三個功能:
request_mem_region
release_mem_region
check_mem_region
- ~$ sudo cat /proc/iomem
- ...
- ...
- ...
- be826000-be82cfff : ACPI Non-volatile Storage
- be82d000-bf744fff : System RAM
- bf745000-bfff4fff : reserved
- bfff5000-dc041fff : System RAM
- dc042000-dc0d2fff : reserved
- dc0d3000-dc138fff : System RAM
- dc139000-dc27dfff : ACPI Non-volatile Storage
- dc27e000-deffefff : reserved
- defff000-deffffff : System RAM
- df000000-dfffffff : RAM buffer
- e0000000-feafffff : PCI Bus 0000:00
- e0000000-efffffff : PCI Bus 0000:01
- e0000000-efffffff : 0000:01:00.0
- f7c00000-f7cfffff : PCI Bus 0000:06
- f7c00000-f7c0ffff : 0000:06:00.0
- f7c10000-f7c101ff : 0000:06:00.0
- f7c10000-f7c101ff : ahci
- f7d00000-f7dfffff : PCI Bus 0000:03
- f7d00000-f7d3ffff : 0000:03:00.0
- f7d00000-f7d3ffff : alx
- ...
- ...
- ...
這些地址的一部分來自該e820_reserve_resources
函式的呼叫。我們可以在arch/x86/kernel/setup.c找到對此函式的呼叫,並且函式本身在arch/x86/kernel/e820.c.中定義。e820_reserve_resources
遍歷e820對映並將記憶體區域插入到根iomem
資源結構中。e820
插入iomem
資源中的所有記憶體區域具有以下型別:
- static inline const char *e820_type_to_string(int e820_type)
- {
- switch (e820_type) {
- case E820_RESERVED_KERN:
- case E820_RAM: return "System RAM";
- case E820_ACPI: return "ACPI Tables";
- case E820_NVS: return "ACPI Non-volatile Storage";
- case E820_UNUSABLE: return "Unusable memory";
- default: return "reserved";
- }
- }
我們可以在/proc/iomem
(如上所示)中看到它們。
ioremap
工作原理
ioremap
功能允許我們將總線上的實體地址轉換為核心虛擬地址
現在,讓我們嘗試瞭解其ioremap
工作原理。我們已經瞭解了一點ioremap
,我們在第五部分中瞭解了Linux核心初始化。如果您已閱讀此部分,則可以記住arch / x86 / mm / ioremap.c中的early_ioremap_init
函式呼叫。的初始化是分為兩個部分:有,我們可以前的正常使用初期可用且正常這是後可用初始化和呼叫。我們暫時一無所知,所以讓我們考慮對的早期初始化。首先檢查在頁面中間目錄邊界上對齊的內容:ioremap
ioremap
ioremap
vmalloc
paging_init
vmalloc
ioremap
early_ioremap_init
fixmap
BUILD_BUG_ON((fix_to_virt(0) + PAGE_SIZE) & ((1 << PMD_SHIFT) - 1));
關於BUILD_BUG_ON
您的更多資訊,可以在第一部分中閱讀有關Linux Kernel初始化的資訊。因此,BUILD_BUG_ON
如果給定的表示式為true,則macro會引發編譯錯誤。在檢查之後的下一步中,我們可以early_ioremap_setup
從mm / early_ioremap.c中看到該函式的呼叫。此函式提供的通用初始化ioremap
。early_ioremap_setup
函式slot_virt
使用早期Fixmap的虛擬地址填充陣列。所有早期的修訂圖都__end_of_permanent_fixed_addresses
在記憶體中。它們從FIX_BITMAP_BEGIN
(頂部)開始,以FIX_BITMAP_END
(向下)結束。實際上512
,早期有一些臨時的啟動時對映ioremap
:
和early_ioremap_setup
:
- void __init early_ioremap_setup(void)
- {
- int i;
- for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
- if (WARN_ON(prev_map[i]))
- break;
- for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
- slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i);
- }
的slot_virt
和其它陣列在相同的原始碼檔案中定義:
- static void __iomem *prev_map[FIX_BTMAPS_SLOTS] __initdata;
- static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata;
- static unsigned long slot_virt[FIX_BTMAPS_SLOTS] __initdata;
slot_virt
包含區域的虛擬地址fix-mapped
,prev_map
陣列包含早期ioremap區域的地址。請注意,我在上面寫過:Actually there are 512 temporary boot-time mappings, used by early ioremap
並且您可以看到所有陣列都使用__initdata
屬性定義,這意味著該記憶體將在核心初始化過程後釋放。後early_ioremap_setup
完成其工作,我們正在頁中間目錄,早期的ioremap與開始early_ioremap_pmd
剛剛得到頁全域性目錄的基地址和計算給定的地址頁面中間目錄功能:
- static inline pmd_t * __init early_ioremap_pmd(unsigned long addr)
- {
- pgd_t *base = __va(read_cr3_pa());
- pgd_t *pgd = &base[pgd_index(addr)];
- pud_t *pud = pud_offset(pgd, addr);
- pmd_t *pmd = pmd_offset(pud, addr);
- return pmd;
- }
之後,我們bm_pte
用零填充(早期的ioremap頁面表條目)並呼叫pmd_populate_kernel
函式:
- pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN));
- memset(bm_pte, 0, sizeof(bm_pte));
- pmd_populate_kernel(&init_mm, pmd, bm_pte);
pmd_populate_kernel
帶有三個引數:
init_mm
-init
程序的記憶體描述符(您可以在上一部分中瞭解它);pmd
ioremap
-Fixmaps開頭的頁面中間目錄;bm_pte
-早期ioremap
頁面表條目陣列,其定義為:
static pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] __page_aligned_bss;
該pmd_populate_kernel
函式在arch / x86 / include / asm / pgalloc.h中定義,並pmd
使用給定的頁面表項(bm_pte
)填充作為引數提供的頁面中間目錄():
- static inline void pmd_populate_kernel(struct mm_struct *mm,
- pmd_t *pmd, pte_t *pte)
- {
- paravirt_alloc_pte(mm, __pa(pte) >> PAGE_SHIFT);
- set_pmd(pmd, __pmd(__pa(pte) | _PAGE_TABLE));
- }
在哪裡set_pmd
:
#define set_pmd(pmdp, pmd) native_set_pmd(pmdp, pmd)
並且native_set_pmd
是:
- static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd)
- {
- *pmdp = pmd;
- }
就這樣。早就ioremap
可以使用了。early_ioremap_init
函式中有幾項檢查,但是它們並不是那麼重要,無論如何它們的初始化ioremap
已完成。
早期ioremap的使用
一旦ioremap
成功完成早期設定,我們就可以使用它。它提供兩個功能:
- early_ioremap
- early_iounmap
用於將I / O實體地址對映/取消對映到虛擬地址。兩種功能均取決於CONFIG_MMU
配置選項。記憶體管理單元是記憶體管理的特殊模組。該塊的主要目的是將實體地址轉換為虛擬地址。儲存器管理單元pgd
從cr3
控制暫存器中瞭解高階頁表地址()。如果CONFIG_MMU
options設定為n
,則early_ioremap
僅返回給定的實體地址,early_iounmap
而不執行任何操作。如果CONFIG_MMU
option設定為y
,則early_ioremap
呼叫__early_ioremap
將使用三個引數:
phys_addr
-I/O
儲存器區域的基本實體地址,以對映到虛擬地址上;size
-I/O
記憶體區域的大小;prot
-頁表入口位。
首先__early_ioremap
,我們將遍歷所有早期的ioremap fixmap插槽,並搜尋prev_map
陣列中的第一個空閒插槽。當我們找到它時,我們會記住它在slot
變數中的編號並設定大小:
- slot = -1;
- for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
- if (!prev_map[i]) {
- slot = i;
- break;
- }
- }
- ...
- ...
- ...
- prev_size[slot] = size;
- last_addr = phys_addr + size - 1;
在下一個spte中,我們可以看到以下程式碼:
- offset = phys_addr & ~PAGE_MASK;
- phys_addr &= PAGE_MASK;
- size = PAGE_ALIGN(last_addr + 1) - phys_addr;
在這裡,我們PAGE_MASK
用於清除phys_addr
除前12位之外的所有位。PAGE_MASK
巨集定義為:
#define PAGE_MASK (~(PAGE_SIZE-1))
我們知道頁面的大小是4096位元組或1000000000000
二進位制。PAGE_SIZE - 1
將是111111111111
,但使用~
,我們將獲得000000000000
,但是使用時,~PAGE_MASK
我們將111111111111
再次獲得。在第二行中,我們執行相同的操作,但是清除了前12位,並在第三行中獲得了頁面對齊的區域大小。我們獲得了對齊的區域,現在我們需要獲取新ioremap
區域所佔據的頁面數,並fixed_addresses
在接下來的步驟中計算出修正對映索引:
- nrpages = size >> PAGE_SHIFT;
- idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot;
現在,我們可以fix-mapped
使用給定的實體地址填充區域。在迴圈的每次迭代中,我們__early_set_fixmap
從arch/x86/mm/ioremap.c呼叫該函式,將給定的實體地址增加頁面大小(即4096
位元組),並更新addresses
索引和頁面數:
- while (nrpages > 0) {
- __early_set_fixmap(idx, phys_addr, prot);
- phys_addr += PAGE_SIZE;
- --idx;
- --nrpages;
- }
該__early_set_fixmap
函式使用以下命令獲取bm_pte
給定實體地址的頁表條目(儲存在中,請參見上文):
pte = early_ioremap_pte(addr);
在下一步中,early_ioremap_pte
我們使用pgprot_val
巨集檢查給定的頁面標誌,並根據給定的標誌進行呼叫set_pte
或pte_clear
:
- if (pgprot_val(flags))
- set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags));
- else
- pte_clear(&init_mm, addr, pte);
如您在上方所見,我們將FIXMAP_PAGE_IO
標記傳遞給__early_ioremap
。FIXMPA_PAGE_IO
擴充套件為:
(__PAGE_KERNEL_EXEC | _PAGE_NX)
標記,因此我們呼叫set_pte
函式來設定頁表條目,該條目的工作方式與set_pmd
PTE相同(參見上面的內容)。PTEs
在迴圈中進行所有設定後,我們現在可以看一下該__flush_tlb_one
函式的呼叫:
__flush_tlb_one(addr);
此函式在arch/x86/include/asm/tlbflush.h中定義,並根據的值進行呼叫__flush_tlb_single
或:__flush_tlb
cpu_has_invlpg
- static inline void __flush_tlb_one(unsigned long addr)
- {
- if (cpu_has_invlpg)
- __flush_tlb_single(addr);
- else
- __flush_tlb();
- }
該__flush_tlb_one
函式使TLB中的給定地址無效。如您所見,我們更新了分頁結構,但TLB
沒有通知更改,因此我們需要手動進行此操作。有兩種方法可以做到這一點。首先是更新cr3
控制暫存器,然後__flush_tlb
函式執行此操作:
native_write_cr3(__native_read_cr3());
第二種方法是使用invlpg
指令來使TLB
條目無效。讓我們看一下__flush_tlb_one
實現。如您所見,首先進行功能檢查cpu_has_invlpg
,其定義為:
如果CPU支援該invlpg
指令,我們將呼叫__flush_tlb_single
擴充套件為以下內容的巨集__native_flush_tlb_single
:
- static inline void __native_flush_tlb_single(unsigned long addr)
- {
- asm volatile("invlpg (%0)" ::"r" (addr) : "memory");
- }
或呼叫__flush_tlb
,它只會更新cr3
我們所看到的暫存器。完成此步驟後,__early_set_fixmap
功能執行完畢,我們可以返回到__early_ioremap
實現。在為給定地址設定了fixmap區域後,我們需要prev_map
使用slot
索引將I / O Re-mapped區域的基本虛擬地址儲存在以下位置:
prev_map[slot] = (void __iomem *)(offset + slot_virt[slot]);
並退回。
第二個函式early_iounmap
取消對映I/O
記憶體區域。此函式採用兩個引數:基地址和I/O
區域大小,通常看起來與十分相似early_ioremap
。它還會通過Fixmap插槽,並查詢具有給定地址的插槽。之後,它獲取fixmap插槽的索引並進行呼叫__late_clear_fixmap
或__early_set_fixmap
根據其after_paging_init
值進行選擇。它的呼叫方式__early_set_fixmap
有一個不同early_ioremap
:作為實體地址early_iounmap
傳遞zero
。最後,它將I / O儲存區的地址設定為NULL
:
prev_map[slot] = NULL;
這就是fixmaps
和有關的ioremap
。當然,這部分內容並不涵蓋的所有功能ioremap
,僅涵蓋早期的ioremap,但也包含普通的ioremap。但是在更詳細地研究之前,我們需要知道更多的事情。
Links
- apic
- vsyscall
- Intel Trusted Execution Technology
- Xen
- Real Time Clock
- e820
- Memory management unit
- TLB
- Paging
- Linux kernel memory management Part 1.
相關閱讀
《linux記憶體管理:kmap、vmap、ioremap》
《ARM SMMU原理與IOMMU技術(“VT-d” DMA、I/O虛擬化、記憶體虛擬化)》
《記憶體管理:Linux Memory Management:MMU、段、分頁、PAE、Cache、TLB》
【作者】張昺華 【出處】http://www.cnblogs.com/sky-heaven/ 【部落格園】 http://www.cnblogs.com/sky-heaven/ 【知乎】 http://www.zhihu.com/people/zhang-bing-hua 【我的作品---旋轉倒立擺】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【我的作品---自平衡自動循跡車】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【大餅教你學系列】https://edu.csdn.net/course/detail/10393 【新浪微博】 張昺華--sky 【twitter】 @sky2030_ 【微信公眾號】 張昺華 本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利.