1.LiveCD原理
LiveCD本质上是ISO 9660/El Torito格式的CD-ROM。
下面对LiveCD涉及的各种技术做了简单的调研。
1.1. CD-ROM
CD-ROM是一种光盘存储介质。
与磁盘类似,CD-ROM被划分为多个track,track又被划分为多个sector。sector大小为2352字节,随着sector所采用的编码方式的不同,其内部结构也不同,见下图。CD digital audio格式是CD唱片采用的方式,CD-ROM Mode1/2是CD-ROM数据盘(LiveCD属于此种情况)采用的方式。
1.2. ISO9660
CD-ROM中,每个track上都可以构建一个独立的文件系统。ISO 9660便是一种构建在CD-ROM之上的文件系统,专为光盘存储介质而设计,又被称为CDFS(Compact Disk File System)。
ISO 9660考虑了CD-ROM的顺序访问及只读的特性,将文件连续存储在CD-ROM的track上,对文件名的格式、属性、大小、数量、目录的层次等都作了限制。
ISO 9660文件系统的存储格式如下
在Data Area的头部,有多个Volume Descriptor,其功能类似于ext4文件系统中的Super Block,用于描述当前Volume的属性信息。
Volume是逻辑上的概念,对应到底层物理介质,就是存储当前ISO 9660文件系统实例的CD-ROMtrack。
Volume Descriptor有多种类型,如Boot Record Volume Descriptor, PrimaryVolume Descriptor, Volume Descriptor Set Terminator等,其中Primary Volume Descriptor记录了当前volume的大小, Root Directory的起始地址(Logical Block Address)等,ISO 9660要求至少包含一个Primary Volume Descriptor。
1.3. ElTorito
El Torito Bootable CD Specification是对ISO 9660的扩展,用于实现可引导的CD-ROM。
El Torito定义了3种引导模式,分别是floppy disk emulation, hard disk emulation以及no emulation。前两种模拟模式,通过将软盘或者磁盘的image存放在CD-ROM中,由BIOS负责加载到内存中,并将其虚拟成软盘或者磁盘驱动器,然后从虚拟软盘或虚拟磁盘中引导OS,从而实现对老OS的支持。现在最常用的是第3种方式,即直接从CD-ROM引导OS (LiveCD采用此方式)。El Torito Bootable CD需要BIOS的支持,当然,现在的BIOS都已经支持完全3种引导模式了。
El Torito遵循ISO 9660的存储格式,只是对特定区域的功能做了进一步的定义,其格式如下
El Torito将紧接着Primary Volume Descriptor的Sector 17定义为Boot Record Volume Descriptor,其记录了Booting Catalog的起始地址。
实际上,Boot Record Volume Descriptor并不是新的VolumeDescriptor类型,其在ISO 9660中已有定义。
Booting Catalog是一个目录表,包含了多个目录项,目录项进一步记录了可引导镜像文件的起始地址。从上图可以看出,El Torito支持单镜像引导和多镜像引导两种模式。顾名思义,在多镜像引导下,从一个CD-ROM可以引导多个系统。
从ISO 9660文件系统的角度看,El Torito Bootable CD中的Booting Catalog实际上就是Root Directory文件,Bootable Disk Image以及CD-ROM Image都是文件系统中的文件,只不过是在引导阶段使用的特殊文件,如bootloader等。
当从El Torito Bootable CD进行no emulation启动时,BIOS读取Boot Record Volume Descriptor,得到Booting Catalog的位置并读取此目录表,如果发现其中包含多个Boot Entry,则需要用户选择一个启动项,接着,BIOS将被选中的Boot Entry所指向的Bootable Disk Image载入内存。
需要注意的是,无论Disk Image文件有多大,BIOS都只会载入固定数量的sector,这就要求Bootable Disk Image不能太大。
载入完成之后,BIOS跳转到Bootable Disk Image的0字节处开始执行,顺序执行到代码结束。可以看出,Bootable Disk Image实际上就是bootloader,其需要完成OS加载和初始化的工作,由于bootloader不能太大,因此它会进一步加载下一阶段的bootloader完成复杂的工作。
1.4. ISOLINUX
isolinux是为Linux/i386的CD-ROM启动而设计的引导程序(bootloader),它支持以ISO 9660/El Torito的no emulation模式引导OS。
isolinux是syslinux的一部分,syslinux是一个bootloader集合,针对各种介质设计了相应的引导程序,如针对MS-DOS FAT文件系统的轻量级引导程序syslinux,针对Ext2/3/4/btrfs设计的extlinux,网络引导程序pxelinux以及CD-ROM引导程序isolinux。
isolinux的主体是bootloader,即isolinux.bin,其负责读取isolinux.cfg配置文件,载入相应的linux内核。isolinux.bin实现了遍历ISO 9660文件系统的功能,因此,启动时所需的配置,Linux内核等都可以以文件的形式存放在CD-ROM中。除了isolinux.bin和isolinux.cfg两个文件,isolinux中还有一个重要文件,即boot.cat,即Booting Catalog,它记录了所有可引导的文件的起始地址,对于isolinux来说,boot.cat中只记录了isolinux.bin。
1.5. Initrd/Initramfs
1.5.1. Initrd的作用
Initrd是initial ram disk的简称,是Linux内核在启动过程中使用的临时根文件系统,只包含必要的可执行文件和系统文件,为挂载最终的rootfs作准备。其设计的初衷是,考虑到内核启动时缺少驱动,无法访问多数I/O外设(尤其是存储设备),于是在内存中虚拟一个根文件系统,使得内核可以加载其中的驱动,执行一些初始化,进而可以访问外设,挂载真正的rootfs。
Initrd的目录结构与最终的rootfs类似,但经过精简,只包含必要的可执行文件和系统文件,如shell, mount, insmod, lvm及基本的驱动等。其中最核心的文件是init,该脚本完成内核参数的解析、驱动的加载、最终rootfs的挂载等初始化工作。
1.5.2. Initrd的原理
Linux内核支持ram disk机制,即将一段内存虚拟成块设备(/dev/ram)。initrd基于该机制。内核在启动时,会将initrd.img拷贝到/dev/ram上,然后将/dev/ram挂载为rootfs。initrd.img实际上是一个块设备镜像,在此镜像中,保存了一个完整的文件系统(如ext2, ramfs等),将initrd.img拷贝到/dev/ram之后,/dev/ram中便包含该文件系统,之后将/dev/ram作为一个普通的块设备进行挂载,即可以访问其中的文件系统。需要注意的是,为了能够访问该文件系统,需要将该文件系统驱动编译到Linux内核中,而不是以可加载模块的形式存在。
由于ram disk是块设备,其上层的文件系统需要在文件系统缓存和块设备间进行数据的拷贝,实际上ram disk与文件系统缓存都在内存中,这就造成了不必要的数据拷贝。ramfs作为ram disk的替代,很好地解决了这个问题。ramfs/tmpfs是内存中的文件系统,实现了文件系统的接口,并将所有文件数据保存在内存中,底层并没有块设备,无须进行缓存和块设备间的数据交换。
1.5.3. Initramfs
Initramfs 即是基于ramfs实现了initrd的功能,两者的rootfs一样,只是存储格式不同。initramfs是一个cpio归档包,即直接对rootfs中的文件进行的打包,而initrd是一个img文件,是对包含rootfs的块设备进行的镜像拷贝。initrd和initramfs都可以进一步压缩,在启动时,内核会完成相应的解压。
2.Casper
Casper是debian系统中initramfs-tools的一种hook,辅助init脚本完成LiveCD的启动工作,主要负责rootfs的挂载和初始化。casper不仅支持LiveCD,也支持Netboot tarball, USB stick image等启动方式。
casper在initramfs的init脚本中插入相应的代码,将init的执行逻辑导向casper中。casper会在所有块设备中查找/casper目录,该目录中保存了根文件系统,通常被打包为一个squashfs镜像文件,找到此文件后,casper会创建一个内存文件系统,然后将squashfs解压到此文件系统中。
init脚本主要通过/scripts/casper脚本中mountroot()函数,完成rootfs的准备工作,具体过程为:
(1) 调用find_livefs()遍历所有的块设备,查找/casper/*.squashfs文件。
在遍历过程中,对于不同的块设备,如cdrom, sata, device-mapper, md等,处理略有不同,但大概过程是相同的,即尝试挂载该块设备,然后查找该块设备的文件系统中,是否包含合法的casper路径(重点是查找/casper/*.squashfs文件)。
(2) 在某个块设备中找到/casper/*.squashfs文件之后,调用setup_unionfs()将这些文件(可能有多个)以unionfs的形式挂载到/root上。
setup_unionfs()首先调用get_backing_device()将*.squashfs文件绑定到/dev/loop上,然后挂载到/$imagename目录上,与此同时,挂载一个tmpfs,然后利用unionfs将tmpfs与/$imagename聚合到/root上。由于设置了tmpfs为RW权限,而/$imagename为RO权限,使得对squashfs中文件的修改都被重定向到tmpfs中,这就解释了为什么物理上只读的LiveCD系统却可以正常读写。
如果向casper传递了toram或todisk参数,则在挂载*.squashfs文件之前,会先将这些文件拷贝到toram/todisk中,之后使用LiveCD系统的过程中,无须再从CD-ROM读取*.squashfs文件,提高了性能。
(3) *.squashfs挂载完成后,需要对未来的rootfs进行一些配置,这些工作由/scripts/casper-bottom中的脚本完成。
至此,casper结束执行,返回init脚本。
3.LiveCD启动流程
BIOS读取CD-ROM中的BootRecord Volume Descriptor,定位boot.cat文件的地址,读取该文件中的Boot Entry,进一步定位isolinux.bin文件的地址,接着,将该文件加载到内存并执行。
isolinux.bin在光盘中查找isolinux.cfg文件,读取该文件,获得启动配置,该文件记录了采用何种启动界面,有图形界面和者字符界面两种,其中图形界面下,会进一步加载vesamenu.c32文件和splash.jpg文件,完成图形界面的显示。在用户选定一个启动项之后,isolinux.bin按照isolinux.cfg中的配置,查找相应的kernel文件,将其加载到内存中,将相应的参数传递给kernel并执行。
kernel进行内核初始化,并将initrd.lz解压到/dev/ram中,并将其挂载为rootfs,执行其中的/init脚本。
init脚本解析isolinux.bin传递进来的内核参数,接着加载核心驱动,挂载运行时文件系统,在参数解析阶段如果识别出boot=casper,init脚本会执行/scripts/casper脚本,主要调用其中mountroot()函数,实现squashfs的解压和挂载rootfs,最后init脚本执行/bin/run-init程序,该程序完成rootfs的切换,并执行/sbin/init程序,init程序根据/etc/rc.d中的配置,进一步初始化用户环境,包括图形界面、网络等。
至此,LiveCD启动结束。
4.参考资料
http://en.wikipedia.org/wiki/CD-ROM
http://en.wikipedia.org/wiki/ISO_9660
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
http://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29
http://wiki.osdev.org/El-Torito
http://download.intel.com/support/motherboards/desktop/sb/specscdrom.pdf
http://littlesvr.ca/isomaster/eltoritosuppl.php
http://www.syslinux.org/wiki/index.php/ISOLINUX
http://blog.csdn.net/liujixin8/article/details/4029887
http://manpages.ubuntu.com/manpages/hardy/man7/casper.7.html
http://en.wikipedia.org/wiki/Klibc
http://en.wikipedia.org/wiki/UnionFS