关于内核页表和进程页表的一个问题
昨天回復了一封電子郵件,有朋友問個問題很有代表性,內(nèi)核初始化時會將896M前的物理頁面作一一映射,那么用戶進程分配到896M前的頁面建立用戶映射時是否要清除內(nèi)核的一一映射。
關(guān)于這個問題,我的前面的文章已經(jīng)有了解釋,但是不甚詳細,現(xiàn)在通過一個例子詳細解釋一下。實際上并不需要清除內(nèi)核的一一映射,內(nèi)核的一一映射只有內(nèi)核自己使用,而且?guī)砹撕芏嗟姆奖?#xff0c;內(nèi)核巧妙的通過一一映射快速的執(zhí)行內(nèi)核路徑,其實內(nèi)核的一一映射也只有內(nèi)核自己知道,用戶進程根本涉及不到,內(nèi)核只要管理好自己的內(nèi)存沒有什么是不可以的。頁面的頁表映射是硬件MMU的機制,而OS內(nèi)核中擁有的是內(nèi)存管理機制,二者并不沖突,它們都是獨立的機制,并且層次不同,完全可以獨立存在,比如你可以在沒有MMU的嵌入式設(shè)備上實現(xiàn)復雜的內(nèi)存管理,同時你也可以在同一個硬件MMU下實現(xiàn)不同的內(nèi)存管理,比如windows和linux的就不同,我的觀點大致總結(jié)如下(缺漏的部分前面的文章中有):在linux中內(nèi)核一般不會介入用戶的策略,它只是提供機制,向上就到系統(tǒng)調(diào)用接口為止,它沒有upcall接口;只要用戶不會訪問內(nèi)核,內(nèi)核不會隨意訪問用戶內(nèi)存就不會有沖突,用戶是難纏的,而且行為是不確定的,內(nèi)核的行為是確定的,用戶顯然不能訪問內(nèi)核內(nèi)存,但是內(nèi)核卻可以訪問用戶內(nèi)存,然而內(nèi)核十分清楚自己擁有哪些內(nèi)存,比如初始化的時候?qū)?96以下頁面作了一一映射,可是內(nèi)核不一定用得了那么多,當用戶進程需要內(nèi)存時,完全可以從還在伙伴系統(tǒng)的512M處拽一個頁面分配之并且映射到用戶空間同時保留著內(nèi)核的一一映射,這時這個512M處的頁面已經(jīng)從伙伴系統(tǒng)脫離了,內(nèi)核如果這時也需要內(nèi)存是不會被分配到該頁面的,這樣就不會有沖突,如果該頁面本來就由內(nèi)核所使用,那么它就不會在伙伴系統(tǒng)也不可能分配到用戶進程,這么來說也不會有沖突,linux的內(nèi)存管理和硬件的 MMU是兩碼事,如果說有聯(lián)系那就是映射,影射僅僅是一個紐帶和一個適配器而已。
下面我就通過一個例子來說明,當然要寫內(nèi)核模塊了,我的機器是512M內(nèi)存,少于896M,也就是全部作了一一映射。我們首先執(zhí)行以下命令得到insmod程序的一些信息,因為模塊加載是在insmod進程的上下文中:
[root@zhaoya ~]#objdump -d /sbin/insmod
...
08048ab0 <.fini>:
8048ab0: 55 push %ebp
8048ab1: 89 e5 mov %esp,%ebp
8048ab3: 53 push %ebx
...//0xcebe0000
我們看到進程地址空間0x8048ab3處是53,我們?nèi)绻苯釉L問0x8048ab3,那么得到的就是53,這是顯然的,但是我們還可以得到0x8048ab3所在頁面的內(nèi)核一一映射地址,然后訪問那個地址看是不是還是53,如果是,那么就說明用戶頁面可能存在兩份映射,接下來寫一個模塊:
#include
#include
#include
#include
#include
#include
static __init int test_init(void)
{
char * user_addr = (char *)0x8048ab3; //常規(guī)訪問這個地址
printk("%x/n",*str);
struct page *page;
int n = get_user_pages(current, current->mm, user_addr, 1, 0, 1,&page,NULL);
printk("count:%d/n",n);
if(n>0)
{
unsigned long addr = page_address(page);
printk("address:%X/n",addr);
unsigned char * p = (unsigned char *)(address + user_addr&0xfff) ;//最后加入偏移
printk("%x/n",*p); //按照內(nèi)核一一映射訪問頁面的內(nèi)核地址
}
return 0;
}
static __exit void test_exit(void)
{
return ;
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Zhaoya");
MODULE_DESCRIPTION("kernel map");
MODULE_VERSION("Ver 0.1");
我們實在沒有必要用cr3得到頁目錄,然后用二級或者三級乃至多級頁面映射的方式去得到頁面,那是非常復雜的,而且和體系結(jié)構(gòu)相關(guān),如果非要那樣做的話首先你必須熟悉你的機器,其次還要熟悉C語言,幸運的是,內(nèi)核提供了get_user_pages這個函數(shù),我們可以輕而易舉的根據(jù)虛擬地址得到頁面。結(jié)果當然顯而易見了,兩次打印都是53,如果覺得不保險可以多試幾個虛擬地址。
實際上一個頁面保持多個映射并不是稀罕事,原因還是前面說的,內(nèi)存管理和MMU管理是兩碼事。比如共享內(nèi)存頁面就可能保持多個映射。
?本文轉(zhuǎn)自 dog250 51CTO博客,原文鏈接:http://blog.51cto.com/dog250/1273945
總結(jié)
以上是生活随笔為你收集整理的关于内核页表和进程页表的一个问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 魔兽世界怀旧服魔精有什么用
- 下一篇: 番茄畅听怎么看文字