本文以是我的学习记录,其中一些文字和图片来自参考资料所列文档,感谢作者对其知识和分享!
最近在自学 Linux kernel 方面的东西,这两天了粗浅的研究了下 kernel boot 过程,在此记录。这里所指 Linux 引导加载未涉及虚拟化环境,即系统未运行在 hypervisor 之上。
Linux 通过执行不同阶段的引导加载程序(boot loader)程序来引导操作系统,在完成内核等引导之后,最终会由调度器接管 CPU,其通过启用中断来周期性的抢占控制权,处理多个用户进程/客户进程(kvm 虚拟化)。Top level 的引导过程如下图。
整个 Linux 系统引导共分 5 步执行操作:
- BIOS/BootMonitor 引导程序;BIOS 包括 POST 和 Runtime 服务。
- 被称为第一阶段的 MBR (Master boot record)引导程序;位于 BIOS 配置的启动磁盘 0 柱面 1 扇区的主引导记录,用于启动第二阶段的 linux boot loader。
- 被称为第二阶段的 linux boot loader;主要有 LILO (Linux loader)和 GNU GRUB (Grand unified boot loader)两种 boot loader 程序,现主流为 GRUB。最后会将默认的内核映像和 initrd 映像加载到内存中。
- linux kernel (及 initrd 函数)引导;负责初使化、解压 zImage/bzImage kernel 及 gzip/cpio initrd 映像,并开始执行 kernel 初使化和引导程序/过程,最后会创建 RAM 盘,加载 initrd 并执行 init 脚本,通过 LKM (linux kernel
module)加载本地磁盘等驱动程序来挂载磁盘中的 root 文件系统。RAM 盘中是个完整的小型 linux
环境,在没有磁盘的嵌入式环境中,initrd 可以是最终的根文件系统,也可以通过 NFS 来挂载最终的文件系统。
- init 进程。用于启动 linux 配置的各项用户空间服务(demon)进程。
加电后首先被执行的是 BIOS (Base input/output system)程序。
嵌入式环境使用 boot monitor,它负责在一个位于 rom/flash 中预定地址开始执行引导程序,而在 PC 环境中这个启动地址是 0xFFFF0,相对来讲 BIOS 提供了更多的配置功能。它主要由两部分组成:
- POST (Power On Self Test)程序;其负责接通电源时对硬件检测,包括创建中断向量、设置寄存器、对一些外部设备进行初始化和检测等。
- BIOS Runtime 服务;负责为操作系统提供一些基础服务,主要与IO外设有关。
当 BIOS POST 执行完后,其将会从内存中清理,而 Runtime 服务会常驻内存,为操作系统提供一些底层的支持。最后 BIOS 将控制权交给称为第一阶段引导程序的 MBR (Master boot record)程序。
接下来执行的 MBR 是一个512 byte 固定大小的映像。
包括 446 byte 长的被称为初始程序加裁程序 (Initial program loader, IPL)的可执行代码和 64 byte 分区表(16 byte * 4 个),最后以 0xaa55 特殊字节结束。如下图所示。
MBR 引导程序会将扫描分区表,获得唯一活动分区后,将其中的引导程序读入 RAM 并开始执行。
MBR 启动的引导程序被称为第二阶段引导程序,它是引导的主体,是引导加载的真正部分。
Linux 中该阶段有两个流行的程序,LILO (较老)和 GRUB。如果安装了 lilo 程序,可以通过 root 用户执行如下命令来通过 lilo 生成默认配置的 MBR ,并写入到启动磁盘 0 柱面 1扇区位置上。
# /sbin/lilo -v -v
一般需要修改 lilo 的配置文件,使生成的 MBR 有效。位于 /etc/lilo.conf 。lilo 配置示例。
boot=/dev/hda
map=/boot/map
install=/boot/boot.b
prompt
timeout=100
compact
default=Linux
image=/boot/vmlinuz-2.4.18-14
label=Linux
root=/dev/hdb3
read-only
password=linux
other=/dev/hda
label=WindowsXP
boot 键指定了 lilo 在哪里安装 MBR。可以通过替换 boot=/dev/fd0 配置来指定 lilo 创建有引导记录的软盘。
LILO 天生存在一些缺点和不足,因此 linux 在新版本中引入了 GRUB 程序。
它为了从磁盘来加裁配置和 kernel 映像,不像 LILO 只能从裸扇区中执行引导程序,而具有了访问磁盘文件系统(ext2/3、reiserfs、
jfs、fat 等)的能力。GRUB 是通过引入所谓 1.5 阶段的引导加载程序来实现这项功能的,在该阶段中,GRUB 主要来加载特殊的文件系统驱动。此后,阶段 2 的引导加载程序就可以进行加载了。
一般 GRUB 有一个不错的 GUI 界面,其中通过分析配置文件来显示了一此引导选项。在我的 ubuntu 8.10 系统中,该配置文件位于 /boot/grub/menu.lst。我们可以选择内核甚至修改附加内核参数,甚至可以使用 GRUB shell 对引导过程进行高级手工控制。我的 menu.lst 文件内容如下。
default 0
timeout 3
hiddenmenu
title Ubuntu 8.10, kernel 2.6.27-11-generic
uuid e2cf53c5-11de-4d57-a532-878901afd9b4
kernel /boot/vmlinuz-2.6.27-11-generic root=UUID=e2cf53c5-11de-4d57-a532-878901afd9b4 ro locale=zh_CN quiet splash
initrd /boot/initrd.img-2.6.27-11-generic
quiet
title Ubuntu 8.10, kernel 2.6.27-11-generic (recovery mode)
uuid e2cf53c5-11de-4d57-a532-878901afd9b4
kernel /boot/vmlinuz-2.6.27-11-generic root=UUID=e2cf53c5-11de-4d57-a532-878901afd9b4 ro locale=zh_CN single
initrd /boot/initrd.img-2.6.27-11-generic
title Ubuntu 8.10, kernel 2.6.27-7-generic
uuid e2cf53c5-11de-4d57-a532-878901afd9b4
kernel /boot/vmlinuz-2.6.27-7-generic root=UUID=e2cf53c5-11de-4d57-a532-878901afd9b4 ro locale=zh_CN quiet splash
initrd /boot/initrd.img-2.6.27-7-generic
quiet
title Ubuntu 8.10, kernel 2.6.27-7-generic (recovery mode)
uuid e2cf53c5-11de-4d57-a532-878901afd9b4
kernel /boot/vmlinuz-2.6.27-7-generic root=UUID=e2cf53c5-11de-4d57-a532-878901afd9b4 ro locale=zh_CN single
initrd /boot/initrd.img-2.6.27-7-generic
title Ubuntu 8.10, memtest86+
uuid e2cf53c5-11de-4d57-a532-878901afd9b4
kernel /boot/memtest86+.bin
quiet
将第二阶段的引导加载程序加载到内存中之后,就可以对文件系统进行查询了,并将默认的内核映像和 initrd 映像加载到内存中。当这些映像文件准备好之后,阶段 2 的引导加载程序就可以调用内核映像。 正如上面配置文件描述的那样,我的 ubuntu 启动会将加载 /boot/vmlinuz-2.6.27-11-generic (zImage/bzImage 格式的 kernel 映像)和 /boot/initrd.img-2.6.27-11-generic (cpio 格式的 initrd 映像)。
接下来就是 kernel 引导加载过程,这个过程包括如下 6 步。
- 执行一个对硬件做些基本设置的例程;
(./arch/i386/boot/head.S 中的 start 例程)
- 设置一个基本的环境(堆栈等),并清除 Block Started by Symbol(BSS);
(./arch/i386/boot/compressed/head.S 中的 startup_32 例程)
- 通过连接在映像中的函数来解压内核;
(./arch/i386/boot/compressed/misc.c 中的 decompress_kernel C 函数)
- 启动 swapper (0 进程)进程,初始化页表,启用 CPU 内存分页。然后会为任何可选的浮点单元(FPU)检测 CPU 的类型,并将其存储起来供以后使用;
(./arch/i386/kernel/head.S 中的 startup_32 函数)
- 调用 linux kernl main 函数,进入与体系结构无关的 Linux 内核部分。
(init/main.c 中的 start_kernel 函数 )
这会调用一系列初始化函数来设置中断,执行进一步的内存配置,并将 initrd 复制到 RAM 盘中并挂载到 kernel。最后启动 init 进程,这是第一个用户空间进程(user-space process);
(./arch/i386/kernel/process.c 中的 kernel_thread)
- 最后,启动空任务。现在调度器就可以接管控制权了(在调用 cpu_idle 之后)。通过启用中断,抢占式的调度器就可以周期性地接管控制权,从而提供多任务处理能力。
上面第 5 步加载的 RAM 盘(initrd)是由阶段 2 引导加载程序加载到内存中的,RAM 盘内的微型系统用来加载必要的磁盘驱动内核模块,最终挂载真正磁盘的 root 文件系统。
引导加载的最后的一步就是执行 init (1 进程),该进程会根据配置来启动服务。
一般的配置都会写在 inittab 里,不过我这里用的 ubuntu 使用的是 upstart,它是基于事件驱动的,发生什么 event 怎么处理,在这里 init 进程会产生 startup event, upstart 据此来启动 rc.* 配置的进程。不过无论如何,此时引导加载程序已经放权了。
这里抄录一段 LILO 与 GURB 的优缺点对比。
- LILO 没有交互式命令界面,而 GRUB 拥有。
- LILO 不支持网络引导,而 GRUB 支持。
- LILO 将关于可以引导的操作系统位置的信息物理上存储在 MBR 中。如果修改了 LILO 配置文件,必须将 LILO 第一阶段引导加载程序重写到 MBR。相对于 GRUB,这是一个更为危险的选择,因为错误配置的 MBR 可能会让系统无法引导。使用 GRUB,如果配置文件配置错误,则只是默认转到 GRUB 命令行界面。
关于 kernel 和 initrd 两个映像。
技术含量很高的,嵌入式开发中 bootloader 可是很大一块。值得深入,只可惜现在的技术水平,哎~
- kernel /boot/vmlinuz-2.6.27-7-generic
- initrd /boot/initrd.img-2.6.27-7-generic
initrd 映像是打包的 RAM 盘根文件系统。
一般 initrd.img-2.6.27-7-generic 是一个 cpio 包文件,老版本也有 gzip 压缩格式的。通过 cpio 命令将其解包到当前目录中,如下。cpio 使用方法可参见 cpio 命令详解
。
zcat initrd.img-2.6.27-11-generic | cpio -i -d --no-absolute-filenames
在我这里解包后的根文件系统包括如下内容。
从上面的 directory tree 可以看到 initrd 中主要包括的就是磁盘、网络、文件系统的驱动 lkm 文件。其中还有最主要是的 init shell 脚本,它包括了初使化的全过程。
#!/bin/sh
echo "Loading, please wait..."
[ -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
mount -t proc -o nodev,noexec,nosuid none /proc
# 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
mount -t tmpfs -o mode=0755 udev /dev
[ -e /dev/console ] || mknod -m 0600 /dev/console c 5 1
[ -e /dev/null ] || mknod /dev/null c 1 3
> /dev/.initramfs-tools
mkdir /dev/.initramfs
# Export the dpkg architecture
export DPKG_ARCH=
. /conf/arch.conf
# Set modprobe env
export MODPROBE_OPTIONS="-Qb"
# Export relevant variables
export ROOT=
export ROOTDELAY=
export ROOTFLAGS=
export ROOTFSTYPE=
export break=
export init=/sbin/init
export quiet=n
export readonly=y
export rootmnt=/root
export debug=
export panic=
export blacklist=
export resume_offset=
# Bring in the main config
. /conf/initramfs.conf
for conf in conf/conf.d/*; do
[ -f ${conf} ] && . ${conf}
done
. /scripts/functions
# Parse command line options
for x in $(cat /proc/cmdline); do
case $x in
init=*)
init=${x#init=}
;;
root=*)
ROOT=${x#root=}
case $ROOT in
LABEL=*)
ROOT="/dev/disk/by-label/${ROOT#LABEL=}"
;;
UUID=*)
ROOT="/dev/disk/by-uuid/${ROOT#UUID=}"
;;
/dev/nfs)
[ -z "${BOOT}" ] && BOOT=nfs
;;
esac
;;
rootflags=*)
ROOTFLAGS="-o ${x#rootflags=}"
;;
rootfstype=*)
ROOTFSTYPE="${x#rootfstype=}"
;;
rootdelay=*)
ROOTDELAY="${x#rootdelay=}"
case ${ROOTDELAY} in
*[![:digit:].]*)
ROOTDELAY=
;;
esac
;;
resumedelay=*)
RESUMEDELAY="${x#resumedelay=}"
;;
loop=*)
LOOP="${x#loop=}"
;;
loopflags=*)
LOOPFLAGS="-o ${x#loopflags=}"
;;
loopfstype=*)
LOOPFSTYPE="${x#loopfstype=}"
;;
cryptopts=*)
cryptopts="${x#cryptopts=}"
;;
nfsroot=*)
NFSROOT="${x#nfsroot=}"
;;
netboot=*)
NETBOOT="${x#netboot=}"
;;
ip=*)
IPOPTS="${x#ip=}"
;;
boot=*)
BOOT=${x#boot=}
;;
resume=*)
RESUME="${x#resume=}"
;;
resume_offset=*)
resume_offset="${x#resume_offset=}"
;;
noresume)
noresume=y
;;
panic=*)
panic="${x#panic=}"
case ${panic} in
*[![:digit:].]*)
panic=
;;
esac
;;
quiet)
quiet=y
;;
ro)
readonly=y
;;
rw)
readonly=n
;;
debug)
debug=y
quiet=n
exec >/tmp/initramfs.debug 2>&1
set -x
;;
debug=*)
debug=y
quiet=n
set -x
;;
break=*)
break=${x#break=}
;;
break)
break=premount
;;
blacklist=*)
blacklist=${x#blacklist=}
;;
esac
done
if [ -z "${noresume}" ]; then
export resume=${RESUME}
else
export noresume
fi
depmod -a
maybe_break top
# export BOOT variable value for compcache,
# so we know if we run from casper
export BOOT
# Don't do log messages here to avoid confusing usplash
run_scripts /scripts/init-top
maybe_break modules
log_begin_msg "Loading essential drivers..."
load_modules
log_end_msg
maybe_break premount
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-premount"
run_scripts /scripts/init-premount
[ "$quiet" != "y" ] && log_end_msg
maybe_break mount
log_begin_msg "Mounting root file system..."
. /scripts/${BOOT}
parse_numeric ${ROOT}
mountroot
log_end_msg
maybe_break bottom
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-bottom"
run_scripts /scripts/init-bottom
[ "$quiet" != "y" ] && log_end_msg
# Move virtual filesystems over to the real filesystem
mount -n -o move /sys ${rootmnt}/sys
mount -n -o move /proc ${rootmnt}/proc
# Check init bootarg
if [ -n "${init}" ] && [ ! -x "${rootmnt}${init}" ]; then
echo "Target filesystem doesn't have ${init}."
init=
fi
# Search for valid init
if [ -z "${init}" ] ; then
for init in /sbin/init /etc/init /bin/init /bin/sh; do
if [ ! -x "${rootmnt}${init}" ]; then
continue
fi
break
done
fi
# No init on rootmount
if [ ! -x "${rootmnt}${init}" ]; then
panic "No init found. Try passing init= bootarg."
fi
# Confuses /etc/init.d/rc
if [ -n ${debug} ]; then
unset debug
fi
# Chain to real filesystem
maybe_break init
exec run-init ${rootmnt} ${init} "$@" <${rootmnt}/dev/console >${rootmnt}/dev/console 2>&1
panic "Could not execute run-init."
Kernel 映像与 initrd 不同,它是个 zImage/bzImage 文件。
通过 linux 编译脚本可以确认 zImage 实际上就是是由一个压缩后的内核(piggy.o),连接上一段初始化及解压功能的代码(head.o、misc.o)组成的。前面 kernel 引导加载过程中的硬件基本设置、设置基本环境(堆栈等)并清除BSS,直至解压内核都是 kernel 映像中压缩内核所连接的代码完成的。关于 kernel 映像这块还在研究、学习中。
学习的资料有如下文档,但不限于此。
- Linux 引导过程内幕
。全面的讲解了 linux 系统引导过程。
- 引导加载程序之争:了解 LILO 和 GRUB
。其给出了 LILO 详细介绍和配置方法。
- zImage内核镜像解压过程详解
。从内核开发角度深入介绍了 zImage 内核映像。
- Linux 内核映象文件解密
。简要介绍了 initrd 映像。
- Ubuntu upstart 简单说明
。
整个的 Linux kernel 引导过程还在研究、学习中欢迎大家分享、指正。
最后再次对技术前辈的进取和谦虚致敬!
晚安~~
// 2009.02.20 22:16 添加 ////
更新了第二阶段引导过程中几处相关的内容,之前存在理解模糊的地方,今天再看的时候发现的。知识还是要多多重复、巩固。呵呵。
// 2009.03.07 13:23 添加 ////
作者:lzy.je
出处:http://lzy.iteye.com
本文版权归作者所有,只允许以摘要和完整全文两种形式转载,不允许对文字进行裁剪。未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
- 大小: 8.2 KB
- 大小: 13.5 KB
- 大小: 6.3 KB
- 大小: 27.9 KB
分享到:
相关推荐
这份“Linux学习笔记.doc”文档可能会详细讲解以上各点,对于初学者来说是一份非常实用的学习资料。通过深入学习,你可以掌握Linux操作系统的核心技能,为后续的系统管理、开发或运维工作打下坚实基础。
### Debian Linux 学习笔记 #### 一、基本命令与目录结构 Debian Linux 是一个广泛使用的 Linux 发行版,其稳定性和安全性受到广大用户的认可。对于初学者来说,掌握 Debian Linux 的基本命令和目录结构至关重要。...
### GRUB 学习笔记 —— 引导 Linux 和 XP #### 1. 启动管理器概述 启动管理器是一段重要的程序,通常被放置于磁盘的开始扇区,比如硬盘上的主引导记录(Master Boot Record, MBR)。当计算机完成自检后,BIOS...
GRUB(GRand Unified Bootloader)和LILO(LInux LOader)是两种常见的Linux引导加载器。它们的作用是在系统启动时提供一个多系统选择菜单,让你可以选择要加载的操作系统。通常建议将GRUB或LILO安装到主引导扇区...
### Linux运维手册学习笔记知识点概览 #### 一、Linux快速入门 **1.1 为什么要学习Linux** - **行业需求:** 在IT行业中,尤其是服务器领域,Linux因其开源、稳定、安全等特性被广泛使用。 - **技能提升:** 掌握...
这个过程包括配置内核选项、编译源码、安装内核模块和更新引导加载器。 7. **鸟哥私房菜Linux**:这是一本广受欢迎的Linux入门书籍,涵盖了Linux系统的方方面面,从基本命令到系统管理,适合初学者和有经验的管理员...
本篇“Linux学习笔记”详细介绍了Linux的基本目录结构及其各自的功能,这对于初学者理解Linux系统的核心概念非常重要。 #### 二、Linux目录结构解析 1. **/(根目录)** - 根目录是Linux文件系统的起点,所有的...
2. **/boot**:存储Linux内核和引导加载程序,如GRUB或LILO,以及内核参数和引导菜单。 3. **/var/log**:系统日志文件存放位置,对于系统故障排查至关重要。 4. **/tmp**:临时文件存放地,用于存储运行时创建的...
- **/etc/lilo.conf**:Lilo(Linux引导加载程序)的配置文件,控制系统的启动顺序和选项。 - **/etc/profile**:设定全局环境变量和函数,对所有非交互式shell有效,影响用户登录体验。 - **/etc/shells**:列出...
"Linux完全完整学习笔记" Linux操作系统是一种基于Unix的开源操作系统,具有高度的可移植性、灵活性和可扩展性。下面是Linux学习笔记的详细知识点: 文件系统结构 * 根目录(/):Linux文件系统的入口,位于最高...
- Linux内核在引导加载器(如GRUB)加载后开始执行,首先进行初始化工作,包括设置内存管理、设备驱动和初始化进程等。 - `init/main.c` 文件是内核启动过程的主要入口,它调用了一系列初始化函数,如`start_...
### Linux入门学习笔记 #### 第一章:目录结构与理解 Linux系统采用了树形结构来组织文件和目录,每一个文件和目录都被放置在一个特定的位置,这有助于用户更好地管理和找到所需的资源。 - **/ 根目录**:Linux...
### Linux学习笔记知识点详解 #### 文件命名规则 在Linux中,文件命名有一定的规范,以便于管理和维护文件系统的一致性。 1. **除了 `/` 之外的所有字符都是合法的**:这表明除斜杠(/)外,几乎任何字符都可以...
在嵌入式Linux系统开发中,我们经常涉及到与硬件紧密集成的工作,比如处理微处理器(如ARM架构)、引导加载程序(U-Boot)以及内核的编译和配置。以下是一些关键知识点的详细说明: 1. **U-Boot编译**: U-Boot是...
- 安装内核:使用`make install`命令将新编译的内核复制到系统目录并更新引导加载器配置。 4. **命令行操作**: - `ls`:列出目录内容。 - `cd`:切换目录。 - `mkdir`和`rmdir`:创建和删除目录。 - `touch`...
### Linux常用命令学习笔记 #### 一、系统目录与配置文件概述 在Linux系统中,不同的目录具有不同的功能和用途,这些目录对于系统的稳定运行至关重要。以下是对几个关键目录及其内容的详细介绍: - **/bin**:...
**Red Hat Linux 9 讲义及学习笔记(基础)** Red Hat Linux 9 是一款历史悠久的开源操作系统,它是基于Linux内核的发行版,专为服务器和工作站环境设计。这款系统以其稳定性、安全性和易用性而备受赞誉,尤其适合...
它可以让你深入理解Linux启动流程,包括BIOS如何加载MBR、引导程序如LILO或GRUB如何加载内核,以及内核如何初始化硬件、启动init程序等。同时,这个过程也涉及到了软盘启动的特殊性。 在着手制作BabyLinux之前,你...