內(nèi)核地址空間分布
直接映射區(qū):線性空間中從3G開(kāi)始最大896M的區(qū)間,為直接內(nèi)存映射區(qū),該區(qū)域的線性地址和物理地址存在線性轉(zhuǎn)換關(guān)系:線性地址=3G+物理地址。
動(dòng)態(tài)內(nèi)存映射區(qū):該區(qū)域由內(nèi)核函數(shù)VMALLOC來(lái)分配,特點(diǎn)是:線性空間連續(xù),但是對(duì)應(yīng)的物理空間不一定連續(xù)。vmalloc分配的線性地址所對(duì)應(yīng)的物理頁(yè)可能處于低端內(nèi)存,也可能處于高端內(nèi)存。
永久內(nèi)存映射區(qū):該區(qū)域可訪問(wèn)高端內(nèi)存。訪問(wèn)方法是使用alloc_page(_GFP_HIGHMEM)分配高端內(nèi)存頁(yè)或者使用kmap函數(shù)將分配到的高端內(nèi)存映射到該區(qū)域。
固定映射區(qū):該區(qū)域和4G的頂端只有4k的隔離帶,其每個(gè)地址項(xiàng)都服務(wù)于特定的用途,如ACPI_BASE等。
進(jìn)程的地址空間
linux采用虛擬內(nèi)存管理技術(shù),每一個(gè)進(jìn)程都有一個(gè)3G大小的獨(dú)立的進(jìn)程地址空間,這個(gè)地址空間就是用戶空間。每個(gè)進(jìn)程的用戶空間都是完全獨(dú)立、互不相干的。進(jìn)程訪問(wèn)內(nèi)核空間的方式:系統(tǒng)調(diào)用和中斷。
創(chuàng)建進(jìn)程等進(jìn)程相關(guān)操作都需要分配內(nèi)存給進(jìn)程。這時(shí)進(jìn)程申請(qǐng)和獲得的不是物理地址,僅僅是虛擬地址。
實(shí)際的物理內(nèi)存只有當(dāng)進(jìn)程真的去訪問(wèn)新獲取的虛擬地址時(shí),才會(huì)由“請(qǐng)頁(yè)機(jī)制”產(chǎn)生“缺頁(yè)”異常,從而進(jìn)入分配實(shí)際頁(yè)框的程序。該異常是虛擬內(nèi)存機(jī)制賴以存在的基本保證,它會(huì)告訴內(nèi)核去為進(jìn)程分配物理頁(yè),并建立對(duì)應(yīng)的頁(yè)表,這之后虛擬地址才實(shí)實(shí)在在的映射到了物理地址上。
vmalloc和kmalloc區(qū)別
1,kmalloc對(duì)應(yīng)于kfree,分配的內(nèi)存處于3GB~high_memory之間,這段內(nèi)核空間與物理內(nèi)存的映射一一對(duì)應(yīng),可以分配連續(xù)的物理內(nèi)存; vmalloc對(duì)應(yīng)于vfree,分配的內(nèi)存在VMALLOC_START~4GB之間,分配連續(xù)的虛擬內(nèi)存,但是物理上不一定連續(xù)。
2,vmalloc() 分配的物理地址無(wú)需連續(xù),而kmalloc() 確保頁(yè)在物理上是連續(xù)的
3,kmalloc分配內(nèi)存是基于slab,因此slab的一些特性包括著色,對(duì)齊等都具備,性能較好。物理地址和邏輯地址都是連續(xù)的。
4,最主要的區(qū)別是分配大小的問(wèn)題,比如你需要28個(gè)字節(jié),那一定用kmalloc,如果用vmalloc,分配不多次機(jī)器就罷工了。
盡管僅僅在某些情況下才需要物理上連續(xù)的內(nèi)存塊,但是,很多內(nèi)核代碼都調(diào)用kmalloc(),而不是用vmalloc()獲得內(nèi)存。這主要是出于性能的考慮。vmalloc()函數(shù)為了把物理上不連續(xù)的頁(yè)面轉(zhuǎn)換為虛擬地址空間上連續(xù)的頁(yè),必須專門建立頁(yè)表項(xiàng)。還有,通過(guò) vmalloc()獲得的頁(yè)必須一個(gè)一個(gè)的進(jìn)行映射(因?yàn)樗鼈兾锢砩喜皇沁B續(xù)的),這就會(huì)導(dǎo)致比直接內(nèi)存映射大得多的緩沖區(qū)刷新。因?yàn)檫@些原因,vmalloc()僅在絕對(duì)必要時(shí)才會(huì)使用,最典型的就是為了獲得大塊內(nèi)存時(shí),例如,當(dāng)模塊被動(dòng)態(tài)插入到內(nèi)核中時(shí),就把模塊裝載到由vmalloc()分配的內(nèi)存上。
進(jìn)程地址空間
前邊我已經(jīng)說(shuō)過(guò)了內(nèi)核是如何管理物理內(nèi)存。但事實(shí)是內(nèi)核是操作系統(tǒng)的核心,不光管理本身的內(nèi)存,還要管理進(jìn)程的地址空間。linux操作系統(tǒng)采用虛擬內(nèi)存技術(shù),所有進(jìn)程之間以虛擬方式共享內(nèi)存。進(jìn)程地址空間由每個(gè)進(jìn)程中的線性地址區(qū)組成,而且更為重要的特點(diǎn)是內(nèi)核允許進(jìn)程使用該空間中的地址。通常情況況下,每個(gè)進(jìn)程都有唯一的地址空間,而且進(jìn)程地址空間之間彼此互不相干。但是進(jìn)程之間也可以選擇共享地址空間,這樣的進(jìn)程就叫做線程。
內(nèi)核使用內(nèi)存描述符結(jié)構(gòu)表示進(jìn)程的地址空間,由結(jié)構(gòu)體mm_struct結(jié)構(gòu)體表示,定義在linux/sched.h中,如下:
struct mm_struct { struct vm_area_struct *mmap; /* list of memory areas */ struct rb_root mm_rb; /* red-black tree of VMAs */ struct vm_area_struct *mmap_cache; /* last used memory area */ unsigned long free_area_cache; /* 1st address space hole */ pgd_t *pgd; /* page global directory */ atomic_t mm_users; /* address space users */ atomic_t mm_count; /* primary usage counter */ int map_count; /* number of memory areas */ struct rw_semaphore mmap_sem; /* memory area semaphore */ spinlock_t page_table_lock; /* page table lock */ struct list_head mmlist; /* list of all mm_structs */ unsigned long start_code; /* start address of code */ unsigned long end_code; /* final address of code */ unsigned long start_data; /* start address of data */ unsigned long end_data; /* final address of data */ unsigned long start_brk; /* start address of heap */ unsigned long brk; /* final address of heap */ unsigned long start_stack; /* start address of stack */ unsigned long arg_start; /* start of arguments */ unsigned long arg_end; /* end of arguments */ unsigned long env_start; /* start of environment */ unsigned long env_end; /* end of environment */ unsigned long rss; /* pages allocated */ unsigned long total_vm; /* total number of pages */ unsigned long locked_vm; /* number of locked pages */ unsigned long def_flags; /* default access flags */ unsigned long cpu_vm_mask; /* lazy TLB switch mask */ unsigned long swap_address; /* last scanned address */ unsigned dumpable:1; /* can this mm core dump? */ int used_hugetlb; /* used hugetlb pages? */ mm_context_t context; /* arch-specific data */ int core_waiters; /* thread core dump waiters */ struct completion *core_startup_done; /* core start completion */ struct completion core_done; /* core end completion */ rwlock_t ioctx_list_lock; /* AIO I/O list lock */ struct kioctx *ioctx_list; /* AIO I/O list */ struct kioctx default_kioctx; /* AIO default I/O context */ };
mm_users記錄了正在使用該地址的進(jìn)程數(shù)目(比如有兩個(gè)進(jìn)程在使用,那就為2)。mm_count是該結(jié)構(gòu)的主引用計(jì)數(shù),只要mm_users不為0,它就為1。但其為0時(shí),后者就為0。這時(shí)也就說(shuō)明再也沒(méi)有指向該mm_struct結(jié)構(gòu)體的引用了,這時(shí)該結(jié)構(gòu)體會(huì)被銷毀。內(nèi)核之所以同時(shí)使用這兩個(gè)計(jì)數(shù)器是為了區(qū)別主使用計(jì)數(shù)器和使用該地址空間的進(jìn)程的數(shù)目。mmap和mm_rb描述的都是同一個(gè)對(duì)象:該地址空間中的全部?jī)?nèi)存區(qū)域。不同只是前者以鏈表,后者以紅黑樹(shù)的形式組織。所有的mm_struct結(jié)構(gòu)體都通過(guò)自身的mmlist域連接在一個(gè)雙向鏈表中,該鏈表的首元素是init_mm內(nèi)存描述符,它代表init進(jìn)程的地址空間。另外需要注意,操作該鏈表的時(shí)候需要使用mmlist_lock鎖來(lái)防止并發(fā)訪問(wèn),該鎖定義在文件kernel/fork.c中。內(nèi)存描述符的總數(shù)在mmlist_nr全局變量中,該變量也定義在文件fork.c中。
我前邊說(shuō)過(guò)的進(jìn)程描述符中有一個(gè)mm域,這里邊存放的就是該進(jìn)程使用的內(nèi)存描述符,通過(guò)current->mm便可以指向當(dāng)前進(jìn)程的內(nèi)存描述符。fork函數(shù)利用copy_mm()函數(shù)就實(shí)現(xiàn)了復(fù)制父進(jìn)程的內(nèi)存描述符,而子進(jìn)程中的mm_struct結(jié)構(gòu)體實(shí)際是通過(guò)文件kernel/fork.c中的allocate_mm()宏從mm_cachep slab緩存中分配得到的。通常,每個(gè)進(jìn)程都有唯一的mm_struct結(jié)構(gòu)體。
前邊也說(shuō)過(guò),在linux中,進(jìn)程和線程其實(shí)是一樣的,唯一的不同點(diǎn)就是是否共享這里的地址空間。這個(gè)可以通過(guò)CLONE_VM標(biāo)志來(lái)實(shí)現(xiàn)。linux內(nèi)核并不區(qū)別對(duì)待它們,線程對(duì)內(nèi)核來(lái)說(shuō)僅僅是一個(gè)共向特定資源的進(jìn)程而已。好了,如果你設(shè)置這個(gè)標(biāo)志了,似乎很多問(wèn)題都解決了。不再要allocate_mm函數(shù)了,前邊剛說(shuō)作用。而且在copy_mm()函數(shù)中將mm域指向其父進(jìn)程的內(nèi)存描述符就可以了,如下:
if (clone_flags & CLONE_VM) { /* * current is the parent process and * tsk is the child process during a fork() */ atomic_inc(¤t->mm->mm_users); tsk->mm = current->mm; }
最后,當(dāng)進(jìn)程退出的時(shí)候,內(nèi)核調(diào)用exit_mm()函數(shù),這個(gè)函數(shù)調(diào)用mmput()來(lái)減少內(nèi)存描述符中的mm_users用戶計(jì)數(shù)。如果計(jì)數(shù)降為0,繼續(xù)調(diào)用mmdrop函數(shù),減少mm_count使用計(jì)數(shù)。如果使用計(jì)數(shù)也為0,則調(diào)用free_mm()宏通過(guò)kmem_cache_free()函數(shù)將mm_struct結(jié)構(gòu)體歸還到mm_cachep slab緩存中。
但對(duì)于內(nèi)核而言,內(nèi)核線程沒(méi)有進(jìn)程地址空間,也沒(méi)有相關(guān)的內(nèi)存描述符,內(nèi)核線程對(duì)應(yīng)的進(jìn)程描述符中mm域也為空。但內(nèi)核線程還是需要使用一些數(shù)據(jù)的,比如頁(yè)表,為了避免內(nèi)核線程為內(nèi)存描述符和頁(yè)表浪費(fèi)內(nèi)存,也為了當(dāng)新內(nèi)核線程運(yùn)行時(shí),避免浪費(fèi)處理器周期向新地址空間進(jìn)行切換,內(nèi)核線程將直接使用前一個(gè)進(jìn)程的內(nèi)存描述符。回憶一下我剛說(shuō)的進(jìn)程調(diào)度問(wèn)題,當(dāng)一個(gè)進(jìn)程被調(diào)度時(shí),進(jìn)程結(jié)構(gòu)體中mm域指向的地址空間會(huì)被裝載到內(nèi)存,進(jìn)程描述符中的active_mm域會(huì)被更新,指向新的地址空間。但我們這里的內(nèi)核是沒(méi)有mm域(為空),所以,當(dāng)一個(gè)內(nèi)核線程被調(diào)度時(shí),內(nèi)核發(fā)現(xiàn)它的mm域?yàn)镹ULL,就會(huì)保留前一個(gè)進(jìn)程的地址空間,隨后內(nèi)核更新內(nèi)核線程對(duì)應(yīng)的進(jìn)程描述符中的active域,使其指向前一個(gè)進(jìn)程的內(nèi)存描述符。所以在需要的時(shí)候,內(nèi)核線程便可以使用前一個(gè)進(jìn)程的頁(yè)表。因?yàn)閮?nèi)核線程不妨問(wèn)用戶空間的內(nèi)存,所以它們僅僅使用地址空間中和內(nèi)核內(nèi)存相關(guān)的信息,這些信息的含義和普通進(jìn)程完全相同。
內(nèi)存區(qū)域由vm_area_struct結(jié)構(gòu)體描述,定義在linux/mm.h中,內(nèi)存區(qū)域在內(nèi)核中也經(jīng)常被稱作虛擬內(nèi)存區(qū)域或VMA.它描述了指定地址空間內(nèi)連續(xù)區(qū)間上的一個(gè)獨(dú)立內(nèi)存范圍。內(nèi)核將每個(gè)內(nèi)存區(qū)域作為一個(gè)單獨(dú)的內(nèi)存對(duì)象管理,每個(gè)內(nèi)存區(qū)域都擁有一致的屬性。結(jié)構(gòu)體如下:
struct vm_area_struct { struct mm_struct *vm_mm; /* associated mm_struct */ unsigned long vm_start; /* VMA start, inclusive */ unsigned long vm_end; /* VMA end , exclusive */ struct vm_area_struct *vm_next; /* list of VMA's */ pgprot_t vm_page_prot; /* access permissions */ unsigned long vm_flags; /* flags */ struct rb_node vm_rb; /* VMA's node in the tree */ union { /* links to address_space->i_mmap or i_mmap_nonlinear */ struct { struct list_head list; void *parent; struct vm_area_struct *head; } vm_set; struct prio_tree_node prio_tree_node; } shared; struct list_head anon_vma_node; /* anon_vma entry */ struct anon_vma *anon_vma; /* anonymous VMA object */ struct vm_operations_struct *vm_ops; /* associated ops */ unsigned long vm_pgoff; /* offset within file */ struct file *vm_file; /* mApped file, if any */ void *vm_private_data; /* private data */ };
每個(gè)內(nèi)存描述符都對(duì)應(yīng)于地址進(jìn)程空間中的唯一區(qū)間。vm_mm域指向和VMA相關(guān)的mm_struct結(jié)構(gòu)體。兩個(gè)獨(dú)立的進(jìn)程將同一個(gè)文件映射到各自的地址空間,它們分別都會(huì)有一個(gè)vm_area_struct結(jié)構(gòu)體來(lái)標(biāo)志自己的內(nèi)存區(qū)域;但是如果兩個(gè)線程共享一個(gè)地址空間,那么它們也同時(shí)共享其中的所有vm_area_struct結(jié)構(gòu)體。
在上面的vm_flags域中存放的是VMA標(biāo)志,標(biāo)志了內(nèi)存區(qū)域所包含的頁(yè)面的行為和信息,反映了內(nèi)核處理頁(yè)面所需要遵循的行為準(zhǔn)則,如下表下述:
上表已經(jīng)相當(dāng)詳細(xì)了,而且給出了說(shuō)明,我就不說(shuō)了。在vm_area_struct結(jié)構(gòu)體中的vm_ops域指向域指定內(nèi)存區(qū)域相關(guān)的操作函數(shù)表,內(nèi)核使用表中的方法操作VMA。vm_area_struct作為通用對(duì)象代表了任何類型的內(nèi)存區(qū)域,而操作表描述針對(duì)特定的對(duì)象實(shí)例的特定方法。操作函數(shù)表由vm_operations_struct結(jié)構(gòu)體表示,定義在linux/mm.h中,如下:
struct vm_operations_struct { void (*open) (struct vm_area_struct *); void (*close) (struct vm_area_struct *); struct page * (*nopage) (struct vm_area_struct *, unsigned long, int); int (*populate) (struct vm_area_struct *, unsigned long, unsigned long,pgprot_t, unsigned long, int); };
open:當(dāng)指定的內(nèi)存區(qū)域被加入到一個(gè)地址空間時(shí),該函數(shù)被調(diào)用。 close:當(dāng)指定的內(nèi)存區(qū)域從地址空間刪除時(shí),該函數(shù)被調(diào)用。 nopages:當(dāng)要訪問(wèn)的頁(yè)不在物理內(nèi)存中時(shí),該函數(shù)被頁(yè)錯(cuò)誤處理程序調(diào)用。 populate:該函數(shù)被系統(tǒng)調(diào)用remap_pages調(diào)用來(lái)為將要發(fā)生的缺頁(yè)中斷預(yù)映射一個(gè)新映射。
記性好的你一定記得內(nèi)存描述符中的mmap和mm_rb域都獨(dú)立地指向與內(nèi)存描述符相關(guān)的全體內(nèi)存區(qū)域?qū)ο蟆K鼈儼耆嗤膙m_area_struct結(jié)構(gòu)體的指針,僅僅組織方式不同而已。前者以鏈表的方式進(jìn)行組織,所有的區(qū)域按地址增長(zhǎng)的方向排序,mmap域指向鏈表中第一個(gè)內(nèi)存區(qū)域,鏈中最后一個(gè)VMA結(jié)構(gòu)體指針指向空。而mm_rb域采用紅--黑樹(shù)連接所有的內(nèi)存區(qū)域?qū)ο蟆K赶蚣t--黑輸?shù)母?jié)點(diǎn)。地址空間中每一個(gè)vm_area_struct結(jié)構(gòu)體通過(guò)自身的vm_rb域連接到樹(shù)中。關(guān)于紅黑二叉樹(shù)結(jié)構(gòu)我就不細(xì)講了,以后可能會(huì)詳細(xì)說(shuō)這個(gè)問(wèn)題。內(nèi)核之所以采用這兩種結(jié)構(gòu)來(lái)表示同一內(nèi)存區(qū)域,主要是鏈表結(jié)構(gòu)便于遍歷所有節(jié)點(diǎn),而紅黑樹(shù)結(jié)構(gòu)體便于在地址空間中定位特定內(nèi)存區(qū)域的節(jié)點(diǎn)。我么可以使用/proc文件系統(tǒng)和pmap工具查看給定進(jìn)程的內(nèi)存空間和其中所包含的內(nèi)存區(qū)域。這里就不細(xì)說(shuō)了。
內(nèi)核也為我們提供了對(duì)內(nèi)存區(qū)域操作的API,定義在linux/mm.h中:
記性好的你一定記得內(nèi)存描述符中的mmap和mm_rb域都獨(dú)立地指向與內(nèi)存描述符相關(guān)的全體內(nèi)存區(qū)域?qū)ο蟆K鼈儼耆嗤膙m_area_struct結(jié)構(gòu)體的指針,僅僅組織方式不同而已。前者以鏈表的方式進(jìn)行組織,所有的區(qū)域按地址增長(zhǎng)的方向排序,mmap域指向鏈表中第一個(gè)內(nèi)存區(qū)域,鏈中最后一個(gè)VMA結(jié)構(gòu)體指針指向空。而mm_rb域采用紅--黑樹(shù)連接所有的內(nèi)存區(qū)域?qū)ο蟆K赶蚣t--黑輸?shù)母?jié)點(diǎn)。地址空間中每一個(gè)vm_area_struct結(jié)構(gòu)體通過(guò)自身的vm_rb域連接到樹(shù)中。關(guān)于紅黑二叉樹(shù)結(jié)構(gòu)我就不細(xì)講了,以后可能會(huì)詳細(xì)說(shuō)這個(gè)問(wèn)題。內(nèi)核之所以采用這兩種結(jié)構(gòu)來(lái)表示同一內(nèi)存區(qū)域,主要是鏈表結(jié)構(gòu)便于遍歷所有節(jié)點(diǎn),而紅黑樹(shù)結(jié)構(gòu)體便于在地址空間中定位特定內(nèi)存區(qū)域的節(jié)點(diǎn)。我么可以使用/proc文件系統(tǒng)和pmap工具查看給定進(jìn)程的內(nèi)存空間和其中所包含的內(nèi)存區(qū)域。這里就不細(xì)說(shuō)了。 內(nèi)核也為我們提供了對(duì)內(nèi)存區(qū)域操作的API,定義在linux/mm.h中:
接下來(lái)要說(shuō)的兩個(gè)函數(shù)就非常重要了,它們負(fù)責(zé)創(chuàng)建和刪除地址空間。
內(nèi)核使用do_mmap()函數(shù)創(chuàng)建一個(gè)新的線性地址空間。但如果創(chuàng)建的地址區(qū)間和一個(gè)已經(jīng)存在的地址區(qū)間相鄰,并且它們具有相同的訪問(wèn)權(quán)限的話,那么兩個(gè)區(qū)間將合并為一個(gè)。如果不能合并,那么就確實(shí)需要?jiǎng)?chuàng)建一個(gè)新的vma了,但無(wú)論哪種情況,do_mmap()函數(shù)都會(huì)將一個(gè)地址區(qū)間加入到進(jìn)程的地址空間中。這個(gè)函數(shù)定義在linux/mm.h中,如下:
unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot,unsigned long flag, unsigned long offset)
這個(gè)函數(shù)中由file指定文件,具體映射的是文件中從偏移offset處開(kāi)始,長(zhǎng)度為len字節(jié)的范圍內(nèi)的數(shù)據(jù),如果file參數(shù)是NULL并且offset參數(shù)也是0,那么就代表這次映射沒(méi)有和文件相關(guān),該情況被稱作匿名映射。如果指定了文件和偏移量,那么該映射被稱為文件映射(file-backed mapping),其中參數(shù)prot指定內(nèi)存區(qū)域中頁(yè)面的訪問(wèn)權(quán)限,這些訪問(wèn)權(quán)限定義在asm/mman.h中,如下:
flag參數(shù)指定了VMA標(biāo)志,這些標(biāo)志定義在asm/mman.h中,如下:
如果系統(tǒng)調(diào)用do_mmap的參數(shù)中有無(wú)效參數(shù),那么它返回一個(gè)負(fù)值;否則,它會(huì)在虛擬內(nèi)存中分配一個(gè)合適的新內(nèi)存區(qū)域,如果有可能的話,將新區(qū)域和臨近區(qū)域進(jìn)行合并,否則內(nèi)核從vm_area_cach
ep長(zhǎng)字節(jié)緩存中分配一個(gè)vm_area_struct結(jié)構(gòu)體,并且使用vma_link()函數(shù)將新分配的內(nèi)存區(qū)域添加到地址空間的內(nèi)存區(qū)域鏈表和紅黑樹(shù)中,隨后還要更新內(nèi)存描述符中的total_vm域,然后才返回新分配的地址區(qū)間的初始地址。在用戶空間,我們可以通過(guò)mmap()系統(tǒng)調(diào)用獲取內(nèi)核函數(shù)do_mmap()的功能,這個(gè)在unix環(huán)境高級(jí)編程中講的很詳細(xì),我就不好意思繼續(xù)說(shuō)了。我們繼續(xù)往下走。
我們說(shuō)既然有了創(chuàng)建,當(dāng)然要有刪除了,是不?do_mummp()函數(shù)就是干這事的。它從特定的進(jìn)程地址空間中刪除指定地址空間,該函數(shù)定義在文件linux/mm.h中,如下:
int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
第一個(gè)參數(shù)指定要?jiǎng)h除區(qū)域所在的地址空間,刪除從地址start開(kāi)始,長(zhǎng)度為len字節(jié)的地址空間,如果成功,返回0,否則返回負(fù)的錯(cuò)誤碼。與之相對(duì)應(yīng)的用戶空間系統(tǒng)調(diào)用是munmap。
下面開(kāi)始最后一點(diǎn)內(nèi)容:頁(yè)表
我們知道應(yīng)用程序操作的對(duì)象是映射到物理內(nèi)存之上的虛擬內(nèi)存,但是處理器直接操作的確實(shí)物理內(nèi)存。所以當(dāng)應(yīng)用程序訪問(wèn)一個(gè)虛擬地址時(shí),首先必須將虛擬地址轉(zhuǎn)化為物理地址,然后處理器才能解析地址訪問(wèn)請(qǐng)求。這個(gè)轉(zhuǎn)換工作需要通過(guò)查詢頁(yè)面才能完成,概括地講,地址轉(zhuǎn)換需要將虛擬地址分段,使每段虛地址都作為一個(gè)索引指向頁(yè)表,而頁(yè)表項(xiàng)則指向下一級(jí)別的頁(yè)表或者指向最終的物理頁(yè)面。linux中使用三級(jí)頁(yè)表完成地址轉(zhuǎn)換。多數(shù)體系結(jié)構(gòu)中,搜索頁(yè)表的工作由硬件完成,下表描述了虛擬地址通過(guò)頁(yè)表找到物理地址的過(guò)程:
在上面這個(gè)圖中,頂級(jí)頁(yè)表是頁(yè)全局目錄(PGD),二級(jí)頁(yè)表是中間頁(yè)目錄(PMD).最后一級(jí)是頁(yè)表(PTE),該頁(yè)表結(jié)構(gòu)指向物理頁(yè)。上圖中的頁(yè)表對(duì)應(yīng)的結(jié)構(gòu)體定義在文件asm/page.h中。為了加快查找速度,在linux中實(shí)現(xiàn)了快表(TLB),其本質(zhì)是一個(gè)緩沖器,作為一個(gè)將虛擬地址映射到物理地址的硬件緩存,當(dāng)請(qǐng)求訪問(wèn)一個(gè)虛擬地址時(shí),處理器將首先檢查TLB中是否緩存了該虛擬地址到物理地址的映射,如果找到了,物理地址就立刻返回,否則,就需要再通過(guò)頁(yè)表搜索需要的物理地址。