Grub2.04分析

Grub第一阶段总结

一、Grub概念简介

GNU Grub(GRand Unified Bootloader简称“Grub”)是一个来自GNU项目的多操作系统启动程序,允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统。引导加载程序是计算机启动时运行的第一个软件程序,它负责加载和传输控制操作系统内核软件,之后内核依次初始化操作系统的其余部分。

使用Grub启动时,可以使用命令行界面(参见命令行界面),也可以使用菜单界面(参见菜单界面)。在菜单中,可以切换到命令行模式,反之亦然。

使用命令行界面,可以手动输入内核的驱动器规范和文件名。

在菜单界面中,该菜单基于预先准备的配置文件(参见configuration),只需使用方向键选择操作系统。

 

图1 图形菜单界面

 

图2 命令行界面

  • Grub使用方法

GNU GRUB 手册 2.06

Grub启动阶段,按c进行shell命令行阶段:

www.gnu.org/software/grub/manual/grub/grub.html#Command_002dline-and-menu-entry-commands

2.1 Grub正常启动

Grub正常启动后,将进入“普通模式”,一般是显示一个菜单(找到了'$prefix/Grub.cfg'),或者直接进入Grub SHELL(没找到'$prefix/Grub.cfg')。

在普通模式中,命令模块[command.lst]与加密模块[crypto.lst]会被自动按需载入(无需使用"insmod"命令),并且可使用完整的Grub脚本功能。但是其他模块则可能需要明确使用"insmod"命令来载入。

2.1.1 Grub在BIOS平台

Grub默认安装在第一个硬盘hd0的MBR上,把引导程序boot.img写入硬盘的MBR,启动时根据MBR提示找到启动分区,加载分区内的Grub核心文件core.img和配置文件Grub.cfg。(hd0,mdsom1)

BIOS --> boot.img[MBR] --> core.img --> 设置"prefix root cmdpath"环境变量 --> 加载"normal.mod"模块[同时还包括依赖的 terminal crypto extcmd boot gettext 模块] --> 执行"normal $prefix/Grub.cfg"命令。

2.1.2 Grub在UEFI平台

UEFI --> core.img[BOOTX64.EFI/BOOTX86.EFI] --> 设置"prefix root cmdpath"环境变量 --> 加载"normal.mod"模块[同时还包括它所依赖的 terminal crypto extcmd boot gettext 模块] --> 执行"normal $prefix/Grub.cfg"命令。

2.2 Grub版本查询

查询ubuntu的Grub版本号:

  1. Grub-install --version
  2. Grub-install -V

 

2.3 Grub菜单加上背景图

首先制作一张PNG格式的图片,分辨率最好是"1024x768"以保证较好的兼容性。然后将这张图片放到"$prefix/themes/1024x768.png"("$prefix"是Grub的安装目录)。然后在'Grub.cfg'中加入如下内容:

set gfxmode=1024x768,auto

insmod gfxterm

insmod png

terminal_output  gfxterm

background_image $prefix/themes/1024x768.png

2.4 Grub显示中文界面(包括显示中文菜单项)

由于Grub在内部使用UTF-8编码,并且所有文本文件(包括'Grub.cfg')也都被假定为使用UTF-8编码,为了避免乱码,请务必以UTF-8编码保存'Grub.cfg'文件。

set gfxterm_font=unicode

set lang=zh_CN

set locale_dir=$prefix/locale

insmod gfxterm

terminal_output  gfxterm

loadfont unicode

2.5 更改Grub的字体

如果默认的unicode字体在1024x768或更高分辨率的屏幕上显得太小,或者默认的字体不好看,可以使用"Grub-mkfont"工具。下面的示例展示了制作一个24px大小的pf2字体:

Grub-mkfont -i1 -n WenQuanYiMicroHeiMono24px -o WenQuanYiMicroHeiMono24px.pf2 -s24 -v wqy-microhei.ttc

将制作好的字体文件(WenQuanYiMicroHeiMono24px.pf2)放到"$prefix/fonts"目录中,修改'Grub.cfg'文件中的两行:

set gfxterm_font=WenQuanYiMicroHeiMono24px

loadfont WenQuanYiMicroHeiMono24px

2.6 Grub-2.0.6版本官方模块

模块间的依赖关系位于"moddep.lst"文件中

2.6.1 命令模块[command.lst]

提供了各种不同的功能,类似标准Unix命令。

insmod module载入名为"module"的Grub模块。

set [envvar=value]将环境变量"envvar"的值设为'value'。如果没有使用参数,则打印出所有环境变量及其值。

help [pattern …]显示内建命令的帮助信息。如果没有指定"pattern",那么将显示所有可用命令的简短描述。如果指定了"pattern",那么将只显示名字以这些"pattern"开头的命令的详细帮助信息。

halt [--no-apm]关闭计算机。如果指定了 --no-apm 选项,表示不执行APM BIOS调用。否则,计算机使用APM关闭。

boot启动已经被载入的OS或链式加载器。仅在运行于交互式命令行的时候才是需要的。在一个菜单项结束时是隐含的。

cat [--dos] file显示文件"file"的内容。如果使用了"--dos"选项,那么"回车/换行符"将被显示为一个简单的换行符。否则,回车符将被显示为一个控制符(<d>)。

ls [arg …]如果不使用参数,那么列出所有对Grub已知的设备。

如果参数是包含在括号内的一个设备名,那么列出该设备根目录下的所有文件。

如果参数是以绝对路径给出的目录,那么列出这个目录的内容。

lsmod列出已经加载的所有模块

normal_exit退出当前的普通模式。如果这个普通模式实例不是嵌套在另一个普通模式里的话,就会返回到救援模式。

2.6.2 加密模块[crypto.lst]

提供了各种数据完整性校验与密码算法支持,一共20多个。例如:gcry_rijndael crc64 gcry_md5 ...

2.6.3 文件系统模块[fs.lst]

提供了访问各种文件系统的功能,一共30多个。例如:btrfs cpio exfat ext2 fat iso9660 ntfs tar xfs zfs ...

2.6.4 分区模块[partmap.lst]

提供了识别各种分区格式的功能,一共10多个。例如:part_bsd part_gpt part_msdos ...

2.6.5 分区工具[parttool.lst]

提供了操作各种分区格式的功能,目前只有 msdospart 这一个。

2.6.6 终端模块[terminal.lst]

提供了各种不同终端的支持,一共不到10个。例如:serial gfxterm vga_text at_keyboard ...

2.6.7 视频模块[video.lst]

提供了各种不同的视频模式支持,一共6个。例如:vga vbe efi_gop efi_uga ...

2.6.8 其他模块

所有未在上述分类文件中列出的模块都归为这一类,一共将近100个。值得关注的有以下几个:

"all_video"可用于一次性加载当前所有可用的视频模块;

"gfxmenu"可用于提供主题支持;

"jpeg png tga"可用于提供特定格式的背景图片支持;

"xzio gzio lzopio"可用于提供特定压缩格式支持(常配合"initrd"命令使用);

2.7 Grub救援模式Rescue

进入救援模式,这意味着Grub由于某种原因未能加载“模块”或者Grub没有在正确的位置读取。

# Inspect the current prefix (and other preset variables):

set    可以输入set查看一下当前信息

# Find out which devices are available:

ls       查看Grub位置

# Set to the correct value, which might be something like this:

set prefix=(hd0)/Grub               

set root=(hd0)

insmod normal

normal     回到引导界面上

  • Grub配置文件

3.1 Grub配置文件生成与更新

Grub-mkconfig脚本自动生成 /boot/Grub/Grub.cfg,不需要手动修改。

# Grub-mkconfig -o /boot/Grub/Grub.cfg

/etc/Grub.d/脚本包含 Grub 菜单信息和操作系统引导脚本,运行 update-grub 命令时,它会读取 Grub 文件和 Grub.d 脚本的内容,并更新创建Grub.cfg文件。

3.2 Grub配置文件详解

下图为在Linux系统下,/etc/default/Grub内包含的自定义Grub配置文件。

 

(1)Grub_DEFAULT

默认的菜单项,默认值为0。其值可为数值N,表示从0开始计算的第N项是默认菜单

也可以指定对应的title表示该项为默认的菜单项。使用数值比较好,因为使用的title可能包含了容易改变的设备名

(2)Grub_SAVEDEFAULT

默认该key的值未设置。如果该key的值设置为true时,如果选定了某菜单项,则该菜单项将被认为是新的默认菜单项。该key只有在设置了"Grub_DEFAULT=saved"时才有效。

(3)Grub_TIMEOUT

在开机选择菜单项的超时时间,超过该时间将使用默认的菜单项来引导对应的操作系统。

默认值为5秒。等待过程中,按下任意按键都可以中断等待。

设置为0时,将不列出菜单直接使用默认的菜单项引导与之对应的操作系统,设置为"-1"时将永久等待选择。

(4)Grub_TIMEOUT_STYLE

如果该key未设置值或者设置的值为"menu",则列出启动菜单项,并等待"Grub_TIMEOUT"指定的超时时间。

如果设置为"countdown"和"hidden",则不显示启动菜单项,而是直接等待"Grub_TIMEOUT"指定的超时时间。如果超时了则启动默认菜单项并引导对应的操作系统

(5)Grub_BACKGROUND

设置背景图片,背景图片必须是Grub可读的,图片文件名后缀必须是".png"、".tga"、".jpg"、".jpeg",在需要的时候,Grub会按比例缩小图片的大小以适配屏幕大小。

(6)Grub_THEME

设置Grub菜单的主题。

四、Grub源码分析

4.1 Grub源代码下载

Grub源代码下载地址:https://ftp.gnu.org/gnu/Grub/,如下图所示根据系统编译环境选择需要的Grub源代码。

 

4.2 Grub源代码文件简介

本次选择下载Grub-2.06.tar.gz

 

在终端输入 tar zxvf Grub-2.06.tar.gz解压文件,进入Grub-2.06文件夹

 

asm-tests:这里面是几个汇编代码文件;

build-aux:包含了编译时可能用到的脚本;

conf:编译需要用的文件,make的时候会用到;

docs:帮助文件,还有Grub.cfg示例模板;

Grub-core:Grub的主体核心代码;

include:Grub主体的头文件;

m4:包含m4文件,configure的时候会使用到;

po:这个目录下主要由两种文件,一种时.po文件,另一种时.gmo文件。它们是用来支持多语言的,这个文件夹内的东西不要管,只是对代码做一些基本的注释;

tests:一些测试的脚本;

themes:Grub界面的主题;

unicode:Unicode数据;

util:一些工具源的文件。

启动时运行的代码在' Grub-core '的子目录中,在启动完整操作系统后运行的代码在顶层的子目录中。

4.3 Grub核心代码详解

Grub内核在' Grub -core/kern/ '中,它包含一些核心工具,比如设备、磁盘和文件框架、环境变量处理、列表处理等等。

Grub-2.06/Grub-core/kern/main.c  调用其它外部函数(没有在 main.c 中定义的);

Grub-2.06/Grub-core/kern/  目录下的其它源文件,分别提供这些外部函数的定义(实现了这些函数的具体功能);

Grub-2.06/include/Grub/   目录下的头文件,则对应这些外部函数的声明;

Grub-2.06/Grub-core/kern/ 目录下的与这些头文件与同名的 C 源文件;定义main.c 包含的这 12 个头文件,全部位于Grub-2.06/include/Grub/  目录下。

终端实现在' Grub-core/term/ '中。

磁盘访问代码分布在' Grub-core/ Disk / '(用于访问磁盘设备本身)' Grub-core/partmap/ '(用于解释分区表数据),和' Grub-core/fs/ '(用于访问文件系统)。注意,除了一些特殊的例外,Grub只包含从文件系统读取的代码,并试图避免包含任何要写入文件系统的代码;这让我们可以放心地向用户保证,Grub不会导致文件系统损坏。

PCI和USB总线处理在' Grub-core/bus/ '中。

视频处理代码在' Grub-core/ Video / '中,图形化菜单系统大量使用这个功能。

大多数命令都是通过' Grub-core/commands/ '中的文件实现的。

在Grub_main之前,有一段汇编代码:

start:

_start:

         movq        %rcx, EXT_C(Grub_efi_image_handle)(%rip)

         movq        %rdx, EXT_C(Grub_efi_system_table)(%rip)

         andq         $~0xf, %rsp

         call   EXT_C(Grub_main)

进入Grub源代码,执行Grub_main函数。

  • Grub编译与安装

5.1 卸载Grub

卸载掉旧的Grub:apt-get purge Grub-pc。

5.2 UEFI模式下编译生成Grub

Grub使用Autoconf和Automake,大部分Automake输入由Python脚本生成。顶级构建规则在' configuration .ac '中,‘Grub-core / Makefile.core.def’,‘Makefile.util.def’。' *.def '文件中的每个块代表一个构建目标,并指定用于在不同平台上构建该目标的源文件。' *.def '文件被' gentpl.py '处理成Automake输入。

使用configure工具生成Makefile文件时:

./autogen.sh 生成configure文件

./configure --prefix=/usr(安装目录) --with-platform=efi(指定平台pc/uefi)  --target=x86_64(指定目标的处理器架构类型,或i386) --sysconfdir=/etc(配置文件目录) --disable-werror(不把报警归结于错误)

make -j4让make最多允许4个编译命令同时执行,这样可以更有效的利用CPU资源

make install 安装编译生成的文件

Grub-install /dev/sda                 安装到启动盘

sudo update-Grub            更新grub配置文件

5.2.1 制作一个BOOT.EFI可启动镜像

在make install之后

#grub-mkimage -O x86_64-efi -d ./grub-core/ -p /boot/EFI/BOOT/ -o bootx64.EFI boot reboot linux  part_gpt part_msdos disk fat exfat ext2 ntfs xfs  hfs iso9660 normal search_fs_file configfile  chain loopback echo efi_gop  file gfxterm gfxterm_background gfxterm_menu halt help  ls png true

UEFI启动是通过/boot/EFI/BOOT/xxxx.EFI来启动grub引导。

参数解析如下:

-d 表示指定查找模块目录

-p 设置gurb目标文件的文件夹,cfg文件中会调用。

-o 表示生成的目标文件        bootx64.efi

-O 表示集成的平台模块   写 i386-pc  x86_64-efi

BOOTx64.EFI后面全是命令模块,可以根据自己的需求进行添加。

拷贝到指定位置

# cp BOOTx64.EFI /boot/EFI/BOOT/

# reboot

重启就可以使用新编译的BOOTx64.EFI进行引导内核启动了。

grub.cfg修改启动主要配置项解析

配置项     解析

default     指grub启动时默认菜单项,表示默认从哪个菜单启动。默认配置为0。

timeout    指菜单到自动启动系统前停留时间,单位时间为sec。

title           指一个启动操作系统的名称。

root          指相应内核镜像所在目录boot所在的磁盘分区 如:root=‘hd0,msdos1’。

linux          指boot目录下内核镜像的名称。

initrd         指linux的initial ramdisk在boot目录下的名称。

boot          指引导内核进行启动。

gurb2命令说明

命令                                              说明

grub-editenv                                编译环境块工具

grub-mkfont                                 设置grub使用的字体

grub-mkimage                    生成一个gurb的可启动镜像

grub-mkrescue                            生成一个适用于软盘的grub的可启动镜像

grub-install                                   在磁盘上安装grub

grub-mkconfig                    生成grub配置文件

grub-mkdevicemap          生成一个新的device map文件

grub-probe                                   扫描计算机收集磁盘和分区信息

grub-set-default                          配置默认启动项

grub-reboot                                 配置重启之后的默认启动项

5.3 Grub中Makefile的指令

5.3.1 clean

清除当前目录下在 make 过程中产生的文件。它不能删除软件包的配置文件,也不能删除 build 时创建的那些文件。

5.3.2 distclean

类似于"clean",删除当前目录下的的配置文件、build 过程产生的文件。

5.3.3 info

产生必要的 Info 文档。

5.3.4 check 或 test

完成所有的自检功能。在执行检查之前,应确保所有程序已经被创建(但可以尚未安装)。为了进行测试,需要实现在程序没有安装的情况下被执行的测试命令。

5.3.5 install

完成程序的编译并将最终的可执行程序、库文件等拷贝到指定的目录。此种安装一般不对可执行程序进行 strip 操作。

5.3.6 install-strip

和"install"类似,但是会对复制到安装目录下的可执行文件进行 strip 操作。

5.3.7 uninstall

删除所有由"install"安装的文件。

5.3.8 installcheck

执行安装检查。在执行安装检查之前,需要确保所有程序已经被创建并且被安装。

5.3.9 installdirs

创建安装目录及其子目录。它不能更改软件的编译目录,而仅仅是创建程序的安装目录。

5.4 Grub编译

Grub源代码不需要修改makefile文件,添加新的.c和.h文件后,需要修改配置文件。如果添加一个新的模块遵循现有的模式,比如一个新的命令或一个新的文件系统,只需要修改两个文件内容。

此处修改Hello模块命令代码,新增对硬盘文件读取与SM3度量,文件内容为abc。

 

5.4.1 Grub-core/Makefile.core.am

Platfor_PROGRAMS

*************

*.marker:**

5.4.2 Grub-core/Makefile.core.def

添加新的模块

Module = {

Name = cmd;

Common = cmd/cmd.c;

};

5.4.3 hello模块

Grub源代码在发布时候,给出了一个测试指令模块,hello.mod。如果在Grub界面,按c进入命令模式,输入hello,会得到下图:

 

添加类似hello这类命令时,按照上述方式,在Grub-core/目录下添加ptest文件夹,加入ptest.c文件,修改Makefile文件即可添加命令模块。

 

5.4.4 在main函数中新增功能

需要在makefile.core.def中,在126行以后添加到kern中

common = kern/模块.c

会自动将文件与main.c一起编译

  • Grub文件介绍

6.1 /Boot目录

vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。Linux支持虚拟内存,

zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有 gzip解压缩代码。

vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。

initrd是文件系统,“initial ramdisk”的简写,initrd一般被用来临时地引导硬件到实际内核vmlinuz能够接管并继续引导的状态。

System.map是内核符号映射表,顾名思义就是将内核中的符号(也就是内核中的函数)和它的地址能联系起来的一个列表。

6.2 /Boot/Grub目录

[root@study ~]# ls -l /boot/Grub

-rw-r--r--.  device.map            <==Grub 的设备对应文件(下面会谈到)

drwxr-xr-x.  fonts                 <==启动过程中的画面会使用到的字体数据

-rw-r--r--.  Grub.cfg              <==Grub 的主配置文件!相当重要!

-rw-r--r--.  Grubenv               <==一些环境区块的符号

drwxr-xr-x.  i386-pc               <==针对一般 x86 PC所需要的 Grub 的相关模块

drwxr-xr-x.  locale                <==就是语言相关的数据啰

drwxr-xr-x.  themes                <==一些启动主题画面数据

[root@study ~]# ls -l /boot/Grub/i386-pc

-rw-r--r--.  acpi.mod              <==电源管理有关的模块

-rw-r--r--.  ata.mod               <==磁盘有关的模块

-rw-r--r--.  chain.mod             <==进行 loader 控制权移交的相关模块

-rw-r--r--.  command.lst           <==一些指令相关性的列表

-rw-r--r--.  efiemu32.o            <==下面几个则是与 uefi BIOS 相关的模块

-rw-r--r--.  efiemu64.o

-rw-r--r--.  efiemu.mod

-rw-r--r--.  ext2.mod              <==EXT 文件系统家族相关模块

-rw-r--r--.  fat.mod               <==FAT 文件系统模块

-rw-r--r--.  gcry_sha256.mod       <==常见的加密模块

-rw-r--r--.  gcry_sha512.mod

-rw-r--r--.  iso9660.mod           <==光盘文件系统模块

-rw-r--r--.  lvm.mod               <==LVM 文件系统模块

-rw-r--r--.  mdraid09.mod          <==软件磁盘阵列模块

-rw-r--r--.  minix.mod             <==MINIX 相关文件系统模块

-rw-r--r--.  msdospart.mod         <==一般 MBR 分区表

-rw-r--r--.  part_gpt.mod          <==GPT 分区表

-rw-r--r--.  part_msdos.mod        <==MBR 分区表

-rw-r--r--.  scsi.mod              <==SCSI 相关模块

-rw-r--r--.  usb_keyboard.mod      <==下面两个为 USB 相关模块

-rw-r--r--.  usb.mod

-rw-r--r--.  vga.mod               <==VGA 显示适配器相关模块

-rw-r--r--.  xfs.mod               <==XFS 文件系统模块

-rw-r--r--.  hello.mod             <==hello模块,用来做基本的测试

6.3 文件介绍

boot.img

相当与 GRUB 的 stage1 它被写入 MBR或 boot分区,它不能识别任何文件系统

在GRUB2安装时GRUB2内核镜像在磁盘中到位置写入到 boot.img 中

这就使得 boot.img 能够在不能识别文件系统的情况下加载内核镜像

cdboot.img

当从CD引导情况下被写入内核镜像第一个扇区到内容,它负责加载其余的内核镜像到内存。

diskboot.img

当从磁盘引导情况下被写入内核镜像第一个扇区到内容,它负责加载其余的内核镜像到内存。

pxeboot.img

当从网络启动时使用到的。

kernel.img

此镜像包含GRUB2运行时包含的基本工具,框架驱动、文件句柄、环境变量、安全模式命令行解析器等

他可以直接使用但是通常它会被编译进所有的内核镜像中使用。

core.img

这是GRUB2的内核镜像,它由grub-mkimage程序将kernel.img和一些模块动态编译而成,一般情况下他已经包含足够的模块去访问/boot/grub,模块机制使得内核镜像能保持很小的尺寸。在某种程度上,它可以被视为 GRUB 中的 stage2。

*.mod

这是一些可以动态加载的一些模块,当我们需要时,可以将它们可以被动态加载编译进内核镜像

也可以使用insmod手动加载。他们就代替 GRUB 中的 stage1_5 之类到镜像

  • Grub启动解析

UEFI将Bootx64.efi文件Load到内存中加载,第一个运行的函数grub_main(),代码路径位于grub/grub-core/kern/main.c中,整个grub的功能可以说基本上都是在这个函数中完成的。

其主要的代码原理是注册一些命令和对应的回调函数,然后解析grub.cfg去执行对应的命令,一次执行其中的mod。在执行linux命令的时候去加载内核,将内核加载到一个内存地址上,执行initrd命令的时候去加载initrd,将initrd也加载到一个内存地址上,然后执行boot命令,开始解压并解析内核,这个时候拿到内核运行的基地址,并将整个内核拷贝到对应的地址上,并开始运行,这样就开时了操作系统的启动过程。

Grub编程解析

GRUB_MOD_INIT:加载该模块时执行的函数,通常用于初始化模块,注册模块命令,设置环境变量。

GRUB_MOD_FINI:释放该模块时执行的函数,主要用于清理。

grub_register_command函数:用于注册命令,第一个参数为命令名称,第二个参数为该命令执行的函数,第三个参数为该命令的注释。

Grub启动解析

Grub-core/kenner/main.c

grub_machine_init:设备初始化;

这个函数是和体系架构相关的,在Arm、MIPS、x86上的实现都不同,这个函数主要的作用就是初始化在本平台上需要做的必要的操作,例如获取后面grub要执行的每个moudule的基地址,初始化控制台,初始化内存管理系统,grub内部有自己的一套机制来管理内存。社会看门狗,挂载硬盘设备等操作都是在这个函数内做的。

内部函数grub_efi_loongson_init ()函数主要的作用就是获取了内核和固件传参结构中的SMBIOS的那个表,然后通过函数为后面需要传递的整个参数申请内存空间,来存放这数据。

grub_boot_time ("After machine init.");  挂起

grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); 打印信息高亮

grub_printf ("Welcome to GRUB!\n\n"); 打印信息

grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); 打印信息正常

grub_load_config

这个函数的作用就是找到后面要执行的其moudule类型为OBJ_TYPE_CONFIG的moudule,然后将这个moudule拷贝到这里申请的一段内存中。

grub_load_modules

这个函数就是去执行Grub中的命令对应的moudule的入口函数。每一个moudule函数是通过GRUB_MOD_INIT(linux)类似与内核编译的形式编译成每一个section中。然后当运行的时候,就运行对应的这里编译的代码,这里面都是注册的命令的代码,这里面的命令包含Linux、initrd两个命令。这两个命令是在grub.cfg中使用的。当运行这两个命令的时候,就会调用这里面注册的命令的回调函数去运行。这里面只是去注册对应的命令,最后通过boot命令来真正的运行内核。linux命令就是去加载内核,initrd就是其加载initrd。所以grub_load_modules()的作用就是去调用注册的命令的函数,为后面使用的命令做准备。这里面 (mod->init) (mod);这行代码才是真正去执行上面提到的GRUB_MOD_INIT(linux)函数。

函数grub_set_prefix_and_root ()

这个函数是设置环境变量prefix和boot这两个环境变量的,后期我们使用效果如下:

Const char*  prefix_env_path = grub_env_get(“prefix”);   //(hd0,gpt2)/boot/grub

Const char*  root_env_path = grub_env_get(“root”); // hd0,gpt2

Grub_load_measurement_mode()函数

执行度量功能,度量grub.cfg配置文件以及启动可选择的所有内核文件和初始化文件系统。

函数reclaim_module_space ()

这个函数是回收前面运行每个moudule的时候,将moudule加载到内存所占用的内存的地址。

函数grub_register_core_commands ()

这个函数就是注册set命令、unset命令、ls命令和insmod命令的回调函数。具体源代码在corecmd.c文件中,四个命令的回调函数分别为grub_core_cmd_set/unset/ls/insmod。

函数if(load_config)                   grub_parser_execute(load_config)

这个函数的作用就是解析grub.cfg中的内容,并运行对应的命令。

在grub_rescue_parse_line函数中,(cmd->func) (cmd, n - 1, &args[1])去执行对应命令的回调函数。

grub_load_normal_mode ()

这个函数就是执行normal这个命令对应的mod

函数grub_rescue_run

这个函数是防止如果上面的函数grub_parser_execute没有执行,那么就在这里去解析grub.cfg中的命令。

Grub_linux_boot函数

在不同的平台下对应着不同的Grub_linux_boot函数,主要完成了BIOS向内核传递参数和开始运行内核的功能。

这个函数最后return grub_relocatorXX_boot (relocator, state, 0);是跳转到内核开始执行内核代码的函数,前面的几个函数主要是完成给内核传递参数。

在command.c文件中,有注册命令和注销命令两个函数。

函数grub_register_command_prio才是最终将命令注册到全局变量grub_command_t grub_command_list这个链表中的。linux命令注册的时候对应的回调函数就是grub_cmd_linux,而initrd对应的回调函数就是grub_cmd_initrd,这就是在使用这两个命令的时候,去执行的函数就是这两个函数。

grub_register_exported_symbols:导入系统函数符号;

grub_load_modules:导入需要镜像中mod;

reclaim_module_space:释放模块占用的内存空间;

grub_register_core_commands:注册内核命令,set,unset,ls,insmod;

grub_load_normal_mode:执行normal命令,进入normal模块开始执行,grub_cmd_normal(Grub-core/normal/main.c)函数,如果没有配置文件或运行参数,该函数会退出,否则不退出。

grub_rescue_run:进入命令行操作界面

Grub-core/normal/main.c

grub_env_get ("prefix"):获取制作镜像是指定配置文件路径,生成grub.cfg文件的绝对路径,例如:TFTP的为(tftp,192.168.5.1)grub2/grub.cfg;

grub_enter_normal_mode:进入normal模块的grub_normal_execute;

grub_normal_execute:执行read_config_file,读取并执行配置文件;

获取需要从外部导入的命令列表,文件为command.lst,导入对应的模块;

获取需要从外部导入的文件系统列表,文件为fs.lst,导入对应的模块;

获取需要从外部导入的密码列表,文件为crypto.lst,导入对应的模块;

获取需要从外部导入的终端列表,文件为terminal.lst,导入对应的模块;

read_config_file:执行grub_file_open,打开配置文件,文件名为grub.cfg;

grub_file_open:打开配置文件为内存文件;

grub_bufio_open:打开内存文件;

按行执行配置文件;

网络文件系统解析

网络文件操作相关操作

grub_file_open(grub-core/kernel/file.c):第一个参数为文件路径,第二个参数为grub文件类型,函数调用grub_device_open,参数为“tftp,192.168.5.1”;

获取设备名称,调用grub_device_open打开设备,并将返回的设备赋值给grub_file的device字段;

调用grub_fs_probe探测文件系统,赋值给grub_file的fs字段;

调用文件系统fs-open字段函数打开文件,实为grub_net_fs_open函数;

grub_device_open(grub-core/kernel/device.c):

如果设备名称为空,打开环境变量root指定的路径;

首先尝试打开个该文件名的disk,如果打开,则返回该设备;

调用grub_net_open(),打开网络,并将返回的grub_net填入device的net字段。

返回device;

grub_net_open:为全局函数变量,在include/grub/net.h中使用extern进行引用,在net模块初始化函数中使用grub_net_open_real进行赋值。

grub_net_open_real(grub-core/net/net.c):

首先根据参入的名称,查找server的ip地址和协议(protname);

从grub_net_app_level_list链表中查找那个协议与需要的相同(protname),并将找到的protocol赋值给grub_net的protocol字段,同时将grub_net_fs赋值给grub_net的fs字段。

grub_net_app_level_list(全局变量):tftp和http模块在初始化时,调用grub_net_app_level_register(include/grub/net.h)函数将各自的grub_net_app_protocol结构体注册进来。

grub_net_fs_open:

初始化ile->device->net->packs;

调用file->device->net->protocol->open打开文件,实为tftp_open函数;

read_config_file_getline:从grub_file中获取一行数据,调用grub_file_getline;

grub_file_getline:调用grub_file_read读取数据;

grub_file_read:调用file->fs->fs_read读取数据,实际为grub_net_fs_read;

grub_net_fs_read:调用grub_net_fs_read_real执行读取;

grub_net_fs_read_real:从已接收的网络缓冲区中读取数据,如果文件数据不足,调用net->protocol->packets_pulled(实际为tftp_packets_pulled)继续通过网络读取数据。

TCP操作解析

grub_net_tcp_open(grub-core/net/tcp.c):创建一个TCP连接,构建一个SYNC包,并调用grub_net_send_ip_packet发送;

grub_net_send_ip_packet:根据IP类型,选择grub_net_send_ip4_packet发送;

grub_net_send_ip4_packet:添加IP头,调用send_ethernet_packet发送;

send_ethernet_packet:添加MAC头,调用inf->card->driver->send发送,实际为grub_pxe_send(grub-core/net/drivers/i386/pc/pxe.c);

grub_pxe_send:调用grub_pxe_call发送数据包;

grub_net_poll_cards:接收调用receive_packets网络数据包

receive_packets:调用card->driver->recv获取网络数据包,实际为grub_pxe_recv。

receive_packets:调用grub_net_recv_ethernet_packet解析接收的数据包;

grub_net_recv_ethernet_packet:解析出MAC包头,调用grub_net_recv_ip_packets解析IP数据包;

grub_net_recv_ip_packets:调用grub_net_recv_ip4_packets解析IPV4数据包;

grub_net_recv_ip4_packets:解析Ipv4数据包,调用handle_dgram执行;

handle_dgram:根据数据包协议,调用grub_net_recv_tcp_packet解析TCP数据;

grub_net_recv_tcp_packet:处于TCP协议相关交互,如果接收到数据,调用sock->recv_hook返回给客户,该函数为调用grub_net_tcp_open该函数时赋值的接收到数据的回调函数。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值