日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

用 GRUB 引导自己的操作系统

發布時間:2025/6/15 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用 GRUB 引导自己的操作系统 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在 PC 機上搗鼓自己的操作系統遇到的第一個難題就是如何將內核加載到內存中執行。如果讀過于淵寫的《自己動手寫操作系統》就會知道這部分的工作還是蠻繁瑣的。而且實際上這部分工作和操作系統沒太大的關系。好在隨著 linux 等開源操作系統的發展,開源的引導加載程序也已經發展的很成熟了。我們可以利用前人的成果,將自己的操作系統改造成可以用現有引導加載程序引導的內核。
?
引導加載程序(BootLoader)是系統加電后運行的第一段軟件代碼。x86 系統中的引導加載程序由 BIOS(Basic Input Output System,基本輸入輸出系統)和位于硬盤主引導記錄(MBR,Master Boot Record)中的操作系統引導加載程序(比如,LILO 和 GRUB 等)一起組成。BIOS 在完成硬件檢測和資源分配后,將硬盤 MBR 中的引導加載程序讀到系統的 RAM 中,然后將控制權交給操作系統引導加載程序。引導加載程序的主要運行任務就是將內核映象從硬盤上讀到 RAM 中,然后跳轉到內核的入口點去運行,也即開始啟動操作系統。?
?
引導加載程序并非操作系統的一部分,但是沒有引導加載程序加載操作系統,操作系統是無法自動運行起來的。可以在 x86 系統中運行的操作系統超過 100 種,其中較為有名的如微軟公司出品的 DOS、Windows 系列,開放源代碼的 Linux、FreeBS D等也不下 10 余種。這些各具特色的操作系統幾乎都有其專用的引導加載程序,并且互相之間并不兼容。
?
經過對比各種常見的引導加載程序的功能和可靠性,我選擇了多重操作系統啟動管理器 GRUB 作為引導加載程序。GRUB 是一個來自自由軟件基金會項目的多操作系統啟動程序,它允許用戶可以在計算機內同時擁有多個操作系統,并在計算機啟動時選擇希望運行的操作系統。GRUB 可用于選擇操作系統分區上的不同內核,也可用于向這些內核傳遞啟動參數。
?
GRUB 引導加載程序廣泛應用于 Linux、各種 BSD 系統的引導,具有極高的可靠性。滿足多重引導規范(The Multiboot Specification),可以引導各種滿足多重引導規范的操作系統內核。并且可以通過配置文件配置為多引導模式,當加載的系統出現故障無法工作時可以自動啟用備用系統,極大的提高了系統的可靠性。
?
多重引導規范?
?
多重引導規范并不強制要求內核的格式,但是如果采用 ELF 格式,將會帶來許多方便。本文下面的介紹都是基于內核采用 ELF 格式。如果您的內核碰巧不能采用 ELF 格式,請您參考多重引導規范的官方文本中 3.1 節關于 Multiboot Header 的介紹。
?
能夠被 GRUB 引導的內核有兩個條件:?
(1) 需要有一個 Multiboot Header ,這個? Multiboot Header 必須在內核鏡像的前 8192 個字節內,并且是首地址是 4 字節對其的。 ?
(2) 內核的加載地址在 1MB 以上的內存中,這個要求是 GRUB 附加的,并非多重引導規范的規定。?
?
Multiboot Header?
?
Multiboot Header的分布必須如下所示:?
?
偏移量?? ?類型?? ?域名?? ????????????? 備注?
0 ?? ?u32 ?? ?magic ?? ??????? ? ???????? 必需?
4 ?? ?u32 ?? ?flags ?? ???????????????????? 必需?
8 ?? ?u32 ?? ?checksum ?? ? ? ?????? 必需?
12 ?? ?u32 ?? ?header_addr??????? 如果flags[16]被置位?
16 ?? ?u32 ?? ?load_addr???????????? 如果flags[16]被置位?
20 ?? ?u32 ?? ?load_end_addr??? 如果flags[16]被置位?
24 ?? ?u32 ?? ?bss_end_addr???? 如果flags[16]被置位?
28 ?? ?u32 ?? ?entry_addr ?? ??????? 如果flags[16]被置位?
32 ?? ?u32 ?? ?mode_type ?? ?????? 如果flags[2]被置位?
36 ?? ?u32 ?? ?width ?? ???????????????? ?如果flags[2]被置位?
40 ?? ?u32 ?? ?height ?? ??????????????? 如果flags[2]被置位?
44 ?? ?u32 ?? ?depth ?? ???????????????? 如果flags[2]被置位 ?
?
magic ?
??? 域是標志頭的魔數,它必須等于十六進制值 0x1BADB002。?
?
flags?
??? flags域指出OS映像需要引導程序提供或支持的特性。0-15 位指出需求:如果引導程序發現某些值被設置但出于某種原因不理解或不能不能滿足相應的需求,它必須告知用戶并宣告引導失敗。16-31位指出可選的特性:如果引導程序不能支持某些位,它可以簡單的忽略它們并正常引導。自然,所有 flags 字中尚未定義的位必須被置為 0。這樣,flags 域既可以用于版本控制也可以用于簡單的特性選擇。
?
??? 如果設置了 flags 字中的 0 位,所有的引導模塊將按頁(4KB)邊界對齊。有些操作系統能夠在啟動時將包含引導模塊的頁直接映射到一個分頁的地址空間,因此需要引導模塊是頁對齊的。
?
??? 如果設置了 flags 字中的 1 位,則必須通過 Multiboot 信息結構(參見引導信息格式)的 mem_* 域包括可用內存的信息。如果引導程序能夠傳遞內存分布(mmap_*域)并且它確實存在,則也包括它。
?
??? 如果設置了 flags 字中的 2 位,有關視頻模式表(參見引導信息格式)的信息必須對內核有效。?
?
??? 如果設置了 flags 字中的 16 位,則 Multiboot 頭中偏移量 8-24 的域有效,引導程序應該使用它們而不是實際可執行頭中的域來計算將 OS 映象載入到那里。如果內核映象為 ELF 格式則不必提供這樣的信息,但是如果映象是 a.out 格式或者其他什么格式的話就必須提供這些信息。
?
checksum?
??? 域 checksum 是一個 32 位的無符號值,當與其他的 magic 域(也就是 magic 和 flags)相加時,結果必須是 32 位的無符號值 0(即magic + flags + checksum = 0)
?
header_addr ?
??? 這里往后的 32 個字節不是必須的,并且對于內核為 ELF 格式時是不需要的,因此就不介紹了。?
?
當引導程序調用32位操作系統時,機器狀態必須如下:?
?
EAX?
??? 必須包含魔數 0x2BADB002;這個值指出操作系統是被一個符合 Multiboot 規范的引導程序載入的。?
EBX?
??? 必須包含由引導程序提供的 Multiboot 信息結構的物理地址(參見引導信息格式)。?
CS?
??? 必須是一個偏移量位于 0 到 0xFFFFFFFF 之間的 32 位可讀/可執行代碼段。這里的精確值未定義。?
DS?
ES?
FS?
GS?
SS?
??? 必須是一個偏移量位于 0 到 0xFFFFFFFF 之間的 32 位可讀/可執行代碼段。這里的精確值未定義。?
A20 gate?
??? 必須已經開啟。?
CR0?
??? 第31位(PG)必須為 0。第 0 位(PE)必須為 1。其他位未定義。?
EFLAGS?
??? 第17位(VM)必須為 0。第 9 位(IF)必須為 1 。其他位未定義。?
?
所有其他的處理器寄存器和標志位未定義。這包括:?
?
ESP?
??? 當需要使用堆棧時,OS 映象必須自己創建一個。?
GDTR?
??? 盡管段寄存器像上面那樣定義了,GDTR 也可能是無效的,所以 OS 映象決不能載入任何段寄存器(即使是載入相同的值也不行!)直到它設定了自己的 GDT。?
IDTR?
??? OS 映象必須在設置完它的 IDT 之后才能開中斷。?
?
Multiboot 信息結構(就目前為止定義的)的格式如下:?
?
???????????? +-------------------+?
???? 0?????? | flags?????????????? ?|??? (必需)?
???????????? +-------------------+?
???? 4?????? | mem_lower???????? |??? (如果flags[0]被置位則出現)?
???? 8?????? | mem_upper???????? |??? (如果flags[0]被置位則出現)?
???????????? +-------------------+?
???? 12????? | boot_device?????? |??? (如果flags[1]被置位則出現)?
???????????? +-------------------+?
???? 16????? | cmdline?????????? |??? (如果flags[2]被置位則出現)?
???????????? +-------------------+?
???? 20????? | mods_count??????? |??? (如果flags[3]被置位則出現)?
???? 24????? | mods_addr???????? |??? (如果flags[3]被置位則出現)?
???????????? +-------------------+?
???? 28 - 40 | syms????????????? |??? (如果flags[4]或flags[5]被置位則出現)?
???????????? |?????????????????? |???????????????? ?
???????????? +-------------------+?
???? 44????? | mmap_length?????? |??? (如果flags[6]被置位則出現)?
???? 48????? | mmap_addr???????? |??? (如果flags[6]被置位則出現)?
???????????? +-------------------+?
???? 52????? | drives_length???? |??? (如果flags[7]被置位則出現)?
???? 56????? | drives_addr?????? |??? (如果flags[7]被置位則出現)?
???????????? +-------------------+?
???? 60????? | config_table????? |??? (如果flags[8]被置位則出現)?
???????????? +-------------------+?
???? 64????? | boot_loader_name? |??? (如果flags[9]被置位則出現)?
???????????? +-------------------+?
???? 68????? | apm_table???????? |??? (如果flags[10]被置位則出現)?
???????????? +-------------------+?
???? 72????? | vbe_control_info? |??? (如果flags[11]被置位則出現)?
???? 76????? | vbe_mode_info???? |?
???? 80????? | vbe_mode????????? |?
???? 82????? | vbe_interface_seg |?
???? 84????? | vbe_interface_off |?
???? 86????? | vbe_interface_len |?
???????????? +-------------------+?
???? ?
第一個 longword 指出 Multiboot 信息結構中的其它域是否有效。所有目前未定義的位必須被引導程序設為 0。操作系統應該忽略任何它不理解的位。因此,flags 域也可以視作一個版本標志符,這樣可以無破壞的擴展Multiboot信息結構。
?
如果設置了 flags 中的第 0 位,則 mem_* 域有效。mem_lower 和 mem_upper 分別指出了低端和高端內存的大小,單位是 K。低端內存的首地址是 0,高端內存的首地址是 1M。低端內存的最大可能值是 640K。返回的高端內存的最大可能值是最大值減去 1M。但并不保證是這個值。
?
flags 的其他位我沒有用到,這里就不介紹了。需要了解的請自己閱讀相關文檔。?
?
下面是一個最簡單的例子:?
?
? [plain]?view plaincopy
  • /**??
  • ?*?boot.S??
  • ?*/??
  • ???
  • #define?MULTIBOOT_HEADER_MAGIC??????????0x1BADB002??
  • #define?MULTIBOOT_HEADER_FLAGS??????????0x00000003??
  • #define?STACK_SIZE??????????????????????0x4000??
  • ???
  • .text??
  • ????????.globl??start,?_start??
  • ???
  • start:??
  • _start:??
  • ????????jmp?????multiboot_entry??
  • ???
  • ????????.align??4??
  • ???
  • multiboot_header:??
  • ????????.long???MULTIBOOT_HEADER_MAGIC??
  • ????????.long???MULTIBOOT_HEADER_FLAGS??
  • ????????.long???-(MULTIBOOT_HEADER_MAGIC?+?MULTIBOOT_HEADER_FLAGS)??
  • ???
  • multiboot_entry:??
  • ????????/*?初始化堆棧指針。?*/??
  • ????????movl????$(stack?+?STACK_SIZE),?%esp??
  • ???
  • ????????/*?重置?EFLAGS。?*/??
  • ????????pushl???$0??
  • ????????popf??
  • ???
  • ????????pushl???%ebx??
  • ????????pushl???%eax??
  • ???
  • ????????/*?現在進入?C?main?函數...?*/??
  • ????????call????cmain??
  • ???
  • loop:???hlt??
  • ????????jmp?????loop??
  • ???
  • ????????.comm???stack,?STACK_SIZE??


  • [cpp]?view plaincopy
  • /**?
  • ?*?kernel.c?
  • ?*/??
  • ??
  • /*?a.out?符號表。?*/??
  • typedef?struct?aout_symbol_table??
  • {??
  • ????unsigned?long?tabsize;??
  • ????unsigned?long?strsize;??
  • ????unsigned?long?addr;??
  • ????unsigned?long?reserved;??
  • }?aout_symbol_table_t;??
  • ??
  • /*?ELF?的?section?header?table。?*/??
  • typedef?struct?elf_section_header_table??
  • {??
  • ????unsigned?long?num;??
  • ????unsigned?long?size;??
  • ????unsigned?long?addr;??
  • ????unsigned?long?shndx;??
  • }?elf_section_header_table_t;??
  • ??
  • /*?Multiboot?信息。?*/??
  • typedef?struct?multiboot_info??
  • {??
  • ????unsigned?long?flags;??
  • ????unsigned?long?mem_lower;??
  • ????unsigned?long?mem_upper;??
  • ????unsigned?long?boot_device;??
  • ????unsigned?long?cmdline;??
  • ????unsigned?long?mods_count;??
  • ????unsigned?long?mods_addr;??
  • ????union??
  • ????{??
  • ????????aout_symbol_table_t?aout_sym;??
  • ????????elf_section_header_table_t?elf_sec;??
  • ????}?u;??
  • ????unsigned?long?mmap_length;??
  • ????unsigned?long?mmap_addr;??
  • }?multiboot_info_t;??
  • ??
  • /*?檢測?FLAGS?中的位?BIT?是否被置位。?*/??
  • #define?CHECK_FLAG(flags,bit)???((flags)?&?(1?<<?(bit)))??
  • ??
  • /*?與顯示相關的設置。?*/??
  • #define?COLUMNS?????????????????80??
  • #define?LINES???????????????????24??
  • #define?ATTRIBUTE???????????????7??
  • #define?VIDEO???????????????????0xB8000??
  • ??
  • static?int?xpos;?/*?X?坐標。?*/??
  • static?int?ypos;?/*?Y?坐標。?*/??
  • static?volatile?unsigned?char?*video;?/*?指向顯存。?*/??
  • ??
  • static?void?cls?(void);??
  • static?void?itoa?(char?*buf,?int?base,?int?d);??
  • static?void?putchar?(int?c);??
  • void?printf?(const?char?*format,?...);??
  • ??
  • void?cmain?(unsigned?long?magic,?unsigned?long?addr)??
  • {??
  • ????multiboot_info_t?*mbi;??
  • ??
  • ????/*?清屏。?*/??
  • ????cls?();??
  • ??
  • ????/*?將?MBI?指向?Multiboot?信息結構。?*/??
  • ????mbi?=?(multiboot_info_t?*)?addr;??
  • ??
  • ????/*?mem_*?是否有效??*/??
  • ????if?(CHECK_FLAG?(mbi->flags,?0))??
  • ????????printf?("mem_lower?=?%uKB,?mem_upper?=?%uKB\n",?(unsigned)?mbi->mem_lower,?(unsigned)?mbi->mem_upper);??
  • ??????
  • ????/*?your?code?here.?*/??
  • }??
  • ??
  • /*?清屏并初始化?VIDEO,XPOS?和?YPOS。?*/??
  • static?void?cls?(void)??
  • {??
  • ????int?i;??
  • ??
  • ????video?=?(unsigned?char?*)?VIDEO;??
  • ??
  • ????for?(i?=?0;?i?<?COLUMNS?*?LINES?*?2;?i++)??
  • ????????*(video?+?i)?=?0;??
  • ??
  • ????xpos?=?0;??
  • ????ypos?=?0;??
  • }??
  • ??
  • /*?將整數?D?轉換為字符串并保存在?BUF?中。如果?BASE?為?'d',則?D?為十進制,如果?BASE?為?'x',則?D?為十六進制。?*/??
  • static?void?itoa?(char?*buf,?int?base,?int?d)??
  • {??
  • ????char?*p?=?buf;??
  • ????char?*p1,?*p2;??
  • ????unsigned?long?ud?=?d;??
  • ????int?divisor?=?10;??
  • ??
  • ????/*?如果指定了?%d?并且?D?是負數,在開始添上負號。?*/??
  • ????if?(base?==?'d'?&&?d?<?0)??
  • ????{??
  • ????????*p++?=?'-';??
  • ????????buf++;??
  • ????????ud?=?-d;??
  • ????}??
  • ????else?if?(base?==?'x')??
  • ????????divisor?=?16;??
  • ??
  • ????/*?用?DIVISOR?去除?UD?直到?UD?==?0。?*/??
  • ????do??
  • ????{??
  • ????????int?remainder?=?ud?%?divisor;??
  • ??
  • ????????*p++?=?(remainder?<?10)???remainder?+?'0'?:?remainder?+?'a'?-?10;??
  • ????}??
  • ????while?(ud?/=?divisor);??
  • ??
  • ????/*?在字符串尾添上終結符。?*/??
  • ????*p?=?0;??
  • ??
  • ????/*?反轉?BUF。?*/??
  • ????p1?=?buf;??
  • ????p2?=?p?-?1;??
  • ????while?(p1?<?p2)??
  • ????{??
  • ????????char?tmp?=?*p1;??
  • ????????*p1?=?*p2;??
  • ????????*p2?=?tmp;??
  • ????????p1++;??
  • ????????p2--;??
  • ????}??
  • }??
  • ??
  • /*?在屏幕上輸出字符?C?。?*/??
  • static?void?putchar?(int?c)??
  • {??
  • ????if?(c?==?'\n'?||?c?==?'\r')??
  • ????{??
  • newline:??
  • ????????xpos?=?0;??
  • ????????ypos++;??
  • ????????if?(ypos?>=?LINES)??
  • ????????????ypos?=?0;??
  • ????????return;??
  • ????}??
  • ??
  • ????*(video?+?(xpos?+?ypos?*?COLUMNS)?*?2)?=?c?&?0xFF;??
  • ????*(video?+?(xpos?+?ypos?*?COLUMNS)?*?2?+?1)?=?ATTRIBUTE;??
  • ??
  • ????xpos++;??
  • ????if?(xpos?>=?COLUMNS)??
  • ????????goto?newline;??
  • }??
  • ??
  • /*?格式化字符串并在屏幕上輸出,就像?libc?函數?printf?一樣。?*/??
  • void?printf?(const?char?*format,?...)??
  • {??
  • ????char?**arg?=?(char?**)?&format;??
  • ????int?c;??
  • ????char?buf[20];??
  • ??
  • ????arg++;??
  • ??
  • ????while?((c?=?*format++)?!=?0)??
  • ????{??
  • ????????if?(c?!=?'%')??
  • ????????????putchar?(c);??
  • ????????else??
  • ????????{??
  • ????????????char?*p;??
  • ??
  • ????????????c?=?*format++;??
  • ????????????switch?(c)??
  • ????????????{??
  • ????????????case?'d':??
  • ????????????case?'u':??
  • ????????????case?'x':??
  • ????????????????itoa?(buf,?c,?*((int?*)?arg++));??
  • ????????????????p?=?buf;??
  • ????????????????goto?string;??
  • ????????????????break;??
  • ??
  • ????????????case?'s':??
  • ????????????????p?=?*arg++;??
  • ????????????????if?(!?p)??
  • ????????????????????p?=?"(null)";??
  • ??
  • string:??
  • ????????????????while?(*p)??
  • ????????????????????putchar?(*p++);??
  • ????????????????break;??
  • ??
  • ????????????default:??
  • ????????????????putchar?(*((int?*)?arg++));??
  • ????????????????break;??
  • ????????????}??
  • ????????}??
  • ????}??
  • }??

  • 下面是編譯命令:

    gcc kernel.c -c -fno-builtin
    gcc boot.S -c
    ld kernel.o boot.o -o kernel -s -Ttext 0x100000 --entry=start


    最后是用 bochs 運行的結果:

    ?

    如何運行自己的內核,可以參考我的文章:制作帶有 GRUB 引導功能的軟盤鏡像文件

    總結

    以上是生活随笔為你收集整理的用 GRUB 引导自己的操作系统的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。