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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

用 GRUB 引导自己的操作系统

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

    ?

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

    總結(jié)

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

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