之前说过了grub的0.X版本, 而新版本是相当于重构了整个bootloader. 来学习一下.
grub2整体观
这些是和grub1的区别:
- 配置文件的名称改变了。在grub中,配置文件为grub.conf或menu.lst(grub.conf的一个软链接),在grub2中改名为grub.cfg
- grub2增添了许多语法,更接近于脚本语言了,例如支持变量、条件判断、循环
- grub2中,设备名称从1开始,而在grub中是从0开始的
- 举个例子, 当我们表示第一块硬盘的时候还是hd(0) 但是当说道分区的时候就变了, (hd0, msdos1)就表示第一块硬盘的第一个mbr分区, 而(hd0, gpt1)就表示第一块硬盘的第一个gpt分区.
- grub2使用img文件,不再使用grub中的stage1、stage1.5和stage2。
- 在已进入操作系统环境下,不再提供grub命令,也就是不能进入grub交互式界面,只有在开机时才能进入
- 在grub2中取消了find命令.
引导方式
grub2有两种引导方式,但是第二种几乎不会用到,除非你需要引导grub2无法引导的操作系统.
这两种方式分别是:
- 直接引导(direct): grub2直接通过默认的grub2 bootloader来引导写在默认配置文件中的操作系统
- 链式引导(indirect): 使用默认的grub2 bootloader 来引导另一个bootloader而不是操作系统.
文件位置以及安装位置
当使用grub来管理启动菜单时,那么boot loader都是grub程序安装的。
传统的grub(legacy)将stage1转换后的内容安装到MBR(VBR或EBR)中的boot loader部分,将stage1_5转换后的内容安装在紧跟在MBR后的扇区中,将stage2转换后的内容安装在/boot分区中。
而grub2将boot.img转换后的内容安装到MBR(VBR或EBR)中的boot loader部分,将diskboot.img和kernel.img结合成为core.img(刚刚在上面说过img文件),同时还会嵌入一些模块或加载模块的代码到core.img中,然后将core.img转换后的内容安装到磁盘的指定位置处。
它们之间更具体的关系后面会有说.
根据分区表格式的不同, grub的安装位置(严格说是core.img的安装位置)也有所不同, 由于我的试验机器是MBR分区,所以就只说一下这个.
MBR允许四个主分区和额外的逻辑分区, 有两种方式安装GURB:
- 嵌入到MBR和第一个分区中间的空间,这部分就是大众所称的”boot track”,”MBR gap”或”embedding area”,它们大致需要31kB的空间;
- 将core.img安装到某个文件系统中,然后使用分区的第一个扇区(严格地说不是第一个扇区,而是第一个block)存储启动它的代码。
这两种方法有不同的问题。
使用嵌入的方式安装grub,就没有保留的空闲空间来保证安全性,例如有些专门的软件就是使用这段空间来实现许可限制的;另外分区的时候,虽然会在MBR和第一个分区中间留下空闲空间,但可能留下的空间会比这更小.
方法二安装grub到文件系统,但这样的grub是脆弱的 例如,文件系统的某些特性需要做尾部包装,甚至某些fsck检测,它们可能会移动这些block.
GRUB开发团队建议将GRUB嵌入到MBR和第一个分区之间,除非有特殊需求,但仍必须要保证第一个分区至少是从第31kB(第63个扇区)之后才开始创建的.
现在的磁盘设备,一般都会有分区边界对齐的性能优化提醒,所以第一个分区可能会自动从第1MB处开始创建的.
img文件
img文件是GRUB2的关键了, 也是他的核心. 我们来说一下这些文件的作用和GRUB legacy中的stage的对应关系.
grub2生成了好几个img文件,有些分布在/usr/lib/grub/i386-pc目录下,有些分布在/boot/grub2/i386-pc目录下,
事实上, 我们的core.img是动态生成的.而其他的img则存在在/usr/lib/grub/i386-pc/下. 在安装grub2的时候 boot.img会拷贝一份到/boot/grub2/i386-pc/目录下
上图描述了各个模块的关系和层级.
boot.img是grub启动的第一个img文件 我们先把他当成入口好了. 这个玩意存在于MBR中或者分区的boot sector里. 因为我们的boot sector大小就是512字节 所以我们的boot.img也正好是这个大小. boot.img的唯一作用就是读取core的第一扇区并跳转上去 接着移交控制权, 由于最大512所以boot.img是无法理解文件系统的. 因此这个core.img的路径是经过硬编码的, 确保boot.img能够找到core.img的位置.
core.img是给grub2-mkimage程序根据diskboot.img、kernel.img和一系列的模块动态创建的 core.img中嵌入了足够多的功能模块以保证grub能访问/boot/grub,并且可以加载相关的模块实现相关的功能,例如加载启动菜单、加载目标操作系统的信息等,由于grub2大量使用了动态功能模块,使得core.img体积变得足够小. core.img中包含了多个img文件的内容,包括diskboot.img/kernel.img等等.. 至于core.img的位置 我们在上面也说过啦.
根据启动环境的不同, core.img第一个扇区的内容是不同的.如果你的启动设备是磁盘, 那么第一个扇区就是diskboot.img, 如果是光盘,那么第一个扇区的内容就是cdboot.img, 作用和diskboot.img是一样的.
kernel.img文件包含了grub的基本运行时环境:设备框架, 文件句柄, 环境变量, 救援模式下的命令行解析器等等. 很少直接使用, 因为它们已经整个嵌入到了core.img中了. 注意, kernel.img是grub的kernel, 和操作系统的内核无关.
如果再多观察一下的话就会发现, kernel.img的大小是比core.img大的.这是因为生成的core中压缩了kernel.
除了这些***.img** 文件 各种*.mod其实就是各种功能模块, 部分模块已经嵌入到core.img中, 或者会被grub自动加载, 但有时也需要使用insmod命令手动加载.
还记得我们在说GRUB的时候, 简单的说了一下stage文件的作用. 现在我们把他们做一个简单的对应.
stage1在功能上就等于boot.img, stage1_5有点类似于core.img中的加载响应文件系统的代码, 但是core.img的功能显然比stage1 _5要多很多.
GRUB2的配置
我们已经知道了GRUB2中的磁盘代号的表示, 现在就来看一下grub.cfg这个重要的配置文件了. grub的官方并不期望用户直接修改这个文件, 而是通过修改其他的配置项接着在使用grub2-mkconfig
来生成配置文件, 这个程序会参考/etc/default/grub作为模板, 这个文件中有很多宏.
1 | [root@study ~]# cat /etc/default/grub |
每次修改完这个模板文件, 为了使其生效, 我们执行:
1 | [root@WWW ~]# vim /etc/default/grub |
观察输出 这个程序能够主动的抓到对应的vmlinuz和initramfs. 这是怎么做到的呢?
这是因为grub2-mkconfig程序会去找/etc/grub.d/*里面的shell脚本, 所以这些脚本就显得尤为重要了我们来看一下(你会发现 这些脚本的名称在grub.cfg中就出现了):
1 | [root@WWW grub.d]# ls -lh |
首先是00_header, 我们在模板文件中所设定的大部分参数都体现在这个脚本中, 因为该脚本主要建立在初始的显示项目上, 比如需要的模块, 终端的格式, 倒计时, 是否隐藏菜单等等.
关于00_tuned, 其实也就只是设置了几个tune这个daemon的参数而已, 我们先忽略. 01 _users我没看懂 为什么是cat了一段shell?? (啊…我好像明白了, 这一段cat的内容会原封不动的跑到最终生成的配置文件里)
接着就是重要的10_linux了 这个脚本主要是为了制作菜单项, 它尝试分析/boot下的配置, 并且尝试找到对应的所需模块和参数.
后面的不想学了(摔!)
一般我们所需要用的就是40_custom了, 也就是手动加上菜单的时候才会动他. 至于怎么写, 从生成好的配置文件里模仿就好了..我不学了.
为菜单加上密码
grub2在用户管理上有一点像Linux, 有两种用户:
- superusers 可以修改grub2下所有的项目的权限, 但是设定了这个参数之后 所有指令的修改将变成受限制的
- users 可以设置多个, 就是简单的进入某个菜单, 搭配账号
我们来看一个grub.cfg:
1 | # 第一个部分就是设定好管理员和用户的账号和密码 |
但是问题是, 我们之前就说过了, 这个配置文件是不推荐进行修改的. 那么怎么让配置文件中出现这些内容呢?
在上文说的那些脚本文件中, 就出现了superusers这个关键词, 对了, 就是01_users这个文件, 我们如果要设置密码的话, 就对这个文件进行修改.
同grub-legacy一样, grub2也提供了密码工具, 叫做grub2-mkpasswd-pbkdf2
这是一个交互式的密码生成工具, 使用起来实在是太简便了.
1 | [root@WWW grub.d]# grub2-mkpasswd-pbkdf2 |
这个从grub2.pbkdf2开始的一大串字符串就是密码了.
这样, 我们直接修改01_users这个脚本, 具体的修改就像这样:
1 |
|
接着我们修改10_linux这个脚本, 在第一个CLASS的位置, 我们把原本在后面的–unrestricted的参数移除.
重启机器. 在开机的时候就会看到请求输入用户名和密码的界面了.