# 二、
來源:[JOS學習筆記(二)](http://blog.csdn.net/ROger__wonG/article/category/1310602)
接上篇,文件跳轉到了entry.S里面,這是kernel的入口。首先面臨這么一個問題,kernel被加載到了什么地方?
回想上篇elf文件的加載機制,以及objdump里打印出的kernel信息,可以看到,kernel的代碼段(text段)被加載到了0x100000的位置,也就是1m的位置,所以內存布局如下:
```
+------------------+ <- 0xFFFFFFFF (4GB)
| 32-bit |
| memory mapped |
| devices |
| |
/\/\/\/\/\/\/\/\/\/\
/\/\/\/\/\/\/\/\/\/\
| |
| Unused |
| |
+------------------+ <- depends on amount of RAM
| |
| |
| Extended Memory |
|------------------|
| kernnel |
+------------------+ <- 0x00100000 (1MB)
| BIOS ROM |
+------------------+ <- 0x000F0000 (960KB)
| 16-bit devices, |
| expansion ROMs |
+------------------+ <- 0x000C0000 (768KB)
| VGA Display |
+------------------+ <- 0x000A0000 (640KB)
| |
| Low Memory |
|------------------| <-0x00010000 (elf herader here!)
|------------------| <-0x00007c00 (boot loader here!)
| bootmain stack |
+------------------+ <- 0x00000000
```
值得注意的是kernel的VMA地址為0xf0100000,也就是內核“認為”自己是在一個高位內存里執行的,因此其中的符號,包括函數名、匯編里定義的符號,都會指向一個高位的地址(大于0xf0000000)的地址,所以到目前為止在entry.S里,只要調用任何和“符號”相關的操作,比如jmp指令等,均會出錯,因為高位地址連是否存在都不知道,更不用說里面是否有內容了(qemu默認是256m內存吧?)。
因此,為了使代碼正常工作,需要對內存地址進行一定的映射。
上篇文章也分析了,JOS開啟的8086分段機制實際上只是一個幌子,根本沒有對空間進行任何的映射,所以進行映射的工作一定要由分頁機制來完成。但是為了使開啟分頁之前的代碼能正常工作,在entry.S里面定義了宏reloc。這個宏就是將一個地址減去一個kernelbase,kernelbase定義在memlayout里面,可以看到是0xf0000000,就目前來說,任何一個高位地址減去此值,就能得到實際在物理內存中的地址了。
在定義了此宏之后,entry.S首先對entry符號和pgdir符號(也就是頁表目錄)地址做了重定向, 接著通過更改cr3寄存器里的某些位,開啟分頁內存轉換。
我們跟一下pgdir里的內容,也就是entrypgdir.c里的內容,發現對于內存做了兩塊映射,首先是將虛擬內存的0--4M映射到物理內存的0--4M,其次是將從0xf000000之后的4M映射到物理內存的0--4M。前者的映射是為了保證低于4M的物理地址還能正確的訪問(開啟分頁后所有的內存地址均會做變換,即使你不想讓它變換),后面的映射是為了kernel能正常的工作。
關于頁目錄、頁表不再詳細說明,畢竟這是LAB2的主要內容。
之后從低地址跳轉到高地址relocate處,然后初始化內核調用堆棧(調用函數都需要堆棧,所以在進入kernel的c語言代碼前需要先給它初始化好堆棧,突然覺得在做用戶態編程的時候不要考慮這些內存分布、棧啥的實在是太幸福了),之后就跳轉到i386_init,轉入c語言代碼了。
為什么要從低地址跳轉到relocate處?個人認為只是想驗證之前開啟的分頁機制、加載的頁表是否有錯誤而已,如果不跳轉,直接執行貌似也可,反正call i386_init得時候也就跳轉到高地址了。
另外不要被memlayout.h里面的那個內存分布所迷惑。目前進入操作系統后的調用棧位于代碼的data段(就在entry.S文件的下面定義),而從objdump取得的信息來看,這個data段是加載到了物理內存的0x0010800,大概在內核代碼段上面一點,虛擬內存的位置為0xf010800,也在內核代碼段上面的位置,memlayout.h給的貌似是lab2以及以后的虛擬內存分布方式。
之后就跳轉到c代碼執行,一切都變的簡單起來了。