国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Linux > 正文

淺析 GRUB 如何加載 linux kernel

2024-06-28 13:22:13
字體:
來源:轉載
供稿:網友
淺析 GRUB 如何加載 linux kernel前言

對于 GRUB 的加載流程,網上絕大部分都是寫對 menu.lst, grub.cfg 這些 GRUB 配置文件的編寫流程,就像是寫腳本語言一樣,用些關鍵字就能讓 PC機能正確啟動桌面 Linux 了。但這只是 GRUB 的使用,而不是GRUB的分析。

本來是沒有想要探究 GRUB 的想法,直到我在自制toy kernel 的學習中進入了 “虛擬內存管理”這一章節。很多介紹虛擬內存管理的時候都會說到 linux 的內存管理,linux 內核會加載到系統 3G~4G 的虛擬內存中, 但 GRUB 是沒有開啟虛擬內存的,linux 內核的加載是被誰,又是如何加載相應段到 3G~4G 區的呢。

分析 kernelvmlinux

我們看下內核源碼編譯后的最原始文件 vmlinux。該文件是 ELF 文件,使用 readelf 讀下該文件的 Section header.

這里只截了幾個段顯示,后面的段都類似. 可以看到這些需要加載的段的地址的確是在 0xC0000000 之后。但 vmlinux 并不是可引導的linux 內核文件。

linux啟動的相關信息一般都在 /boot 下,我們看下里面的內容.

可以看到 grub 文件夾,grub 就是引導 linux 進行啟動的 bootloader,我們看下 /boot/grub/grub.cfg 文件的內容.

menuentry 'Linux Mint 17 {  recordfail  gfxmode $linux_gfx_mode  insmod gzio  insmod part_msdos  insmod ext2  set root='hd0,msdos1'  linux/boot/vmlinuz-3.13.0-24-generic   initrd/boot/initrd.img-3.13.0-24-generic  }  

帶有 linux 的一行就指定了啟動的內核,可以看到不是 vmlinux 文件,而是 vmlinuz 文件。

vmlinuz

搜索后可以看到 vmlinuz 是可引導的,壓縮的內核。 initrd 是"initial ramdisk" 的簡寫,是臨時的虛擬磁盤,暫時不討論。 因為我電腦上 vmlinuz 是64 bit的,對 64bit不太了解,所以找了個 32bit 的vmlinuz 文件來作解析。先試試readelf命令。

# readelf -S vmlinuzreadelf:錯誤: Unable to read in 0x7269 bytes of 節頭readelf:錯誤: 不是 ELF 文件 - 它開頭的 magic 字節錯誤

不是 ELF 文件, 那試試 objdump 吧。

# objdump -afh vmlinuzobjdump: vmlinuz: 不可識別的文件格式

還是不行。

這個時候之所以會相當用這些命令看 vmlinuz 文件的段信息,因為在我的 toy kernel 中使用的是 ELF 文件,而且是使用 grub 加載的,對于 ELF 文件來說內部保護若干 section, 執行時這些 section 必須要在特地的內存地址上. 使用 readelf 查看toy kernel 的 section header 信息如下.

可以看到 Addr 段就是內核運行時這些段在內存中的地址。而加載我的內核的 grub 的配置如下

title toy kernelroot (fd0)kernel  /zkernelmodule /initrd

vmlinuz 之所以叫做壓縮的內核,是因為它是使用 gzip 壓縮后得來,而且不單單是個純數據包,在文件開頭部分內嵌有 gzip 解壓縮代碼,相當于"自解壓"。我的內核需要由grub加載好相應的 section, 但 vmlinuz 都讀不出來段如何讓 grub 加載?

其實答案就在上面的 grub 配置文件里,在 linux 中聲明內核使用的是 linux 關鍵字,在我的配置中聲明內核使用的卻是 kernel. 可以明確看出,grub對 linux 的加載是特殊對待的, 但具體怎么特使對待,只能從源代碼里看了。

分析 GRUB 源碼

從GRUB 官網上下載的是 GRUB2 的代碼,結構更清晰。因為有關鍵字 "linux", 我們就用 "linux" 來搜索,可以搜到如下代碼.

grub-core/loader/i386/pc/linux.ccmd_linux = grub_register_command("linux", grub_cmd_linux, 0, N_("Load Linux."));

從名稱應該能看出就是這個, 注冊了一個命令,關鍵字是 “linux”。進入 grub_cmd_linux 函數進行分析。

struct linux_kernel_header lh;  file = grub_file_open (argv[0]);//打開 vmlinuz//從 vmlinuz 開頭處讀取 linux header 結構grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh);//校驗合法性if(lh.boot_flag != grub_cpu_to_le16 (0xaa55));  goto fail;//拷貝 linux header 結構體到特地地址grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh));//拷貝 vmlinux 的數據到特地地址grub_file_read (file, grub_linux_PRot_chunk, grub_linux16_prot_size)

大部分和討論無關的代碼都被省略了,只保護加載相關代碼??梢钥吹?vmlinuz 在文件頭部提供了一個結構體給 bootloader使用來判斷相關信息,使用 hexedit 打開vmlinuz 也確實可以看到這些數據,而且該結構體是雙向的,即在vmlinuz 中提供給 bootloader 相關信息, bootloader 也會將內核需要的信息填到其中。可以在THE LINUX/x86 BOOT PROTOCOL中看到該結構體更多信息。grub 并沒有更詳細的分析 vmlinuz 相關信息,加載后就會跳到內核部分進行下一步動作。詳細分析可以看下該鏈接.

再回頭看下 grub-core/loader/multiboot_elfxx.c ,這個應該就是加載 ELF 內核時執行的動作了。

static grub_err_t CONCAT(grub_multiboot_load_elf, XX) (grub_file_t fileconst char *filename, void *buffer){  Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer;  char *phdr_base;  int i;  if (ehdr->e_ident[EI_MAG0] != ELFMAG0  || ehdr->e_ident[EI_MAG1] != ELFMAG1  || ehdr->e_ident[EI_MAG2] != ELFMAG2  || ehdr->e_ident[EI_MAG3] != ELFMAG3  || ehdr->e_ident[EI_DATA] != ELFDATA2LSB)    return grub_error(GRUB_ERR_UNKNOWN_OS, N_("invalid arch-independent ELF magic"));  /* Load every loadable segment in memory.  */  for (i = 0; i < ehdr->e_phnum; i++){}

可以看到 grub 對 ELF 內核的加載的確是分析了 ELF 段構成并按照 ELF 文件內的參數加載了。至于之后 kernel 如何自己配置虛擬內存等就要留到以后再說了。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 巴林右旗| 湘阴县| 蓬安县| 称多县| 洪洞县| 海门市| 和龙市| 丰台区| 禹城市| 新沂市| 错那县| 舞钢市| 科尔| 灌阳县| 丰都县| 南阳市| 新化县| 牙克石市| 碌曲县| 昭平县| 同仁县| 蒙城县| 沧州市| 定安县| 和田市| 彩票| 惠水县| 鸡东县| 竹溪县| 恭城| 崇明县| 乌兰浩特市| 札达县| 龙门县| 敦化市| 岳阳县| 墨江| 和龙市| 黄冈市| 花莲市| 德保县|