多系统引导启动项详解和更改启动内核的方法

内容来自网上查找和总结以及自己的尝试
由于机器装有两套系统,分别是kali2-使用grub.centos7-使用grub2.两代grub的区别在网上查询如下:
---------------------------------------------------
关于版本:
GRUB2 使之版本号为1.98之后的grub;GRUB legacy(版本为0.97)是指GRUB,而非GRUB2,grub是指 grub1.97 和以前的,grub 2 指的是 grub1.98和以后的,现在已经发布grub2.00 了。一般还是把grub2 称作grub。
注意,目前我所知道的grub 2 一般用于linux下,windows下继续使用古董 grub4dos0.4.4 顶多使用chenall网友修改的 0.4.5 0.4.6。grub4dos应该是从grub1.97修改到windows下的
检测版本:
$ grub-install -v grub-install (GNU GRUB 1.98-1ubuntu10)
GRUB2与GRUB的区别:
1.GRUB2引导菜单启动项是从/boot自动生成的,不是有menu.lst配置的。
2.执行grub-update之后会自动更新启动项列表,自动添加有效的操作系统项目
3.分区编号发生变化:第一个分区现在是1而不是0,但第一个设备仍然以0开始计数,如hd0
配置文件的不同更为明显:
/boot/grub/menu.lst - 已经被/boot/grub/grub.cfg代替。
/boot/grub.cfg - 即使是root也不要编辑它,它在每次update-grub后自动生成。
/etc/default/grub - 改变引导菜单外观的主要配置文件
/etc/grub.d/ - 各种用于生成grub.cfg的脚本文件,每次update-grub时会执行里面的文件

--------------------------------------------------
命令介绍:
ls:
ls (hd0,5)/ > 显示该分区的所有
列出当前的所有设备。如(hd0),(hd0,1),(hd0,5),(hd1),(hd1,1),(hd1,2)等
ls -l 详细列出当前的所有设备。对于分区,会显示其label及uuid。
ls / 列出当前设为root的分区下的文件
ls (hd1,1)/ 列出(hd1,1)分区根目录的文件
root: 指定用于启动系统的分区(也就是根目录 /)。(set root 就是设置root这个环境变量的值)
root (hd0,x)
>挂载硬盘0的 第X个分区,你可以通过ls来查看这个分区的内容
grub2 同样以 fd 表示软盘, hd 表示硬盘(包含 IDE 和 SCSI 硬盘)。
设备是从 0 开始编号,分区则是从 1 开始,主分区从 1-4,逻辑分区从 5 开始,而 grub 分区编号是从 0 开始的。
下面给出几个例子 : (fd0) :表示整个软盘 (hd0,1) :表示 BIOS 中的第一个硬盘的第 1 个分区 (hd0,5)/boot/vmlinuz :表示 BIOS 中的第一个硬盘的第一个逻辑分区下的 boot 目录下的 vmlinuz 文件
loopback:
loopback loop (hd0,3)/ubuntu.iso
这是利用grub的回放设备,挂iso,这样可以使你不用把casper文件夹提取出来了。
search
search -f /ntldr 列出根目录里包含ntldr文件的分区,返回为分区号
search -l LINUX 搜索label是LINUX的分区。
search --set -f /ntldr 搜索根目录包含ntldr文件的分区并设为root,注意如果多个分区含有ntldr文件,
set失去作用。
loopback
loopback命令可用于建立回放设备,如
loopback lo0 (hd1,1)/abc.iso 可以使用lo0设备来访问abc.iso里的内容,比如说,可以从abc.iso里的软盘映像中启动
loopback lo0 (hd1,1)/aa.iso
linux (lo0)/memdisk
initrd (lo0)/abc.img
要删除某一回放设备,可以使用-d参数:
loopback -d lo0
linux
用linux命令取代grub中的kernel命令,kernel 是Grub的一个命令,用来加载Linux内核
kernel /boot/bzImage ro ramdisk_size=65536 root=/dev/ram0
指定根设备是通过内核参数“root=”来完成的
linux /boot/vmlinuz-XXX root=UUID=XXX【告诉内核,从哪里加载必要的一些文件,如果是ISO镜像启动,那肯定这些文件就在这个镜像所在的磁盘分区上,这时指定这个磁盘分区就好了,如果是硬盘上已装的系统,一般指定系统所在路径即可】
linux 是一个指令,它告诉 grub2 程序:“请使用后面给出的这个内核”,于是名为“/boot/vmlinuz-XXX”的内核将被载入内存,并读取“root=UUID=XXX”这个分区的一些必需的文件。【这里传入的内核参数root=UUID=XXX 也可以用 /dev/ram0 这种格式,这里这个路径 是从linux角度出发】
initrd /boot/initrd.img-XXX
initrd 也是一个指令,意思是:先用名称为“/boot/initrd.img-XXX”的文件,在内存中构造一个“虚拟”的根文件系统,以便于内核检测完硬件信息后载入必需的模块等等。
其实这个 initrd 也可以认为是内核的一部分,只是为了保持 vmlinuz 的简洁而分离出来的,有的内核甚至根本不需要这个东东
kernel是什么以及有哪些功能相信大家都很清楚,我就不多说了。这里主要介绍ubuntu10.04上安装的相关软件包、文件、目录以及kernel的配置方法。
至于initrd文件是干什么用的呢?
大家只要做一个简单的实验就可以明白了:在开机的GRUB命令行上去掉initrd这一行,然后按CTRL+x启动,过会你会发现屏幕出现“kernel panic”的提示。
为什么会这样呢?这是因为为了减小kernle文件(vmlinuz)的大小和增强灵活性,现在的发行版如ubuntu默认将硬盘接口卡如IDE,STAT,SCSI的驱动程序以模块(modules)的形式
放在initrd文件中,GRUB中去掉initrd这一行意味着initrd中的modules没有被加载kernel就不能识别该硬盘更别提挂载上面的根文件系统了。

initrd文件的功能主要有两个:
1、提供开机必需的但kernel文件(即vmlinuz)没有提供的驱动模块(modules)
2、负责加载硬盘上的根文件系统并执行其中的/sbin/init程序进而将开机过程持续下去【内核先以initrd /boot/initrd.img-XXX 的内存盘为根文件系统,再去加载硬盘上的根文件系统】
GRUB将kernle加载到内存并执行,kernel在运行的后期会读取并执行initrd文件中的init脚本文件并按照其中命令逐行执行,所以要掌 握initrd的作用和硬盘上的根文件系统加载过程,全面解析init文件是很重要的。在本文的第二部分,我会将/boot/initrd文件打开,逐行 解读init文件.
initrd 详细的介绍:
initrd /boot/initrd.img-XXX,它是由 bootloader 初始化的内存盘。在 linux 启动之前,bootloader 会将它(通常是 initrd.img-xxx…xxx 文件)加载到内存中。内核启动的时候会将这个文件解开,并作为根文件系 统使用。而启动阶段的驱动模块(如jbd)放在这些文件系统上,内核是无法读取文件系统的,从而只能通过Linux initrd启动的虚拟文件系统来装载这些模块。
chainloader
调用另一个启动器,如
chainloader (hd0,1)+1
调用第一硬盘第一分区引导扇区内的启动器,可以是windows或linux的启动器
menuentry
‘Fedora iso’{
setroot=‘hd1,msdos4’
search
–no-floppy --fs-uuid --set=root
1fec4ef0-a925-4711-8de8-c3a332b315c3
loopback lo
/Fedora-18-x86_64-DVD.iso
linux (lo)/images/pxeboot/vmlinuz
repo=hd:/dev/sda4:/ 【可用root=UUID=xxx 用ls -l 看你这个系统所在的磁盘分区的UUID】
initrd (lo)/images/pxeboot/initrd.img
boot
}

1、需要注意的是,我的ISO文件在机器的sda4的硬盘分区内,而我是通过写U盘中的grub2来引导的。U盘中没有Fedoara18 ISO文件,在机器的硬盘内。U盘中只有grub2。
举例说明

BEGIN /etc/grub.d/00_header

load_env
#加载变量,如果在grubenv保存变量,则启动时装载
set default=“0”
#设置默认引导项,默认值为0
insmod ext2
#插入文件系统支持的模块,除了用作启动的分区外,其他分区格式可在menuentry底下再添加
set root=(hd0,8)

指定系统root分区,也就是 / 分区

search --no-floppy --fs-uuid --set 2d61e5f9-1d2a-4167-a6f1-b991ba00878b
#指定uuid=2d61e5f9-1d2a-4167-a6f1-b991ba00878b的分区为root分区,如果前面的分区号(hd0,8)的#uuid与这里的uuid一致,这两句作用一样,如果不一致,则指定uuid的起作用。
if loadfont /usr/share/grub/unicode.pf2 ; then
#设置终端字体,unicode.pf2支持中文字符显示
set gfxmode=640x480
#设置显示分辨率,默认为640x480,可用800x600,1024x768,建议跟你想设定的图片大小一致
insmod gfxterm
#插入终端模块gfxterm,支持中文字符显示和支持24位图像
insmod vbe
#插入vbe模块,GRUB2引入模块化机制,要使用它,需要在这里加入
if terminal_output gfxterm ; then true ; else

For backward compatibility with versions of terminal.mod that don’t

understand terminal_output

terminal gfxterm
#设定grub2终端为gfxterm
fi
fi
if [ ${recordfail} = 1 ]; then
set timeout=-1 # 若有启动失败的记录,则菜单项不再倒计时
else
set timeout=10 #倒计时10秒后进按默认启动项启动
fi
#设定默认启动前等待时间,默认为10秒

END /etc/grub.d/00_header

BEGIN /etc/grub.d/05_debian_theme

set menu_color_normal=white/black
#设定菜单字体及背景颜色
set menu_color_highlight=black/blue
#设定选择项字体及背景颜色
#如果使用默认,背景将完全被蓝色挡住了,需要修改blue为black,背景图片才能显示

END /etc/grub.d/05_debian_theme

BEGIN /etc/grub.d/10_linux

#10_linux为系统自动添加的当前root分区linux引导项
#每个菜单项要包括menuentry双引号" "和大括号{ }才完整,否则不显示菜单
menuentry “Ubuntu, Linux 2.6.31-10-generic” {
set quiet=1
insmod ext2
set root=(hd0,8)
search --no-floppy --fs-uuid --set 2d61e5f9-1d2a-4167-a6f1-b991ba00878b
#这句与set root=(hd0,8)效果一样,可删除其一,二者不一致以这句为准
linux /boot/vmlinuz-2.6.31-10-generic root=UUID=2d61e5f9-1d2a-4167-a6f1-b991ba00878b ro quiet splash
#不喜欢看到一长串的, roo=UUID=***可用root=/dev/sda8(/分区的分区号)代替
initrd /boot/initrd.img-2.6.31-10-generic
}

END /etc/grub.d/10_linux

#20_memtest86+为系统自动添加的内存测试菜单项

BEGIN /etc/grub.d/20_memtest86+

menuentry “Memory test (memtest86+)” {
linux16 /boot/memtest86+.bin
}
menuentry “Memory test (memtest86+, serial console 115200)” {
linux16 /boot/memtest86+.bin console=ttyS0,115200n8
}

END /etc/grub.d/20_memtest86+

#30_os-prober或30_others为系统自动查找并添加其他系统菜单项,按windows,linux,macos顺序
#查找并添加,支持windows 7识别

BEGIN /etc/grub.d/30_os-prober

END /etc/grub.d/30_os-prober

BEGIN /etc/grub.d/30_otheros

This entry automatically added by the Debian installer for a non-linux OS

on /dev/sda1

menuentry “Microsoft Windows XP Professional” {
set root=(hd0,1)
search --no-floppy --fs-uuid --set e852-230b
drivemap -s (hd0) $root
#对以ntldr引导的系统如win2000,xp,win2003,因其引导机制只能从第一硬盘启动,系统会自动添加#映射命令,对vista和win7就没有这句命令
chainloader +1
}

END /etc/grub.d/30_otheros

#40_custom为自定义的启动项,如启动cdlinux

BEGIN /etc/grub.d/40_custom

menuentry “CDLinux”{
set root=(hd0,8)
linux /CDlinux/bzImage root=/dev/ram0 vga=791 CDL_LANG=zh_CN.UTF-8
initrd /CDlinux/initrd
}

END /etc/grub.d/40_custom

定制个性化的配置文件,可以加入背景图片,使用中文字符,让启动画面独具特色,而不是单调的黑、白、蓝三色。下面是一张经美化启动画面:
kernel被GRUB加载经内存并执行后会进一步加载initrd文件,它是按照initrd文件中提供的init脚本一步步执行的,所以要掌握initrd文件的执行过程就必须要搞清楚init文件的内容和作用。下面我会重点介绍init文件。

1、首先什么是 initial ram disk (缩写 ) initrd
它是由 bootloader 初始化的内存盘。在 linux 启动之前,bootloader 会将它(通常是 initrd.img-xxx…xxx 文件)加载到内存中。内核启动的时候会将这个文件解开,并作为根文件系 统使用。而启动阶段的驱动模块(如jbd)放在这些文件系统上,内核是无法读取文件系统的,从而只能通过Linux initrd启动的虚拟文件系统来装载这些模块。这里有些人会问: 既然内核此时不能读取文件系统,那内核的文件是怎么装入内存中的呢?答案很简单,Grub是file-system sensitive的,能够识别常见的文件系统。
2、设 计 initrd 的主要目的
目的是让系统的启动分为两个阶段。首先,带有最少但是必要的驱动(这些驱动是在配置内核时选择嵌入方式)的内核启动。然后,其它需要的模块将 从 initrd 中根据实际需要加载(使用udev机制,最重要的根文件系统所在硬盘的控制器接口module)。这样就可以不必将所有的驱动都编译进内核,而根据实际情况有选择地加载。对于启动较慢的设备 如 usb 设备等,如果将驱动编译进内核,当内核访问其上的文件系统时,通常设备还没有准备好,就会造成访问失败。所以,通常 在 initrd 中加载 usb 驱动,然后休眠几秒钟,带设备初始化完成后,再挂载其中的文件系统。
3、系统上安装的相关软件包
geekard@geekard-laptop:~$ dpkg -l *ini* |grep ii
ii busybox-initramfs 1:1.13.3-1ubuntu11 Standalone shell setup for initramfs
ii initramfs-tools 0.92bubuntu78 tools for generating an initramfs
ii initramfs-tools-bin 0.92bubuntu78 binaries used by initramfs-tools
geekard@geekard-laptop:~$

BusyBox combines tiny versions of many common UNIX utilities into a single small executable. It provides minimalist replacements for the most common
utilities you would usually find on your desktop system (i.e., ls, cp, mv, mount, tar, etc.). The utilities in BusyBox generally have fewer options than
their full-featured GNU cousins; however, the options that are included provide the expected functionality and behave very much like their GNU
counterparts.
initramfs-tools-bin软件包提供initrd文件制作工具命令,但更好的方法是手动制作(下面我会详细介绍)。

4、initrd 的具体形式
目前有两种形式:cpio-initrd 和 image-initrd。
image- initrd 的制作相对麻烦,处理流程相对复杂(内核空间->用户空间->内核空间 与初始化越来越多的在用户空间进 行的趋势不符),主要是2.4及以前的kernle使用,本文不对其进行介绍。

cpio- initrd 的处理流程(内核空间->用户空间):
1. boot loader 把内核以 及 initrd 文件加载到内存的特定位置。
2. 内核判断 initrd 的文件格式,如果 是 cpio 格式。
3. 将 initrd 的内容释放 到 rootfs 中。
4. 执行 initrd 中 的 /init 文件,执行到这一点,内核的工作全部结束,完全交给 /init 文件处 理。

cpio- initrd 的制作:
首先在一个目录中建立必要的文件及目录。例如:
song@ubuntu:/home/linux_src/initrd/debian_etch/initrd$ ls -l
总用量 5
drwxr-xr-x 2 song song 864 2007-05-01 21:37 bin
drwxr-xr-x 3 song song 160 2007-05-01 21:37 conf
drwxr-xr-x 4 song song 136 2007-05-01 21:37 etc
-rwxr-xr-x 1 song song 3233 2007-05-02 15:16 init
drwxr-xr-x 4 song song 416 2007-05-01 21:37 lib
drwxr-xr-x 2 song song 48 2007-04-14 15:59 modules
drwxr-xr-x 2 song song 208 2007-05-01 21:37 sbin
drwxr-xr-x 11 song song 400 2007-05-01 21:37 scripts
然后,将这些内容打成 gzip 压缩过的 cpio 包:
song@ubuntu:/home/linux_src/initrd/debian_etch/initrd$ find . | cpio -o -H newc | gzip -9 >…/initrd.img.gz
20500 blocks
song@ubuntu:/home/linux_src/initrd/debian_etch/initrd$ ls -l …/initrd.img.gz
-rw-r–r-- 1 song song 4493175 2007-05-02 17:17 …/initrd.img.gz

initrd文件的解包:
首先建立一个空目录,然 后进入那个目录,并运行相应的命令。例如,在 /home/linux_src/initrd/debian_etch 目录下存 在 initrd.img-2.6.18-4-686 文件,我们现在要把它解开,过程如下:
song@ubuntu:/home/linux_src/initrd/debian_etch$ mkdir tmp
song@ubuntu:/home/linux_src/initrd/debian_etch$ cd tmp
song@ubuntu:/home/linux_src/initrd/debian_etch/tmp$ gzip -dc …/initrd.img-2.6.18-4-686 | cpio -idm
20500 blocks
song@ubuntu:/home/linux_src/initrd/debian_etch/tmp$ ls -l
总用量 5
drwxr-xr-x 2 song song 864 2007-05-02 17:23 bin
drwxr-xr-x 3 song song 160 2007-05-02 17:23 conf
drwxr-xr-x 4 song song 136 2007-05-02 17:23 etc
-rwxr-xr-x 1 song song 3213 2007-03-08 06:30 init
drwxr-xr-x 4 song song 416 2007-05-02 17:23 lib
drwxr-xr-x 2 song song 48 2007-04-14 15:59 modules
drwxr-xr-x 2 song song 208 2007-05-02 17:23 sbin
drwxr-xr-x 11 song song 400 2007-05-02 17:23 scripts

initrd 中 init 脚本的分析
由前面 cpio-initrd 的处理流程可以看到,内核在将其解开并放入 rootfs 后,将要执 行 /init 文件,所以我们分析的重点就是这个文件。其它的文件请结合具体的源码与本文的内容进行理 解。
#!/bin/sh
该行说明该init文件是一个由sh解释并执行的脚本文件,内核通过文件头来确定应该怎样执行(即是直接执行还是调用哪个程序执行该文件)。
[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir -m 0700 /root #这是硬盘上的根分区预先挂载到的目录
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
mkdir -p /var/lock
mount -t sysfs -o nodev,noexec,nosuid none /sys #udev会参考的vfs,udev会根据其中的信息加载modules和创建设备文件
mount -t proc -o nodev,noexec,nosuid none /proc
这一部分很简单,建立了相关目录和挂载点,并将kernel运行过程中产生的信息挂载到/sys和/proc目录下。注意/sys目录是udev会参考的 vfs,udev会根据其中的信息加载modules和创建设备文件,当不使用udev机制(我后面会讲)时/sys目录可以不建立。/proc目录和相 应的proc文件系统必须建立和挂载,因为脚本会参考其中的/proc/cmdline文件获得kernel命令行上的参数。
grep -q ‘’ /proc/cmdline || echo “Loading, please wait…”
如果在GRUB的kernel行上有quiet关键字,则在kernel启动和initrd中init脚本执行的过程中不会在屏幕上显示相关信息而是一个闪烁的下划线,否则将显示"Loading, please wait…"

Note that this only becomes /dev on the real filesystem if udev’s scripts

are used; which they will be, but it’s worth pointing out

if ! mount -t devtmpfs -o mode=0755 none /dev; then #其实then的代码一般不会执行
mount -t tmpfs -o mode=0755 none /dev
mknod -m 0600 /dev/console c 5 1
mknod /dev/null c 1 3
fi

这一部分在/dev目录下建立devtmpfs文件系统,devtmpfs是一个虚拟的文件系统被挂载后会自动在/dev目录生成很多常见的设备节点文 件,当挂载devtmpfs失败时会手动建立/dev/console和/dev/null设备节点文件。/dev/console 总是代表当前终端,用于输出kernel启动时的输出内容,在最后通 过 exec 命令用指定程序替换当前 shell 时使用。/dev/null 也是很常用的,凡是重定向到它的数据都将消失得无影无踪。

mkdir /dev/pts #主要用于nfs等启动时使用,对于本地/dev/pts不使用,故下面一段代码可忽略
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 none /dev/pts || true

/dev/.initramfs-tools
mkdir /dev/.initramfs
usplash 会使用/dev/.initramfs 目录。usplash 会在机器启动的时候提供类似 windows的启动画面,ubuntu linux 的启动画面就是通过 usplash 实现的。由于 在 /sbin 目录当中没有任何 usplash 相关的文件,所以我们可以忽略这个目录的存 在。

Export the dpkg architecture

export DPKG_ARCH=
. /conf/arch.conf
DPKG_ARCH 表明了当前运行linux的计算机的类型,对一般的pc是大多 i386,也可能是别的比如 powerpc 一类的。用export是为了让这个变量不仅在此 shell环境中有效,而且在它的子 shell环境中仍然有效。而且在第27行 export DPKG_ARCH 变量的时候,让 DPKG_ARCH 变量等于空。这样,当前运行的计算机 的类型就完全由 /conf/arch.conf 决定了

Set modprobe env

export MODPROBE_OPTIONS="-qb"
设置modprobe默认的选项,-b 表示用use-blacklist(主要是系统的硬件有多个modules支持,选择使用哪一个,其他的加入到黑名单中以防冲突) ,-q表示quiet

Export relevant variables

export ROOT= #从kernel中提取的realfs所在的设备
export ROOTDELAY=
export ROOTFLAGS=
export ROOTFSTYPE=
export IPOPTS=
export HWADDR=
export break=
export init=/sbin/init #realfs中的第一个执行的程序位置
export quiet=n
export readonly=y
export rootmnt=/root #realfs在rootfs中的临时挂载点
export debug= #将本脚本的输出定向到一个文件,以便启动系统后分析
export panic=
export blacklist= #设置modules的黑名单
export resume_offset=

这一部分主要是输出一些变量到环境中
ROOT 保存GRUB的kernel命令行上的root参数即硬盘上根分区所对应的设备节点文件
ROOTDELAY 指定将要进入的系统的根目录所在的分区必 须在多少秒之内准备好
ROOTFLAGS 指定将要进入的系统的根目录所在的分区挂 载到 ${rootmnt} 目录时的参数
ROOTFSTYPE 指定根分区所在的文件系统类型
IPOPTS 当kernel为nfs时指定的服务器IP
HWADDR 当kernel为nfs时指定的服务器MAC
break 由 maybe_break 函数使用。若 break 的值同 maybe_break 的第一个参数相同,则 maybe_break 函数调 用 panic 函数(注意 panic 函数和 panic 变量是不同 的)。 若 panic 变量为"0"(此 处是字符串,其内容是"0",不是整数), 则 panic 函数将重新启动机器。其他情况下(包括 panic 变量为空的情况)都将以交互的方式调出 shell,此shell的输入输出使用已经创建好的节点 /dev/console。
init 指定硬盘realfs中的第一个执行的程序位置, 此变量指定在这个脚本最后要执行的进程。 此处 /sbin/init 是系统上所有进程的父进程,负责开启其它进程。当然,你也可以把它换成其他的程序,甚至是 ls,不一定非要是 /sbin/init,虽然这样你的系统启动之后什么都不能做。
quiet=n 指定为非"y",会显示一些启动的状态信息;若指定为"y"则不显示这些信息。
readonly=y 如果 readonly 等于字符串"y",则以只读方式挂 载最终要进入的系统的根目录所在分区到 ${rootmnt} 目录,其他情况(包括 readonly 为 空)以读写方式挂载。
rootmnt=/root 指定硬盘上的realfs在rootfs中的临时挂载点
debug #将本脚本的输出定向到一个文件,以便启动系统后分析
panic 描述见 break参数的说明。
blacklist #设置modules的黑名单
resume_offset 一般用不到

Bring in the main config

. /conf/initramfs.conf #最重要的是BOOT参数,定义了是本地启动还是nfs启动
for conf in conf/conf.d/*; do #对于不支持kernel命令行选项的bootloader很有用
[ -f ${conf} ] && . ${conf}
done
. /scripts/functions #这个脚本文件很重要,在其中定义了很多以后要用到的工具函数

在当前shell中引入主配置文件 /conf/initramfs.conf。 这个配置文件实际上是 mkinitramfs(8) 的配置文件,其中定义了一些变量,并赋予了适当的值,如 BOOT=local 则默认从本地磁盘启动(可以是可移动磁盘)。 BOOT 变量的值实际上是 /scripts 目录下的一个文件,可以是 local 或 是 nfs。在此 init 脚本挂载将要进入的系统的根目录所在分区的时候,会先读取并运 行 /scripts/${BOOT} 文件。 在这个文件中定义了 mountroot 函数,对于 local 启动和 nfs 启动 此函数的实现不同。这样通过对不同情况引入不同的文件,来达到同样名称的函数行为不同的目的。这就导致了具体挂载的行为和启动方式相关。

引入 /conf/conf.d 下的所有文件,注意在引入的时候用了 -f 参数判断,这样只有普通的文件才 会被引入,链接(硬链接除外)、目录之类的非普通文件不会被引入,当使用不支持命令行参数的开机引导
程序时,可以在该目录下建立各种参数设置文件。(Uubunt使用的GRUB支持kernel命令行参数,所以这个目录下就上面提到的两个文件initramfs.conf和arch.conf)

按照/scripts/init-top/ORDER文件的配置依次执行其下的脚本文件,这里最重要的是开启了udev daemon
udev 以 daemon 的方式启动 udevd,接着执行 udevtrigger 触发在机器启动前已 经接入系统的设备的 uevent,然后调用 udevsettle 等待,直到当 前 events 都被处理完毕。之后,如果 ROOTDELAY 变量不为空,就 sleep ROOTDELAY 秒以等待 usb/firewire disks 准备 好。

maybe_break modules
log_begin_msg “Loading essential drivers…”
load_modules
log_end_msg

load_modules按照/conf/modules文件的配置加载重要的module,由于使用了udev机制这一步其实是多余的,实际上/conf/modules文件并不存在。
maybe_break premount
[ “KaTeX parse error: Expected 'EOF', got '&' at position 17: …uiet" != "y" ] &̲& log_begin_msg…quiet” != “y” ] && log_end_msg
这段代码从字面上理解是为接下来挂载将要使用的系统的根目录所在的分区作准备,/scripts/init-premount实际并不存在,所以这一步其实啥都没干。

maybe_break mount #这一步主要是执行/scripts/local中的mountroot函数,将realfs挂载到 r o o t m n t , 在 m o u n t r o o t 函 数 中 最 重 要 的 是 检 测 r e a l f s 的 f y t y p e l o g b e g i n m s g " M o u n t i n g r o o t f i l e s y s t e m . . . " . / s c r i p t s / {rootmnt},在mountroot函数中最重要的是检测realfs的fytype log_begin_msg "Mounting root file system..." . /scripts/ rootmnt,mountrootrealfsfytypelogbeginmsg"Mountingrootfilesystem..."./scripts/{BOOT}
parse_numeric KaTeX parse error: Expected 'EOF', got '#' at position 17: …ROOT} #̲这一步主要用于解析lilo启动…{rootmnt}特别重要,大部分的系统启动问题都是这一部分存在问题引起的。其中很大一部分的原因是硬盘控制器的module
没有被udev或上面的load_modules加载,导致kernel不能读取硬盘中的数据。

下面我们来看一下 /scripts/local 中定义的 mountroot 函数是如何工作 的。
首先,它通 过 run_scripts 函数执行 /scripts/local-top 目录下所有具有可执行权限的文件。在这个目录下有3个文件:lvm,mdrun 和 udev_helper。
lvm 是逻辑卷管理方面的脚本,我没有过(估计一般pc很少有人会用),而且其中调用的具有可执行权限的文件在此 initrd.img 中也不存在。因为这个脚本在运行的时候会先检查需要的文件是否存在,若不存在则退出,所以这个脚本相当于什么也没做。略过。
mdrun 是 raid 方面的脚本。它要求 udev_helper 先被执行(见第136行代码的说明)其中用到的具有可执行权限的文件在 此 initrd.img 中不存在。这等效于这个脚本不起作用。
udev_helper 脚本 mdrun 的先决条件,根据实际情况 ide-generic 模块可能会被加 载。
在这三个脚本执行过之 后,mountroot 函数会查看 ROOT 设备节点是否已经存在,如果不存在将等 ${ROOTDELAY} 秒。若在这段时间内 ROOT 设备节点没有出现则调 用 panic 函数重启机器或是生一 个交互 shell。
若 ROOT 设备节点已经存在,则查看 ROOTFSTYPE 变量是否为空。若不空, 则 FSTYPE 变量的值就是 ${ROOTFSTYPE};否则通过 eval 用 fstype 命令得到 ROOT 的分区格式。其中,fstype 命令会输 出 FSTYPE=blabla 类型的字符串,它跟在 eval 后面就相当于作了 FSTYPE=blabla 这样的赋值操作。如果经过这一步之后 ROOTFSTYPE 的值是 “unknown”(包括通过在 kernel 后添 加 rootfstype=unknown 参数和 fstype 输出的 FSTYPE=unknown),则 mountroot 函数调用 /lib/udev/vol_id 得到 分区的格式。此时,FSTYPE 的值仍有可能是 “unknown”。 如果是这样的话,在最后的 mount 操作就会失败。或许你会觉得这里要判断分区格式是不是很麻烦。是的,确实如此。但是要知道这 里的 mount 不会自己判断分区格式,所以要在参数中指定。
在得到了 FSTYPE 之后,mountroot 函数调用 run_scripts 函数运 行 /scripts/local-premount 下面具有可执行权限的文件。
在 /scripts/local-premount 目录中只有一个具有可执行权限的脚本 resume。此脚本负责在 计算机休眠后恢复休眠前的状态。若 resume 变量为空或者这个变量所指的设备不存在,则直接退出;否则,运 行 /bin/resume 恢复状态。
在这之后,mountroot 函数根据变量 readonly 确定是以只读还是读写的方式挂载,根 据 FSTYPE 变量加载适当得内核模块。在得到了所有必要的参数之后,通过 mount 命令将将要进入的 系统的根目录所在的分区挂载到 ${rootmnt} 目录下。
最后,mountroot 函数通过 run_scripts 函数执行 /scripts/local- bottom 下具有可执行权限的文件。由于在此目录下没有文件,所以这一步什么都没有做。
parse_numeric 函数 ( /scripts/functions 中定义)从它的注释中可以看出,这个是为了和 lilo 兼容而存 在的。由于现在一般用 grub 作为 bootloader,我们平常写的 root=/dev /hdxx,root=LABEL=xx…xx 或 root=UUID=x…x-…-xxx 的形式都会造 成此函数的直接返回,相当于什么都没有做。由于我没有用过 lilo,所以对于下面 lilo 的处理,我也不好说什 么。

小 结
好了,上面我已经说了这么多。那么,init 脚本究竟都作了什么呢?
首先,建立一些必要的文件夹作为程序工作的时候需要的目录或者必要的挂载点,以及必需的设备节点。
然后,根据提供的参数建立适当的设备节点并加载适当的内核模块,启动适当的进程(udevd)帮助我们完成这一步骤。当没有使用udev机制时应在/conf/modules中指明要加载的驱动。同时要自己建好相关的设备节点。
最后,在做完了这些乱七八糟的为挂载根目录及运行 /sbin/init 进程作准备的事情之后,调用 run- init 来运行 /sbin/init 从而启动我们的系统。
由以上可以看出Ubunt为了兼容各种不同的硬件配置将该init脚本写的非常繁琐,在实际应用中我们可以根据自己主机的情况定制该文件。


boot里面的启动项是根据其它文件生成的,如果改boot里面,会在你更新grub后再次回到原来的状态。(之后 我(有显卡驱动问题的用户)通过在开机时选择系统页面按e在倒数第二行ro quiet splash 后面加上acpi_osi=! acpi_osi="Windows 2009"可以从最新内核进入系统。在/etc/default/grub 文件里面相同位置也加上这一句后会让系统不能自己更新grub,之后你就可以在boot/grub里面直接更改启动项内容而不必担心系统回滚)
更改启动项主要从两个文件夹改:/etc/default/grub和/etc/grub.d,下面解释一下这两个文件夹的内容。.
1./etc/default/grub:

GRUB_BACKGROUND="/boot/grub/themes/deepin/background.png" #背景图片的路径
GRUB_CMDLINE_LINUX_DEFAULT=“splash quiet” #开机的开机动画(貌似是,需要加载显卡)有显卡驱动问题的可以在后面加上 nomodeset(此选项只会追加在一般模式后)
GRUB_DEFAULT=0 #默认启动项,这个值为0就是默认启动第一个,为1默认启动第二个
GRUB_DISABLE_RECOVERY=“true” #禁止显示救援模式(这个不太懂)
GRUB_DISTRIBUTOR="/usr/bin/lsb_release -d -s 2>/dev/null || echo Deepin" #获得发行版本(此行将追加到所有的linux 定义内核行的后面,不论是救援模式还是一般模式)
GRUB_GFXMODE=“1920x1080” #启动的分辨率
GRUB_THEME="/boot/grub/themes/deepin/theme.txt" #启动的主题,是各种图片和各种显示的字体
GRUB_TIMEOUT=5 #等待时间,5秒未操作直接进入默认系统。改为-1是一直等待。

2./etc/grub.d:

00_header # 配置初始的显示项目,如默认选项,时间限制等,一般由/etc/default/grub导入,一般不需要配置
05_debian_theme # 配置引导画面,文字颜色等主题
10_linux #定位当前操作系统使用中的root设备内核的位置,包含deepin 启动项和advanced里面的启动项
15_linux_bar # 救援模式的启动项
20_linux_xen # 虚拟机监视器的东西,(暂时不知有什么用
30_uefi-firmware # “system setup” 的启动项
35_os-prober # windows的启动项一般在这个里面
40_custom # 用来加入用户自定义的启动项,将会在执行update-grub时更新至grub.cfg中
41_custom # 判断custom.cfg此配置文件是否存在,如果存在就加载它
前面的数字是对文件排列执行的顺序进行排序,可进行更改,比如你想把windows启动项调到第一个,就把35_os-prober前面那个数字改成5到10的数字,比如06、07、08、09.

3.想更改deepin系统的启动内核(有这个需求是不少人在新内核上有显卡驱动问题,而从advanced里面进不能默认进入)
(deepin默认的应该是最新的启动内核,你在boot/grub/grub.cfg里面更改的话只要一更新grub就会回到原来的内核)因此是要改10_linux文件的,但是里面是汇编命令看不懂,
这时40_custom 提供了一个在启动页面加一个新的启动项的简单方法,具体操作如下:

1.打开boot/grub/grub.cfg,找到你默认的启动项(或者你想要改到外面的advanced里面的启动项)(这些启动项都在10_linux里面),大概如下:
menuentry ‘Deepin 15.6 GNU/Linux(名字在这里改)’ --class deepin --class gnu-linux --class gnu --class os m e n u e n t r y i d o p t i o n ′ g n u l i n u x − s i m p l e − 6873 b a b 1 − c d f 1 − 4931 − 8717 − d 2258 c b 3 a d 8 7 ′ l o a d v i d e o i n s m o d g z i o i f [ x menuentry_id_option 'gnulinux-simple-6873bab1-cdf1-4931-8717-d2258cb3ad87' load_video insmod gzio if [ x menuentryidoptiongnulinuxsimple6873bab1cdf149318717d2258cb3ad87loadvideoinsmodgzioif[xgrub_platform = xxen ]; then insmod xzio; insmod lzopio; fi
insmod part_gpt
insmod ext2
set root=‘hd0,gpt4’
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt4 --hint-efi=hd0,gpt4 --hint-baremetal=ahci0,gpt4 6873bab1-cdf1-4931-8717-d2258cb3ad87
else
search --no-floppy --fs-uuid --set=root 6873bab1-cdf1-4931-8717-d2258cb3ad87
fi
linux /boot/vmlinuz-4.15.0-21deepin-generic root=UUID=6873bab1-cdf1-4931-8717-d2258cb3ad87 ro splash quiet
initrd /boot/initrd.img-4.15.0-21deepin-generic
}
2.复制粘贴到40_custom那三行字下面(需要以管理员身份打开)
3.然后把名字改一下(为了避免重复嘛,你要是在advanced里面复制的就不用改了),最后两行是启动内核,改一下(当然,你要是复制的advanced里面你想改的内核启动项就直接粘贴就行了)
4.保存 之后sudo update-grub就可以了。之后再根据你的需要改顺序和默认启动项就行。

你可能感兴趣的:(linux-disk)