天天看點

關于核心頁表和程序頁表的一個問題

昨天回複了一封電子郵件,有朋友問個問題很有代表性,核心初始化時會将896M前的實體頁面作一一映射,那麼使用者程序配置設定到896M前的頁面建立使用者映射時是否要清除核心的一一映射。

關于這個問題,我的前面的文章已經有了解釋,但是不甚詳細,現在通過一個例子詳細解釋一下。實際上并不需要清除核心的一一映射,核心的一一映射隻有核心自己使用,而且帶來了很多的友善,核心巧妙的通過一一映射快速的執行核心路徑,其實核心的一一映射也隻有核心自己知道,使用者程序根本涉及不到,核心隻要管理好自己的記憶體沒有什麼是不可以的。頁面的頁表映射是硬體MMU的機制,而OS核心中擁有的是記憶體管理機制,二者并不沖突,它們都是獨立的機制,并且層次不同,完全可以獨立存在,比如你可以在沒有MMU的嵌入式裝置上實作複雜的記憶體管理,同時你也可以在同一個硬體MMU下實作不同的記憶體管理,比如windows和linux的就不同,我的觀點大緻總結如下(缺漏的部分前面的文章中有):在linux中核心一般不會介入使用者的政策,它隻是提供機制,向上就到系統調用接口為止,它沒有upcall接口;隻要使用者不會通路核心,核心不會随意通路使用者記憶體就不會有沖突,使用者是難纏的,而且行為是不确定的,核心的行為是确定的,使用者顯然不能通路核心記憶體,但是核心卻可以通路使用者記憶體,然而核心十厘清楚自己擁有哪些記憶體,比如初始化的時候将896以下頁面作了一一映射,可是核心不一定用得了那麼多,當使用者程序需要記憶體時,完全可以從還在夥伴系統的512M處拽一個頁面配置設定之并且映射到使用者空間同時保留着核心的一一映射,這時這個512M處的頁面已經從夥伴系統脫離了,核心如果這時也需要記憶體是不會被配置設定到該頁面的,這樣就不會有沖突,如果該頁面本來就由核心所使用,那麼它就不會在夥伴系統也不可能配置設定到使用者程序,這麼來說也不會有沖突,linux的記憶體管理和硬體的 MMU是兩碼事,如果說有聯系那就是映射,影射僅僅是一個紐帶和一個擴充卡而已。

下面我就通過一個例子來說明,當然要寫核心子產品了,我的機器是512M記憶體,少于896M,也就是全部作了一一映射。我們首先執行以下指令得到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,我們如果直接通路0x8048ab3,那麼得到的就是53,這是顯然的,但是我們還可以得到0x8048ab3所在頁面的核心一一映射位址,然後通路那個位址看是不是還是53,如果是,那麼就說明使用者頁面可能存在兩份映射,接下來寫一個子產品:

#include

static __init int test_init(void)

{

char * user_addr = (char *)0x8048ab3; //正常通路這個位址

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); //按照核心一一映射通路頁面的核心位址

}

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得到頁目錄,然後用二級或者三級乃至多級頁面映射的方式去得到頁面,那是非常複雜的,而且和體系結構相關,如果非要那樣做的話首先你必須熟悉你的機器,其次還要熟悉C語言,幸運的是,核心提供了get_user_pages這個函數,我們可以輕而易舉的根據虛拟位址得到頁面。結果當然顯而易見了,兩次列印都是53,如果覺得不保險可以多試幾個虛拟位址。

實際上一個頁面保持多個映射并不是稀罕事,原因還是前面說的,記憶體管理和MMU管理是兩碼事。比如共享記憶體頁面就可能保持多個映射。

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1273945

繼續閱讀