linux 内核学习资料

          最近在自学 Linux kernel 方面的东西,这两天了粗浅的研究了下 kernel boot 过程,在此记录。这里所指 Linux 引导加载未涉及虚拟化环境,即系统未运行在 hypervisor 之上。

          Linux 通过执行不同阶段的引导加载程序(boot loader)程序来引导操作系统,在完成内核等引导之后,最终会由调度器接管 CPU,其通过启用中断来周期性的抢占控制权,处理多个用户进程/客户进程(kvm 虚拟化)。Top level 的引导过程如下图。

linux 内核学习资料

          整个 Linux 系统引导共分 5 步执行操作:

  1. BIOS/BootMonitor 引导程序;BIOS 包括 POST 和 Runtime 服务。
  2. 被称为第一阶段的MBR(Masterbootrecord)引导程序;位于BIOS配置的启动磁盘0柱面1扇区的主引导记录,用于启动第二阶段的linuxbootloader。
  3. 被称为第二阶段的linuxbootloader;主要有LILO(Linuxloader)和GNUGRUB(Grandunifiedbootloader)两种bootloader程序,现主流为GRUB。包括了通过initrd来创建RAM盘,执行init脚本,通过LKM(linuxkernelmodule)加载本地磁盘等驱动程序来挂载磁盘中的root文件系统。RAM盘中是个完整的小型linux环境,在没有磁盘的嵌入式环境中,initrd可以是最终的根文件系统,也可以通过NFS来挂载最终的文件系统。
  4. linuxkernel(及initrd函数)引导;负责加载并解压zImage/bzImagekernel及initrd映像,并开始执行kernel初使化和引导程序/过程。
  5. init进程。用于启动linux配置的各项用户空间服务(demon)进程。

          加电后首先被执行的是 BIOS (Base input/output system)程序。 嵌入式环境使用 boot monitor,它负责在一个位于 rom/flash 中预定地址开始执行引导程序,而在 PC 环境中这个启动地址是 0xFFFF0,相对来讲 BIOS 提供了更多的配置功能。它主要由两部分组成:

  1. POST (Power On Self Test)程序;其负责接通电源时对硬件检测,包括创建中断向量、设置寄存器、对一些外部设备进行初始化和检测等。
  2. 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 特殊字节结束。如下图所示。

linux 内核学习资料

          MBR 引导程序会将扫描分区表,获得唯一活动分区后,将其中的引导程序读入 RAM 并开始执行。

          MBR 启动的引导程序被称为第二阶段引导程序,它是引导的主体,是引导加载的真正部分。 Linux 中该阶段有两个流行的程序,LILO (较老)和 GRUB。如果安装了 lilo 程序,可以通过 root 用户执行如下命令来通过 lilo 生成默认配置的 MBR ,并写入到启动磁盘 0 柱面 1扇区位置上。

Shell代码 linux 内核学习资料
  1. # /sbin/lilo -v -v  
# /sbin/lilo -v -v

          一般需要修改 lilo 的配置文件,使生成的 MBR 有效。位于 /etc/lilo.conf 。lilo 配置示例。

Config代码 linux 内核学习资料
  1. boot=/dev/hda   
  2. map=/boot/map   
  3. install=/boot/boot.b   
  4. prompt   
  5. timeout=100  
  6. compact   
  7. default=Linux   
  8. image=/boot/vmlinuz-2.4.18-14  
  9.     label=Linux   
  10.     root=/dev/hdb3   
  11.     read-only   
  12.     password=linux   
  13. other=/dev/hda   
  14.     label=WindowsXP  
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 文件内容如下。

Config代码 linux 内核学习资料
  1. default  0  
  2. timeout  3  
  3. hiddenmenu   
  4.   
  5. title  Ubuntu 8.10, kernel 2.6.27-11-generic   
  6. uuid   e2cf53c5-11de-4d57-a532-878901afd9b4   
  7. kernel /boot/vmlinuz-2.6.27-11-generic root=UUID=e2cf53c5-11de-4d57-a532-878901afd9b4 ro locale=zh_CN quiet splash    
  8. initrd /boot/initrd.img-2.6.27-11-generic   
  9. quiet   
  10.   
  11. title  Ubuntu 8.10, kernel 2.6.27-11-generic (recovery mode)   
  12. uuid   e2cf53c5-11de-4d57-a532-878901afd9b4   
  13. kernel /boot/vmlinuz-2.6.27-11-generic root=UUID=e2cf53c5-11de-4d57-a532-878901afd9b4 ro locale=zh_CN  single   
  14. initrd /boot/initrd.img-2.6.27-11-generic   
  15.   
  16. title  Ubuntu 8.10, kernel 2.6.27-7-generic   
  17. uuid   e2cf53c5-11de-4d57-a532-878901afd9b4   
  18. kernel /boot/vmlinuz-2.6.27-7-generic root=UUID=e2cf53c5-11de-4d57-a532-878901afd9b4 ro locale=zh_CN quiet splash    
  19. initrd /boot/initrd.img-2.6.27-7-generic   
  20. quiet   
  21.   
  22. title  Ubuntu 8.10, kernel 2.6.27-7-generic (recovery mode)   
  23. uuid   e2cf53c5-11de-4d57-a532-878901afd9b4   
  24. kernel /boot/vmlinuz-2.6.27-7-generic root=UUID=e2cf53c5-11de-4d57-a532-878901afd9b4 ro locale=zh_CN  single   
  25. initrd /boot/initrd.img-2.6.27-7-generic   
  26.   
  27. title  Ubuntu 8.10, memtest86+   
  28. uuid   e2cf53c5-11de-4d57-a532-878901afd9b4   
  29. kernel /boot/memtest86+.bin   
  30. quiet  
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 步。

  1. 执行一个对硬件做些基本设置的例程;

    (,/arch/i386/boot/head.S中的start例程)

  2. 设置一个基本的环境(堆栈等),并清除BlockStartedbySymbol(BSS);

    (./arch/i386/boot/compressed/head.S中的startup_32例程)

  3. 通过连接在映像中的函数来解压内核;

    (./arch/i386/boot/compressed/misc.c中的decompress_kernelC函数)

  4. 启动swapper(0进程)进程,初始化页表,启用CPU内存分页。然后会为任何可选的浮点单元(FPU)检测CPU的类型,并将其存储起来供以后使用;

    (./arch/i386/kernel/head.S中的startup_32函数)

  5. 调用linuxkernlmain函数,进入与体系结构无关的Linux内核部分。

    (init/main.c中的start_kernel函数)

    这会调用一系列初始化函数来设置中断,执行进一步的内存配置,并加载已初始化的RAM盘。最后启动init进程,这是第一个用户空间进程(user-spaceprocess);

    (./arch/i386/kernel/process.c中的kernel_thread)

  6. 最后,启动空任务。现在调度器就可以接管控制权了(在调用cpu_idle之后)。通过启用中断,抢占式的调度器就可以周期性地接管控制权,从而提供多任务处理能力。

          上面第 5 步加载的 RAM 盘(initrd)是由阶段 2 引导加载程序加载到内存中的,它用来加载必要的磁盘驱动内核模块,来挂载真正磁盘的 root 文件系统。

linux 内核学习资料

          引导加载的最后的一步就是执行 init (1 进程),该进程会根据配置来启动服务。 一般的配置都会写在 inittab 里,不过我这里用的 ubuntu 使用的是 upstart,它是基于事件驱动的,发生什么 event 怎么处理,在这里 init 进程会产生 startup event, upstart 据此来启动 rc.* 配置的进程。不过无论如何,此时引导加载程序已经放权了。

这里抄录一段 LILO 与 GURB 的优缺点对比。

  1. LILO 没有交互式命令界面,而 GRUB 拥有。
  2. LILO 不支持网络引导,而 GRUB 支持。
  3. LILO 将关于可以引导的操作系统位置的信息物理上存储在 MBR 中。如果修改了 LILO 配置文件,必须将 LILO 第一阶段引导加载程序重写到 MBR。相对于 GRUB,这是一个更为危险的选择,因为错误配置的 MBR 可能会让系统无法引导。使用 GRUB,如果配置文件配置错误,则只是默认转到 GRUB 命令行界面。

          关于 kernel 和 initrd 两个映像。 技术含量很高的,嵌入式开发中 bootloader 可是很大一块。值得深入,只可惜现在的技术水平,哎~

  1. kernel /boot/vmlinuz-2.6.27-7-generic
  2. initrd /boot/initrd.img-2.6.27-7-generic

          initrd 映像是打包的 RAM 盘根文件系统。 一般 initrd.img-2.6.27-7-generic 是一个 cpio 包文件,老版本也有 gzip 压缩格式的。通过 cpio 命令将其解包到当前目录中,如下。cpio 使用方法可参见 cpio 命令详解

Shell代码 linux 内核学习资料
  1. zcat initrd.img-2.6.27-11-generic | cpio -i -d --no-absolute-filenames  
zcat initrd.img-2.6.27-11-generic | cpio -i -d --no-absolute-filenames

 

          在我这里解包后的根文件系统包括如下内容。

linux 内核学习资料

          从上面的 directory tree 可以看到 initrd 中主要包括的就是磁盘、网络、文件系统的驱动 lkm 文件。其中还有最主要是的 init shell 脚本,它包括了初使化的全过程。

Shell代码 linux 内核学习资料
  1. #!/bin/sh   
  2.   
  3. echo "Loading, please wait..."  
  4.   
  5. [ -d /dev ] || mkdir -m 0755 /dev   
  6. [ -d /root ] || mkdir -m 0700 /root   
  7. [ -d /sys ] || mkdir /sys   
  8. [ -d /proc ] || mkdir /proc   
  9. [ -d /tmp ] || mkdir /tmp   
  10. mkdir -p /var/lock   
  11. mount -t sysfs -o nodev,noexec,nosuid none /sys    
  12. mount -t proc -o nodev,noexec,nosuid none /proc    
  13.   
  14. # Note that this only becomes /dev on the real filesystem if udev's scripts   
  15. # are used; which they will be, but it's worth pointing out   
  16. mount -t tmpfs -o mode=0755 udev /dev   
  17. [ -e /dev/console ] || mknod -m 0600 /dev/console c 5 1  
  18. [ -e /dev/null ] || mknod /dev/null c 1 3  
  19. > /dev/.initramfs-tools   
  20. mkdir /dev/.initramfs   
  21.   
  22. # Export the dpkg architecture   
  23. export DPKG_ARCH=   
  24. . /conf/arch.conf   
  25.   
  26. # Set modprobe env   
  27. export MODPROBE_OPTIONS="-Qb"  
  28.   
  29. # Export relevant variables   
  30. export ROOT=   
  31. export ROOTDELAY=   
  32. export ROOTFLAGS=   
  33. export ROOTFSTYPE=   
  34. export break=   
  35. export init=/sbin/init   
  36. export quiet=n   
  37. export readonly=y   
  38. export rootmnt=/root   
  39. export debug=   
  40. export panic=   
  41. export blacklist=   
  42. export resume_offset=   
  43.   
  44. # Bring in the main config   
  45. . /conf/initramfs.conf   
  46. for conf in conf/conf.d/*; do   
  47.     [ -f ${conf} ] && . ${conf}   
  48. done   
  49. . /scripts/functions   
  50.   
  51. # Parse command line options   
  52. for x in $(cat /proc/cmdline); do   
  53.     case $x in   
  54.     init=*)   
  55.         init=${x#init=}   
  56.         ;;   
  57.     root=*)   
  58.         ROOT=${x#root=}   
  59.         case $ROOT in   
  60.         LABEL=*)   
  61.             ROOT="/dev/disk/by-label/${ROOT#LABEL=}"  
  62.             ;;   
  63.         UUID=*)   
  64.             ROOT="/dev/disk/by-uuid/${ROOT#UUID=}"  
  65.             ;;   
  66.         /dev/nfs)   
  67.             [ -z "${BOOT}" ] && BOOT=nfs   
  68.             ;;   
  69.         esac   
  70.         ;;   
  71.     rootflags=*)   
  72.         ROOTFLAGS="-o ${x#rootflags=}"  
  73.         ;;   
  74.     rootfstype=*)   
  75.         ROOTFSTYPE="${x#rootfstype=}"  
  76.         ;;   
  77.     rootdelay=*)   
  78.         ROOTDELAY="${x#rootdelay=}"  
  79.         case ${ROOTDELAY} in   
  80.         *[![:digit:].]*)   
  81.             ROOTDELAY=   
  82.             ;;   
  83.         esac   
  84.         ;;   
  85.     resumedelay=*)   
  86.         RESUMEDELAY="${x#resumedelay=}"  
  87.         ;;   
  88.     loop=*)   
  89.         LOOP="${x#loop=}"  
  90.         ;;   
  91.     loopflags=*)   
  92.         LOOPFLAGS="-o ${x#loopflags=}"  
  93.         ;;   
  94.     loopfstype=*)   
  95.         LOOPFSTYPE="${x#loopfstype=}"  
  96.         ;;   
  97.     cryptopts=*)   
  98.         cryptopts="${x#cryptopts=}"  
  99.         ;;   
  100.     nfsroot=*)   
  101.         NFSROOT="${x#nfsroot=}"  
  102.         ;;   
  103.     netboot=*)   
  104.         NETBOOT="${x#netboot=}"  
  105.         ;;   
  106.     ip=*)   
  107.         IPOPTS="${x#ip=}"  
  108.         ;;   
  109.     boot=*)   
  110.         BOOT=${x#boot=}   
  111.         ;;   
  112.     resume=*)   
  113.         RESUME="${x#resume=}"  
  114.         ;;   
  115.     resume_offset=*)   
  116.         resume_offset="${x#resume_offset=}"  
  117.         ;;   
  118.     noresume)   
  119.         noresume=y   
  120.         ;;   
  121.     panic=*)   
  122.         panic="${x#panic=}"  
  123.         case ${panic} in   
  124.         *[![:digit:].]*)   
  125.             panic=   
  126.             ;;   
  127.         esac   
  128.         ;;   
  129.     quiet)   
  130.         quiet=y   
  131.         ;;   
  132.     ro)   
  133.         readonly=y   
  134.         ;;   
  135.     rw)   
  136.         readonly=n   
  137.         ;;   
  138.     debug)   
  139.         debug=y   
  140.         quiet=n   
  141.         exec >/tmp/initramfs.debug 2>&1  
  142.         set -x   
  143.         ;;   
  144.     debug=*)   
  145.         debug=y   
  146.         quiet=n   
  147.         set -x   
  148.         ;;   
  149.     break=*)   
  150.         break=${x#break=}   
  151.         ;;   
  152.     break)   
  153.         break=premount   
  154.         ;;   
  155.     blacklist=*)   
  156.         blacklist=${x#blacklist=}   
  157.         ;;   
  158.     esac   
  159. done   
  160.   
  161. if [ -z "${noresume}" ]; then   
  162.     export resume=${RESUME}   
  163. else   
  164.     export noresume   
  165. fi   
  166.   
  167. depmod -a   
  168. maybe_break top   
  169.   
  170. # export BOOT variable value for compcache,   
  171. # so we know if we run from casper   
  172. export BOOT   
  173.   
  174. # Don't do log messages here to avoid confusing usplash   
  175. run_scripts /scripts/init-top   
  176.   
  177. maybe_break modules   
  178. log_begin_msg "Loading essential drivers..."  
  179. load_modules   
  180. log_end_msg   
  181.   
  182. maybe_break premount   
  183. [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-premount"  
  184. run_scripts /scripts/init-premount   
  185. [ "$quiet" != "y" ] && log_end_msg   
  186.   
  187. maybe_break mount   
  188. log_begin_msg "Mounting root file system..."  
  189. . /scripts/${BOOT}   
  190. parse_numeric ${ROOT}   
  191. mountroot   
  192. log_end_msg   
  193.   
  194. maybe_break bottom   
  195. [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-bottom"  
  196. run_scripts /scripts/init-bottom   
  197. [ "$quiet" != "y" ] && log_end_msg   
  198.   
  199. # Move virtual filesystems over to the real filesystem   
  200. mount -n -o move /sys ${rootmnt}/sys   
  201. mount -n -o move /proc ${rootmnt}/proc   
  202.   
  203. # Check init bootarg   
  204. if [ -n "${init}" ] && [ ! -x "${rootmnt}${init}" ]; then   
  205.     echo "Target filesystem doesn't have ${init}."  
  206.     init=   
  207. fi   
  208.   
  209. # Search for valid init   
  210. if [ -z "${init}" ] ; then   
  211.     for init in /sbin/init /etc/init /bin/init /bin/sh; do   
  212.         if [ ! -x "${rootmnt}${init}" ]; then   
  213.             continue   
  214.         fi   
  215.         break   
  216.     done   
  217. fi   
  218.   
  219. # No init on rootmount   
  220. if [ ! -x "${rootmnt}${init}" ]; then   
  221.     panic "No init found. Try passing init= bootarg."  
  222. fi   
  223.   
  224. # Confuses /etc/init.d/rc   
  225. if [ -n ${debug} ]; then   
  226.     unset debug   
  227. fi   
  228.   
  229. # Chain to real filesystem   
  230. maybe_break init   
  231. exec run-init ${rootmnt} ${init} "$@" <${rootmnt}/dev/console >${rootmnt}/dev/console 2>&1  
  232. panic "Could not execute run-init."  
#!/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 映像这块还在研究、学习中。

学习的资料有如下文档,但不限于此。

  1. Linux 引导过程内幕 。全面的讲解了 linux 系统引导过程。
  2. 引导加载程序之争:了解 LILO 和 GRUB 。其给出了 LILO 详细介绍和配置方法。
  3. zImage内核镜像解压过程详解 。从内核开发角度深入介绍了 zImage 内核映像。
  4. Linux 内核映象文件解密 。简要介绍了 initrd 映像。
  5. Ubuntu upstart 简单说明

整个的 Linux kernel 引导过程还在研究、学习中欢迎大家分享、指正。

最后再次对技术前辈的进取和谦虚致敬!

相关推荐