本文主要參考《深刻分析linux內核》,配圖都來自這本書,加入了一些本身的理解。linux
頁目錄項的位定義編程
頁表項定義app
本文只會對0,1,2位解釋,0位present表示是否在內存中oop
1 /* 2 * .org new-lc,fill,這條彙編程序指令將本節的位置計數器提早至new-lc(New Location Counter)處,中間的字節被填充fill值,默認爲0 3 * .fill repeat,size,value 若是size大於8也只會取8,以size爲一組,重複repeat次,填充value值 4 * 只能在本節中往前,計數器只能增長,因此第一條將位置計數器變爲0xc000000+0x100000+0x1000,由於編譯後這些地址都是虛擬地址,因此 5 * 爲內核地址須要加上__PAGE_OFFSET,而內核前1M有特殊用途,因此從1M以後開始初始化內存 6 * swapper_pg_dir爲全局頁目錄起始地址,第0,1項是映射的pg0和pg1,共8M,第3-767項爲空,第768,769項也爲pg0和pg1,第770-1023項爲空 7 * 因此0x1000手動完成了全局頁目錄的初始化,用戶區和內核區的開始兩項的映射相同 8 */ 9 .org 0x1000 10 ENTRY(swapper_pg_dir) 11 .long 0x00102007 12 .long 0x00103007 13 .fill BOOT_USER_PGD_PTRS - 2, 4, 0 14 /* default: 766 entries */ 15 .long 0x00102007 16 .long 0x00103007 17 /* default: 254 entries */ 18 .fill BOOT_KERNEL_PGD_PTRS - 2, 4, 0 19 20 /* 21 * The page tables are initialized to only 8MB here - the final page 22 * tables are set up later depending on memory size. 23 * 後文會介紹完成對這兩個頁表的初始化 24 */ 25 .org 0x2000 26 ENTRY(pg0) 27 28 .org 0x3000 29 ENTRY(pg1) 30 31 /* 32 * empty_zero_page must immediately follow the page tables ! (The 33 * initialization loop counts until empty_zero_page) 34 * 0頁表是不容許訪問的,全是0 35 */ 36 37 .org 0x4000 38 ENTRY(empty_zero_page) 39 40 .org 0x5000 41 42 /* 43 * Real beginning of normal "text" segment 44 * 這裏開始存放內核的代碼段 45 */ 46 ENTRY(stext) 47 ENTRY(_stext) 48 49 /* 50 * This starts the data section. Note that the above is all 51 * in the text section because it has alignment requirements 52 * that we cannot fulfill any other way. 53 * 內核data段 54 */ 55 .data 56 57 ALIGN 58 59 60 /* 61 * Initialize page tables 62 * 開始初始化pg0和pg1兩組頁表,pg0- __PAGE_OFFSET就是pg0的起始物理地址0x102000 63 */ 64 movl $pg0 - __PAGE_OFFSET, %edi /* initialize page tables */ 65 movl $007, %eax 66 /* "007" doesn't mean with right to kill, but PRESENT+RW+USER 67 */ 68 2: 69 /* 將eax中的值存入edi指定的位置中,於是第一次回在pg0第0項處存入0x007,對應的頁就是起始物理地址爲0的4KB物理內存,edi自動增長4,一項爲4個byte*/ 70 stosl 71 /* eax+4K,表明下一頁*/ 72 add $0x1000, %eax 73 /* 若是edi還未到0x4000,繼續循環,即初始化了pg0和pg1兩張頁表 */ 74 cmp $empty_zero_page - __PAGE_OFFSET, %edi 75 jne 2b 76 77 /* 78 * Enable paging 79 */ 80 3: 81 movl $swapper_pg_dir - __PAGE_OFFSET, %eax 82 movl %eax, %cr3 /* set the page table pointer.. */ 83 movl %cr0, %eax 84 orl $0x80000000, %eax 85 /* 此處啓用cr0的最高位PG容許位,表示開啓分頁機制,但由於eip中存儲的仍爲內存的物理地址,因此cpu對eip中的地址經過分頁機制解析出來將是用戶區 86 * 的,若是不映射到內存開始8M位置,那兒eip解析出來的物理地址就是無效的,後面的指令就沒法執行了,而咱們須要的是按當前順序繼續執行下去,完成從 87 * 實模式到保護模式的平穩過渡,內核初始化完成以後會刪除用戶區的這兩個映射 88 * ..and set paging (PG) bit 89 */ 90 movl %eax, %cr0 91 /* flush the prefetch-queue,這是intel所建議的操做,邏輯上無做用,但能夠將流水線上其餘指令丟棄,避免亂序執行的影響 */ 92 jmp 1f 93 1: 94 /* 上面的jmp是短跳轉,EIP中仍然是物理地址,因此須要手動去完成EIP刷新,下一個1:在編譯生成的時候採用的是虛擬地址,因此跳轉到這個虛擬地址就 95 * 真正的完成了EIP的虛擬地址更新,也就完成了實模式到保護模式的過渡 96 */ 97 movl $1f, %eax 98 jmp *%eax /* make sure eip is relocated */ 99 1 : 100 /* Set up the stack pointer */ 101 lss stack_start, %esp
此時內存映射關係fetch
以後linux會根據開機加電後的BIOS探測到的物理內存信息進行內存的初始化,生成一張內存構成圖,下一節將會是這些內容。ui