日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

MIT JOS学习笔记01:环境配置、Boot Loader(2016.10.22)

發(fā)布時(shí)間:2024/4/14 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MIT JOS学习笔记01:环境配置、Boot Loader(2016.10.22) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

未經(jīng)許可謝絕以任何形式對(duì)本文內(nèi)容進(jìn)行轉(zhuǎn)載!

一、環(huán)境配置

  關(guān)于MIT課程中使用的JOS的配置教程網(wǎng)上已經(jīng)有很多了,在這里就不做介紹,個(gè)人使用的是Ubuntu 16.04 + qemu。另注,本文章中貼出的代碼均是JOS中未經(jīng)修改的源代碼,其中有一些細(xì)節(jié)是MIT課程中要求學(xué)生自己實(shí)現(xiàn)的。

二、Boot Loader代碼分析

  1.boot.S(AT&T匯編格式)

1 #include <inc/mmu.h> 2 3 # Start the CPU: switch to 32-bit protected mode, jump into C. 4 # The BIOS loads this code from the first sector of the hard disk into 5 # memory at physical address 0x7c00 and starts executing in real mode 6 # with %cs=0 %ip=7c00. 7 8 .set PROT_MODE_CSEG, 0x8 # kernel code segment selector 9 .set PROT_MODE_DSEG, 0x10 # kernel data segment selector 10 .set CR0_PE_ON, 0x1 # protected mode enable flag 11 12 .globl start 13 start: 14 .code16 # Assemble for 16-bit mode 15 cli # Disable interrupts 16 cld # String operations increment 17 18 # Set up the important data segment registers (DS, ES, SS). 19 xorw %ax,%ax # Segment number zero 20 movw %ax,%ds # -> Data Segment 21 movw %ax,%es # -> Extra Segment 22 movw %ax,%ss # -> Stack Segment 23 24 # Enable A20: 25 # For backwards compatibility with the earliest PCs, physical 26 # address line 20 is tied low, so that addresses higher than 27 # 1MB wrap around to zero by default. This code undoes this. 28 seta20.1: 29 inb $0x64,%al # Wait for not busy 30 testb $0x2,%al 31 jnz seta20.1 32 33 movb $0xd1,%al # 0xd1 -> port 0x64 34 outb %al,$0x64 35 36 seta20.2: 37 inb $0x64,%al # Wait for not busy 38 testb $0x2,%al 39 jnz seta20.2 40 41 movb $0xdf,%al # 0xdf -> port 0x60 42 outb %al,$0x60 43 44 # Switch from real to protected mode, using a bootstrap GDT 45 # and segment translation that makes virtual addresses 46 # identical to their physical addresses, so that the 47 # effective memory map does not change during the switch. 48 lgdt gdtdesc 49 movl %cr0, %eax 50 orl $CR0_PE_ON, %eax 51 movl %eax, %cr0 52 53 # Jump to next instruction, but in 32-bit code segment. 54 # Switches processor into 32-bit mode. 55 ljmp $PROT_MODE_CSEG, $protcseg 56 57 .code32 # Assemble for 32-bit mode 58 protcseg: 59 # Set up the protected-mode data segment registers 60 movw $PROT_MODE_DSEG, %ax # Our data segment selector 61 movw %ax, %ds # -> DS: Data Segment 62 movw %ax, %es # -> ES: Extra Segment 63 movw %ax, %fs # -> FS 64 movw %ax, %gs # -> GS 65 movw %ax, %ss # -> SS: Stack Segment 66 67 # Set up the stack pointer and call into C. 68 movl $start, %esp 69 call bootmain 70 71 # If bootmain returns (it shouldn't), loop. 72 spin: 73 jmp spin 74 75 # Bootstrap GDT 76 .p2align 2 # force 4 byte alignment 77 gdt: 78 SEG_NULL # null seg 79 SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg 80 SEG(STA_W, 0x0, 0xffffffff) # data seg 81 82 gdtdesc: 83 .word 0x17 # sizeof(gdt) - 1 84 .long gdt # address gdt boot.S

?  boot.S的代碼如上所示,這部分代碼的作用是將處理器從實(shí)模式切換到保護(hù)模式,然后再進(jìn)行后續(xù)的加載內(nèi)核程序的操作。為什么要讓處理器切換到保護(hù)模式下工作?這就要從PC物理內(nèi)存的分布來(lái)考慮,以現(xiàn)在4GB的內(nèi)存為例,PC物理內(nèi)存的分布大致可以用以下的圖來(lái)表示:

在早期16bits的8088處理器上,地址總線為20位,可尋址空間為2^20,因此只能訪問(wèn)最下方1MB的內(nèi)存。之后隨著技術(shù)發(fā)展,Intel公司的處理器發(fā)展到了32bits尋址,為了兼容原來(lái)的軟硬件,保留了PC物理內(nèi)存中最下方1MB內(nèi)存的布局和使用方式,把這之上的內(nèi)存高地址部分設(shè)置為擴(kuò)展內(nèi)存(盡管這部分內(nèi)存有一部分預(yù)留給了32bits的設(shè)備,如上圖所示),而只有處理器工作在保護(hù)模式下才能訪問(wèn)到這部分?jǐn)U展內(nèi)存(詳見(jiàn)其他關(guān)于保護(hù)模式的分析)。

  在boot.S的開(kāi)頭,使用了.set匯編偽指令定義了Boot Loader代碼段和數(shù)據(jù)段的段選擇子(Segment Selector)和標(biāo)志位CR0_PE_ON(這個(gè)標(biāo)志位與切換到保護(hù)模式有關(guān),具體會(huì)在后面介紹)。這之后,進(jìn)行了一系列的初始化,包括關(guān)中斷、DF寄存器復(fù)位、用0初始化部分段寄存器等(還有一部分是通過(guò)in、out等匯編指令和端口交換字節(jié)信息,因?yàn)閭€(gè)人對(duì)硬件端口和工作原理并不熟悉,這部分的分析可能以后補(bǔ)上)。再之后就是關(guān)鍵的從實(shí)模式切換到保護(hù)模式的代碼:

1 lgdt gdtdesc 2 movl %cr0, %eax 3 orl $CR0_PE_ON, %eax 4 movl %eax, %cr0

其中第1行使用lgdt指令載入了事先定義好的GDT(全局描述符表,Global Descriptor Table),這張GDT的內(nèi)容在boot.S的末尾:

1 # Bootstrap GDT 2 .p2align 2 # force 4 byte alignment 3 gdt: 4 SEG_NULL # null seg 5 SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg 6 SEG(STA_W, 0x0, 0xffffffff) # data seg 7 8 gdtdesc: 9 .word 0x17 # sizeof(gdt) - 1 10 .long gdt # address gdt

值得注意的是,lgdt指令需要的參數(shù)共6bytes,其中低位的2bytes表示該GDT的大小,高位的4bytes表示指向該GDT的32bits基址(gdtdesc所指向的參數(shù)滿足這一要求),使用lgdt指令的目的是在實(shí)模式切換到保護(hù)模式之前進(jìn)行初始化。接著我們來(lái)分析GDT的結(jié)構(gòu)(上述代碼的3-6行),其中SEG_NULL、SEG宏均是在“inc/mmu.h”中定義的宏:

1 #define SEG_NULL \ 2 .word 0, 0; \ 3 .byte 0, 0, 0, 0 4 #define SEG(type,base,lim) \ 5 .word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ 6 .byte (((base) >> 16) & 0xff), (0x90 | (type)), \ 7 (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)

從宏定義來(lái)看,SEG_NULL定義了一個(gè)空段(根據(jù)習(xí)慣,GDT的第一個(gè)段都是空段),接著

1 SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg

定義了可執(zhí)行的(STA_X)、可讀的(STA_R)、基址為0x0且大小為0xffffffff(即占整個(gè)PC內(nèi)存、大小為4GB的)的代碼段,而

1 SEG(STA_W, 0x0, 0xffffffff) # data seg

定義了可讀的(STA_W,取這個(gè)值時(shí)該段不可執(zhí)行)、基址為0x0且大小為0xffffffff(同上)的數(shù)據(jù)段,以上就是boot.S中預(yù)定義的GDT的內(nèi)容。

  然后我們?cè)倩氐綇哪J角袚Q部分的匯編代碼的第2-4行(值得注意的是,AT&T匯編和Intel匯編的源操作數(shù)和目標(biāo)操作數(shù)的順序不同),這3行代碼利用eax寄存器,讓原本的控制寄存器CR0和標(biāo)志位CR0_PE_ON進(jìn)行or運(yùn)算(實(shí)質(zhì)上是將CR0寄存器的第0位置1),將處理器由實(shí)模式切換到了保護(hù)模式。為什么這3行代碼能夠做到模式的切換?這就需要了解CR0各位表示的含義:

CR0各位含義
比特位簡(jiǎn)寫(xiě)全稱描述
0PEProtected Mode Enable保護(hù)模式使能,PE=1表示CPU處于保護(hù)模式,PE=0表示CPU處于實(shí)模式
1MPMonitor co-processor協(xié)處理器監(jiān)控,MP=1表示協(xié)處理器在工作,MP=0表示協(xié)處理器未工作
2EMEmulation協(xié)處理器仿真,當(dāng)MP=0且EM=1表示正在使用軟件仿真協(xié)處理器工作
3TSTask switched任務(wù)轉(zhuǎn)換,每次任務(wù)轉(zhuǎn)換時(shí),TS=1表示任務(wù)轉(zhuǎn)換完畢
4ETExtension type處理器擴(kuò)展類型,表示所擴(kuò)展的協(xié)處理器的類型,ET=0表示80287,ET=1表示80387
5NENumeric error數(shù)值異常中斷控制,如果運(yùn)行協(xié)處理器指令發(fā)生故障,NE=1表示使用異常中斷處理,NE=0表示用外部中斷處理
16WPWrite protect寫(xiě)保護(hù),WP=1表示對(duì)只讀頁(yè)面進(jìn)行寫(xiě)操作時(shí)會(huì)產(chǎn)生頁(yè)故障
18AMAlignment mask對(duì)齊標(biāo)志,AM=1表示允許對(duì)齊檢查,AM=0表示不允許對(duì)其檢查
29NWNot-write through和CD一起控制CPU內(nèi)部Cache,NW=0且CD=0表示Cache使能,其他組合參見(jiàn)Intel手冊(cè)
30CDCache disable同上
31PGPaging頁(yè)式管理機(jī)制使能,PG=1表示頁(yè)式管理機(jī)制工作,PG=0表示不工作

可以看出,CPU的工作模式是依靠CR0的PE位控制的,因此要想切換到保護(hù)模式,只需要把CR0的PE位置為1。

  在CPU完成從實(shí)模式到保護(hù)模式的切換之后,boot.S使用一條ljmp指令跳轉(zhuǎn)到模式切換后的第一條指令地址:

1 # Jump to next instruction, but in 32-bit code segment. 2 # Switches processor into 32-bit mode. 3 ljmp $PROT_MODE_CSEG, $protcseg

還記得boot.S開(kāi)頭的兩個(gè)段選擇子嗎?這里的PROT_MODE_CSEG就是其中的一個(gè)標(biāo)記了代碼段的段選擇子,在解釋為什么PROT_MODE_CSEG要定義為0x8之前,我們有必要先了解一下實(shí)模式和保護(hù)模式下尋址方式的不同。

  在實(shí)模式下,要想尋址某個(gè)內(nèi)存單元,需要知道所在段的基地址base和它在段中的偏移量offset,由公式:base<<4 + offset得到。而在保護(hù)模式下,尋址不再需要段的基地址,而是換成了段選擇子。段選擇子的結(jié)構(gòu)如下:

其中RPL(第0、1位)表示特權(quán)請(qǐng)求級(jí),TI(第2位)表示描述符表標(biāo)識(shí)符(用于區(qū)分GDT和LDT),Index(第3-15位)表示描述符在描述符表中的索引(從0計(jì)起)。現(xiàn)在我們?cè)賮?lái)考慮為什么PROT_MODE_CSEG定義為0x8。我們把PROT_MODE_CSEG用二進(jìn)制的段選擇子的結(jié)構(gòu)表示為:0000 0000?0000?1000,根據(jù)段選擇子的結(jié)構(gòu)可以得到:

RPL = 00(0),TI = 0,Index =?0000 0000?0000?1(1)

這說(shuō)明該段選擇子訪問(wèn)RPL為0的、GDT中的第1個(gè)段。那這個(gè)段究竟是什么?我們回顧一下之前在boot.S中預(yù)定義的GDT:

1 gdt: 2 SEG_NULL # null seg 3 SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg 4 SEG(STA_W, 0x0, 0xffffffff) # data seg

其中第1個(gè)段就是代碼段(注意是從0開(kāi)始計(jì)數(shù)),另一個(gè)段選擇子PROT_MODE_DSEG的分析同上。到這里上述ljmp指令的功能就很清楚了:跳轉(zhuǎn)到由PROT_MODE_CSEG段選擇子和protcseg偏移量指定的代碼入口處。而跳轉(zhuǎn)到的代碼所完成的工作是:通過(guò).code32偽指令編碼的對(duì)各段寄存器的初始化,隨后利用start標(biāo)號(hào)指向的地址作為esp調(diào)用main.c中的bootmain()函數(shù)(注意bootmain()函數(shù)不需要參數(shù),因此也就沒(méi)有參數(shù)的壓棧操作)。

1 # Set up the stack pointer and call into C. 2 movl $start, %esp 3 call bootmain

  到此boot.S的功能結(jié)束。

  2.main.c

1 #include <inc/x86.h> 2 #include <inc/elf.h> 3 4 /********************************************************************** 5 * This a dirt simple boot loader, whose sole job is to boot 6 * an ELF kernel image from the first IDE hard disk. 7 * 8 * DISK LAYOUT 9 * * This program(boot.S and main.c) is the bootloader. It should 10 * be stored in the first sector of the disk. 11 * 12 * * The 2nd sector onward holds the kernel image. 13 * 14 * * The kernel image must be in ELF format. 15 * 16 * BOOT UP STEPS 17 * * when the CPU boots it loads the BIOS into memory and executes it 18 * 19 * * the BIOS intializes devices, sets of the interrupt routines, and 20 * reads the first sector of the boot device(e.g., hard-drive) 21 * into memory and jumps to it. 22 * 23 * * Assuming this boot loader is stored in the first sector of the 24 * hard-drive, this code takes over... 25 * 26 * * control starts in boot.S -- which sets up protected mode, 27 * and a stack so C code then run, then calls bootmain() 28 * 29 * * bootmain() in this file takes over, reads in the kernel and jumps to it. 30 **********************************************************************/ 31 32 #define SECTSIZE 512 33 #define ELFHDR ((struct Elf *) 0x10000) // scratch space 34 35 void readsect(void*, uint32_t); 36 void readseg(uint32_t, uint32_t, uint32_t); 37 38 void 39 bootmain(void) 40 { 41 struct Proghdr *ph, *eph; 42 43 // read 1st page off disk 44 readseg((uint32_t) ELFHDR, SECTSIZE*8, 0); 45 46 // is this a valid ELF? 47 if (ELFHDR->e_magic != ELF_MAGIC) 48 goto bad; 49 50 // load each program segment (ignores ph flags) 51 ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); 52 eph = ph + ELFHDR->e_phnum; 53 for (; ph < eph; ph++) 54 // p_pa is the load address of this segment (as well 55 // as the physical address) 56 readseg(ph->p_pa, ph->p_memsz, ph->p_offset); 57 58 // call the entry point from the ELF header 59 // note: does not return! 60 ((void (*)(void)) (ELFHDR->e_entry))(); 61 62 bad: 63 outw(0x8A00, 0x8A00); 64 outw(0x8A00, 0x8E00); 65 while (1) 66 /* do nothing */; 67 } 68 69 // Read 'count' bytes at 'offset' from kernel into physical address 'pa'. 70 // Might copy more than asked 71 void 72 readseg(uint32_t pa, uint32_t count, uint32_t offset) 73 { 74 uint32_t end_pa; 75 76 end_pa = pa + count; 77 78 // round down to sector boundary 79 pa &= ~(SECTSIZE - 1); 80 81 // translate from bytes to sectors, and kernel starts at sector 1 82 offset = (offset / SECTSIZE) + 1; 83 84 // If this is too slow, we could read lots of sectors at a time. 85 // We'd write more to memory than asked, but it doesn't matter -- 86 // we load in increasing order. 87 while (pa < end_pa) { 88 // Since we haven't enabled paging yet and we're using 89 // an identity segment mapping (see boot.S), we can 90 // use physical addresses directly. This won't be the 91 // case once JOS enables the MMU. 92 readsect((uint8_t*) pa, offset); 93 pa += SECTSIZE; 94 offset++; 95 } 96 } 97 98 void 99 waitdisk(void) 100 { 101 // wait for disk reaady 102 while ((inb(0x1F7) & 0xC0) != 0x40) 103 /* do nothing */; 104 } 105 106 void 107 readsect(void *dst, uint32_t offset) 108 { 109 // wait for disk to be ready 110 waitdisk(); 111 112 outb(0x1F2, 1); // count = 1 113 outb(0x1F3, offset); 114 outb(0x1F4, offset >> 8); 115 outb(0x1F5, offset >> 16); 116 outb(0x1F6, (offset >> 24) | 0xE0); 117 outb(0x1F7, 0x20); // cmd 0x20 - read sectors 118 119 // wait for disk to be ready 120 waitdisk(); 121 122 // read a sector 123 insl(0x1F0, dst, SECTSIZE/4); 124 } main.c

  在分析main.c的功能之前,先介紹3種將要使用到的結(jié)構(gòu)體:Elf(Executable and Linkable Format)、Proghdr(Program Header)、Secthdr(Section Header),這三種結(jié)構(gòu)體在“inc/elf.h”中定義如下(詳見(jiàn):https://en.wikipedia.org/wiki/Executable_and_Linkable_Format):

1 #define ELF_MAGIC 0x464C457FU /* "\x7FELF" in little endian */ 2 3 struct Elf { 4 uint32_t e_magic; //此處必須與ELF_MAGIC相等,否則不是有效Elf文件 5 uint8_t e_elf[12]; // 6 uint16_t e_type; // 7 uint16_t e_machine; //標(biāo)明支持的指令集結(jié)構(gòu) 8 uint32_t e_version; // 9 uint32_t e_entry; //kernel進(jìn)程開(kāi)始執(zhí)行的入口地址 10 uint32_t e_phoff; //Program Header表的偏移量 11 uint32_t e_shoff; //Section Header表的偏移量 12 uint32_t e_flags; // 13 uint16_t e_ehsize; //Elf文件頭的大小 14 uint16_t e_phentsize; // 15 uint16_t e_phnum; //Program Header表中的條目數(shù)量 16 uint16_t e_shentsize; // 17 uint16_t e_shnum; //Section Header表中的條目數(shù)量 18 uint16_t e_shstrndx; // 19 }; 20 21 struct Proghdr { 22 uint32_t p_type; //標(biāo)明該段的類型 23 uint32_t p_offset; //該段在文件鏡像中的偏移量 24 uint32_t p_va; //該段在內(nèi)存中的虛擬地址 25 uint32_t p_pa; //在使用相對(duì)物理地址的系統(tǒng)中,表示該段在內(nèi)存中的物理地址 26 uint32_t p_filesz; //該段在文件鏡像中的大小(以bytes計(jì),可以為0) 27 uint32_t p_memsz; //該段在內(nèi)存中的大小(以bytes計(jì),可以為0) 28 uint32_t p_flags; // 29 uint32_t p_align; // 30 }; 31 32 struct Secthdr { 33 uint32_t sh_name; // 34 uint32_t sh_type; // 35 uint32_t sh_flags; // 36 uint32_t sh_addr; //該節(jié)在內(nèi)存中的虛擬地址(對(duì)已經(jīng)加載的扇區(qū)而言) 37 uint32_t sh_offset; //該節(jié)在文件鏡像中的偏移量 38 uint32_t sh_size; //該節(jié)在文件鏡像中的大小(以bytes計(jì),可以為0) 39 uint32_t sh_link; // 40 uint32_t sh_info; // 41 uint32_t sh_addralign; // 42 uint32_t sh_entsize; // 43 };

?  接下來(lái)我們從函數(shù)的角度來(lái)分析main.c都做了什么工作。在main.c的開(kāi)頭處我們看到兩行函數(shù)頭,分別是readsect(void *, uint32_t)和readseg(uint32_t, uint32_t, uint32_t),緊接著是在boot.S中被調(diào)用的bootmain()函數(shù)體。我們依次對(duì)這三個(gè)函數(shù)進(jìn)行觀察:

  首先是readsect(void *dst, uint32_t offset)。該函數(shù)中的函數(shù)通過(guò)__asm __volatile()間接使用了in和out匯編指令(詳見(jiàn)“inc/x86.h”)對(duì)指定編號(hào)(在函數(shù)中,編號(hào)參數(shù)被定義為offset)的磁盤(pán)扇區(qū)進(jìn)行讀操作,并把讀到的信息寫(xiě)到dst指向的內(nèi)存中。

  其次是readseg(uint32_t pa, uint32_t count, uint32_t offset)。從接口上看,該函數(shù)是向pa所指向的地址中讀入count個(gè)bytes的、在磁盤(pán)上偏移量為offset的數(shù)據(jù)。而在函數(shù)的實(shí)現(xiàn)中,該函數(shù)對(duì)數(shù)據(jù)的讀取是借助readsect(void *dst, uint32_t offset),以扇區(qū)為單位進(jìn)行的,即通過(guò)offset計(jì)算所在的磁盤(pán)扇區(qū),然后再將整個(gè)扇區(qū)讀入,最終達(dá)到讀入所有數(shù)據(jù)的目的。

  最后我們對(duì)bootmain()函數(shù)進(jìn)行觀察:

  在bootmain()函數(shù)的開(kāi)頭,先通過(guò)

1 readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);

從磁盤(pán)偏移量為0的地方向ELFHDR這個(gè)指針指向的地址(在main.c中該地址由宏定義為0x10000)讀入1個(gè)頁(yè)(512 * 8 = 4096 bytes,即8個(gè)扇區(qū))大小的數(shù)據(jù)。隨后對(duì)讀入的kernel是否為Elf格式進(jìn)行了校驗(yàn)。若校驗(yàn)不通過(guò),向特定端口輸出特定字信息后進(jìn)入死循環(huán)(bad標(biāo)號(hào)指向的代碼,此處也通過(guò)__asm __volatile()間接使用了out匯編指令)。若通過(guò)文件格式校驗(yàn),則通過(guò)ph和eph兩個(gè)Proghdr *類型的指針加載該Elf文件中的所有程序段(其中ph指向Program Header表的表頭,eph指向Program Header表的表尾,每一次指針的自加操作都是指向當(dāng)前表中的下一條目,表中條目的數(shù)量都保存在ELFHDR->e_phnum中),更具體地說(shuō),對(duì)于表中的每一項(xiàng)ph(即每個(gè)程序段),通過(guò)

1 readseg(ph->p_pa, ph->p_memsz, ph->p_offset);

將ph所對(duì)應(yīng)的偏移量為ph->offset的、大小為ph->memsz的程序段從磁盤(pán)中都入到ph->p_pa指向的物理內(nèi)存中。個(gè)人認(rèn)為,上述部分代碼實(shí)質(zhì)上是將整個(gè)kernel程序從磁盤(pán)部署到內(nèi)存中等待執(zhí)行。而最后一行

1 ((void (*)(void)) (ELFHDR->e_entry))();

顯然是通過(guò)函數(shù)指針進(jìn)行函數(shù)調(diào)用,這里的ELFHDR->e_entry指向的地址就是上面一開(kāi)始所介紹的kernel進(jìn)程的第一條指令的地址,也就是說(shuō),這條C語(yǔ)句執(zhí)行過(guò)后,Boot Loader就將執(zhí)行權(quán)交給了kernel,因?yàn)槔碚撋蟢ernel并不返回,所以這是Boot Loader所執(zhí)行的最后一條語(yǔ)句(如果kernel返回說(shuō)明操作系統(tǒng)出現(xiàn)嚴(yán)重錯(cuò)誤,跳轉(zhuǎn)到bad標(biāo)號(hào)指向的代碼執(zhí)行)。

  到此main.c的功能結(jié)束。到此整個(gè)Boot Loader的功能結(jié)束。

轉(zhuǎn)載于:https://www.cnblogs.com/LostChristmas/p/5987381.html

總結(jié)

以上是生活随笔為你收集整理的MIT JOS学习笔记01:环境配置、Boot Loader(2016.10.22)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。