linux 内核代码学习(九)--Linux内核启动和文件系统
一个比较顺手的学习平台可以达到事半功倍的效果,这里使用的平台环境主要是利用了主机和从机间的文件共享,以及从机自带的编译环境可以比较顺利的编译busybox1.0版本,方便进行内核和文件系统的测试了学习。
主机环境:vmware7.0+win10
从机环境:RedHat9.0 linux发行版
所有的PC机在加电之后,BIOS会寻找到启动盘第一个扇区,并将其复制到RAM中来执行它,对于两种不同的启动方式,这个扇区通常含有两种不同的代码:1、引导程序(比如Lilo或Grub等)的代码,引导程序会帮助定位内核的位置;2、内核的代码,这通常是从软盘启动时使用的引导的方式。对于前者,通常需要内核支持initrd。如果是后者,使用的Boot Loader就是arch/i386/boot/bootsect.S,当内核被编译的时候,这段执行代码就被链接到内核image的最开始的地方,这样很容易就能只要把内核复制到起始位置为第一个扇区的软盘上就能得到可自启动的软盘,内核会初始化设备驱动和内部的数据结构,之后它会到一个特定的位置――Ramdisk Word来获得根文件系统的位置,内核必须知道去那里寻找这个根文件系统,否则它将停机,在使用软盘启动的方式时,内核可以把一个压缩的文件系统释放到RAM中,称之为Ramdisk,这是一个内存区域,但内核会把它当作磁盘一样使用。
我们首先要得到一张可以引导进入linux环境的软盘,这种软盘必须由两部分组成,即内核和根文件系统。我们首先制作一个内核。内核的编译要把内核部分放到一张1.44MB的软盘上去,通常要对内核进行压缩,压缩内核的最好方法是进行重新编译内核,将一些不必要的支持去掉,如对网络和其它周边设备的支持,重要的一点是记住内核必须支持RAMDISK及ext2,否则系统不能正常引导。
一:制作从软盘直接启动的liunx,软盘上包括内核及简单文件系统。
下载linux2.4.20内核源代码,源代码放置在win10主机上,通过vmware的文件共享功能在从机redhat9上进行编译,编译出合适大小的内核。同样先注释掉makefile中下面两行,然后在内核include目录里面根据cpu类型手动拷贝asm目录。
使用vmware工具创建一张虚拟启动软盘,根据内核readme文档说明“If you want to make a boot disk (without root filesystem or LILO), insert a floppy in your A: drive, and do a "make bzdisk". ”先创建一张不带文件系统的boot盘,再用vmware创建一个测试从机,插入boot盘,看看在没有根文件系统的时候内核的启动效果:
重新配置内核,对内核配置不熟悉可以参考发行版的内核配置,再次制作引导盘测试,可以看到内核能够正常启动,最后提示没有文件系统:
制作根文件系统:
制作根文件系统前,我们先要解决一个问题:因为一个根文件系统要实现基本的功能,必须包括一些常用工具:如:sh,ls,cd,cat…… 但是常用工具会占用很多空间,要是用原来系统中的这些命令,就是全部用静态编译,不是用动态连接库,大概也要有2MB~3MB,放不进软盘。因此我们的解决的方案是使用BusyBox工具。BusyBox 它包含了七十多种 Linux 上标准的工具程序,只需要的磁盘空间仅仅几百 k 。在嵌入式 系统上常用到它 (例如 Linux Router Project 和 Debian boot floppy就使用到它)。
建立 BusyBox
Index of /downloads上下载busybox-1.00.tar.bz2,在/home目录下解压和安装
首先我们从官方网站上下载BusyBox的最新版本:busybox-1.00-rc3.tar.gz并且解开
#tar zxvf busybox-1.00-rc3.tar.gz
为了压缩空间,我们采用静态编译,可以是使用busybox图形化配置或者修改 Makefile 中的 DOSTATIC 参数为true
DOSTATIC=true
这两个选项是不使用系统的/usr安装目录,当前目录中_install为安装目录。
然后修改 BusyBox 中的 init.c,设定系统要执行的第一个程序为: /etc/rc.d/rc.sysinit,这样做的目的是让制作的启动盘的启动效果同redhat9发行版一样的效果。
#define INIT_SRCIPT \"/etc/rc.d/rc.sysinit\"
#make
#make install
到这一步我们就得到了可执行命令busybox,解决了这个问题后,我们可以开始制作根文件系统。
制作根文件系统:首先为根文件系统建一个目录叫做 floppy-Linux,然后进入 floppy-Linux 目录内
# mkdir floppy-Linux
# cd floppy-Linux
然后为 root filesystem 建立一些标准的目录
# mkdir dev etc etc/rc.d bin proc mnt tmp var
# chmod 755 dev etc etc/rc.d bin mnt tmp var
# chmod 555 proc
# ln -s bin sbin
然后进入 /dev 目录下建立根文件系统必须的一些设备文件。
建立一般终端机设备
# mknod tty c 5 0
# mknod console c 5 1
# chmod 666 tty console
建立 VGA Display 虚拟终端机设备
# mknod tty0 c 4 0
# chmod 666 tty0
建立 RAM disk 设备
# mknod ram0 b 1 0
# chmod 600 ram0
建立 floppy 设备
# mknod fd0 b 2 0
# chmod 600 fd0
建立 null 设备
# mknod null c 1 3
# chmod 666 null
到这里我们就有了一个初步的小型根文件系统,但是还需要配置一些有关的 shell script来完善它。编辑有关的 shell script:首先进入到 /floppy-Linux/etc/ 这个目录下编辑 inittab,rc.d/rc.sysinit,fstab这三个文件 ,内容分别如下:
inittab
::sysinit:/etc/rc.d/rc.sysinit
::askfirst:/bin/sh
rc.sysinit
#!/bin/sh
mount –a
fstab
proc /proc proc defaults 0 0
然后修改inittab,rc.sysinit,fstab这三个文件的权限
# chmod 644 inittab
# chmod 755 rc.sysinit
# chmod 644 fstab
配置完shell script后,我们注意到这些shell script会使用一些 /bin目录下的命令,但是我们的/bin目录下是空的。现在我们就使用BusyBox来制作这些常用命令。将busybox 复制到软盘的/bin目录下,并且改名为init。
# cp busybox /floppy-Linux/bin/init
然后创建常用命令的link,具体的工作原理请参阅busybox的官方说明。
# ln -s init ls
# ln -s init cp
# ln -s init mount
# ln -s init umount
# ln -s init more
# ln -s init ps
# ln -s init sh
# ln -s init df
现在我们就有了所需的常用命令。
到这里我们的根文件系统就制作完成了,但是和内核一样,要把根文件系统部分放到一张1.44MB的软盘上去,也要进行压缩,下面我们就着手压缩它。
压缩根文件系统:一般我们会采取 RAM Disk 的方式实现。简单的来说就是将准备好的根文件系统压缩成为Ramdisk的镜像文件,当用软盘启动时,再把镜像文件解压到内存中,形成一个虚拟盘(RAMDISK),通过RAMDISK控制系统启动。
我们现在制作Ramdisk的镜像文件
# dd if=/dev/zero of=/tmp/tmp_loop bs=1k count=2048
# losetup /dev/loop0 /tmp/tmp_loop
# mke2fs -m 0 /dev/loop0
# mount -t ext2 /dev/loop0 /mnt
# cp -a floppy-Linux/* /mnt
# umount /mnt
# losetup -d /dev/loop0
# dd if=/tmp/tmp_loop | gzip -9 > /tmp/Image.gz
# rm -f /tmp/tmp_loop
# sync
或者
dd if=/dev/zero of=/dev/ram0 bs=1k count=4096
mke2fs -m 0 /dev/ram0
mount -t ext2 /dev/ram0 ramdisk/
cp -R floppy-Linux/* ramdisk/
umount ramdisk
dd if=/dev/ram0 bs=1k | gzip -v9 > rootfs.gz
这样我们就得到了压缩过的根文件系统也就是Ramdisk的镜像文件rootfs.gz。目前为止我们已经有了内核和压缩过的根文件系统.现在剩下的就是把它们整合在一张软盘里面。根据引导的方式不同,有以下三种整合方案:用grub引导、用sysLinux引导、直接引导。
1.将内核与文件系统放置在一张软盘上
依次执行:
# dd if=/mnt/hgfs/linux-2.4.20/arch/i386/boot/bzImage of=/dev/fd0 bs=1k
876+1 records in
876+1 records out
在这个例子中,dd 写入了 876个完整记录(records) + 1个partial record ,所以内核占用了 877 个软盘的blocks 。这个数字称为 KERNEL_BLOCKS ,请记得它,这个数字还要使用。
之后,设置根设备为软盘本身,并且设置根以读写方式装载
#rdev /dev/fd0 /dev/fd0
#rdev -R /dev/fd0 0
上面这个例子表示dd写了876个完整记录和一个部分记录到软盘上,因此内核占用了软盘的前877 个记录块。记住这个数字,然后设置内核的Ramdisk Word。Ramdisk Word可以通过rdev命令设置,它的内容为:
命令 “ rdev –r ” 设置内核镜像文件中的两个字节 (32bit) ,这两个字节中各个位的含义如下:
低 11 位 (0 -> 10) 指定了一个偏移量 ( 以 1K 的块为单位 ) ,最到能寻址到 2M ,用以指定到何处去寻找 RAM 磁盘。
第 14 位指示 RAM 磁盘是否被加载。
第 15 位指示是否在加载 RAM 磁盘之前给出一个提示并等待用户指令。
如果随着数据被写入 RAM 磁盘, RAM 磁盘的大小是动态增长的,那么指定 RAM 磁盘的大小的域将被忽略。 11 到 13 位没有被使用,所以可以为 0 。
上面所列的数据并非什么秘密,可以在参照下列地方:
进入内核源码所在目录:
./arch/i386/kernel/setup.c:#define RAMDISK_IMAGE_START_MASK 0x07FF
./arch/i386/kernel/setup.c:#define RAMDISK_PROMPT_FLAG 0x8000
./arch/i386/kernel/setup.c:#define RAMDISK_LOAD_FLAG 0x4000
考察一个典型的 " 两张软盘启动 " ,内核在第一张软盘上,并且已经将一个 RAM 磁盘镜像文件放到了第二张软盘上。所以你希望将 0 到 13 位设置为 0 ,这将意为着你的 RAM 磁盘处于从软盘起始地址偏移量为 0KB 的地方。相同功能的命令行参数为: "ramdisk_start=0"
你希望第 14 位为 1 ,即声明加载 RAM 磁盘。相同功能的命令行参数为: "load_ramdisk=1"
你希望第 15 位为 1 ,这是声明希望显示一个提示并等待用户的按键以得到一个提示机会来更换软盘。形同功能的命令行参数为: "prompt_ramdisk=1"
如果15位设置的话,内核在加载文件系统之前会进行提示,这在将内核与文件系统盘分开的情况时是必要的。对于在一张软盘上集成内核和文件系统的情况,需要在0-10位指出ramdisk的偏移,并将14位置1,所以得出的ramdisk word十进制表示为:877 + 2^14 = 877 + 16384 = 17261
#rdev -r /dev/fd0 17261
之后
#dd if=rootfs.gz of=/dev/fd0 bs=1k seek=877
这样一张同时包含内核和文件系统的软盘就成功了。
这样就将linux2.4.20内核和文件系统集成到一张软盘上了,现在我们就拥有了一张可以自激活到Linux环境的软盘。对于本例来讲,想要执行一个helloworld程序,只要将helloworld这个可执行文件复制到 /bin目录就可以了。我们可以在“使用BusyBox制作常用命令“这个阶段来完成它.
# cp helloworld /floppy-Linux/bin
这张软盘会自激活到linux环境下,并显示“#”命令提示符,我们只要执行helloworld就可以了。
#/bin/helloworld
如果希望系统一开机就直接执行helloworld,则需要在“编辑有关的 shell script”这个阶段编辑rc.sysinit文件为:
#!/bin/sh
mount –a
/bin/helloworld
这样软盘引导进入linux后会直接执行helloworld而不再显示“#”命令提示符。
除了以上的方法,我们也可以通过引导器给内核传递参数来实现内核和根文件系统分别放置在不同的软盘
上,这样内核就可以再大一些,支持的功能也就越多。