LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: laowang_buaa

精通initramfs构建step by step

[复制链接]
 楼主| 发表于 2008-11-13 23:52:35 | 显示全部楼层

(七)modules

二十二、内核模块支持
到目前为止,我们在构建initramfs时还没有涉及内核模块的支持,所用到的硬件驱动程序都是直接编译到内核中。现在我们就看看如何使initramfs支持内核模块。
首先,内核配置要支持模块,并支持内核模块的自动加载功能:在内核配置菜单中的激活下面的配置项,编译进内核 Load module support / Enable loadable module support / Automatic kernel loading ;
然后把需要的硬件驱动程序配置模块形式,比如把我的机器上的硬盘控制器的驱动编译成模块,则选择
Device Driver
|---->SCSI device support
|---->SCSI disk support
|----->verbose SCSI error reporting (不是必须的,但可方便问题定位)
|----->SCSI low-level drivers
  |---->Serial ATA (SATA) support
  |---->intel PIIX/ICH SATA support
把它们配置成模块。
最后,编译内核,并把编译好的内核模块安装到image的目录下:
make
make INSTALL_MOD_PATH=~/initramfs-test/image modules_install
命令执行完毕后,在image/lib/modules/2.6.17.13/kernel/drivers/scsi目录下安装了4个内核模文件:scsi_mod.ko、sd_mod.ko、ata_piix.ko、libata.ko,它们就是所需的硬盘控制器的驱动程序。

好了,都准备好了,可以用cpio命令生成inintramfs了。不过,为了方便后面的试验,我们再把init脚本改成
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
exec /bin/sh
使系统启动后进入shell环境,并且用exec调用的方式,使shell的pid为1,能够执行switch_root命令。

二十三、试验:用initramfs中的内核模块安装硬盘文件系统
用新生成的initramfs启动系统,内核并没有自动加载硬盘控制器的驱动程序,所以 /dev目录下也没有sda等硬盘设备文件。好吧,我们自己加载内核模块文件。不幸的是,busybox的modprobe命令执行不正常,不能加载内核模块。怀疑是busybox的modprobe命令配置或编译有问题,后续再花时间定位吧,先用insmod命令依次加载。查看/lib/modules/2.6.17.13/modules.dep,弄清楚了4个模块的依赖关系,执行下面的命令加载:
insmod scsi_mod
insmod libata
insmod ata_piix
insmod sd_mod
然后再用
mdev -s
命令生成硬盘的设备文件。
好了,可以安装CLFS的硬盘分区,并把根文件系统切换到CLFS的硬盘分区:
mount /dev/sda8 /mnt
exec switch_root /mnt /sbin/init   
系统正常启动到了CLFS,我们可以做到用initramfs中的硬盘控制器的驱动模块安装硬盘分区了。

二十四、mdev的hotplug模式
上面的试验中,我们在加载完驱动模块后调用了mdev -s 命令来生成硬盘的设备文件。其实,可以使用mdev的hotplug模式在加载内核时自动生成对应的设备文件:
在执行insmod命令前,用
echo /sbin/mdev > /proc/sys/kernel/hotplug
命令设置系统的hotplug程序为mdev。
后续使用insmod命令加载模块时,系统自动调用mdev生成相应的设备文件。
注意:内核必须配置支持hotplug功能,而前面提到的CLFS最简内核配置方案是没有配置hotplug支持的。



---下节预告---

在这节里我们知道自己的硬件情况,也知道加载什么样的内核模块,所以用了一系列insmod命令加载硬件驱动程序。如果要做一个支持各种硬件配置的initramfs,就不能使用这种方法,而是要借助著名的udev来自动根据内核侦测到硬件类型来加载相应的驱动程序,也就是系统的coldplug。在initramfs中如何使用udev做coldplug,请看下一个step:
精通initramfs构建step by step (八):coldplug
回复 支持 反对

使用道具 举报

发表于 2008-11-15 13:22:50 | 显示全部楼层
太好了!

期待lz继续!
回复 支持 反对

使用道具 举报

发表于 2008-11-16 20:35:48 | 显示全部楼层
二十四、mdev的hotplug模式
上面的试验中,我们在加载完驱动模块后调用了mdev -s 命令来生成硬盘的设备文件。其实,可以使用mdev的hotplug模式在加载内核时自动生成对应的设备文件:
在执行insmod命令前,用
echo /sbin/mdev > /proc/sys/kernel/hotplug
命令设置系统的hotplug程序为mdev。
后续使用insmod命令加载模块时,系统自动调用mdev生成相应的设备文件。
注意:内核必须配置支持hotplug功能,而前面提到的CLFS最简内核配置方案是没有配置hotplug支持的。
配置内核时有要填入hotplug程序的。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-11-16 22:57:13 | 显示全部楼层

(八)coldplug

二十五、udev的coldplug模式
内核在启动时已经检测到了系统的硬件设备,并把硬件设备信息通过sysfs内核虚拟文件系统导出。udev扫描sysfs文件系统,根据硬件设备信息生成热插拔(hotplug)事件,udev再读取这些事件,生成对应的硬件设备文件。由于没有实际的硬件插拔动作,所以这一过程被称为coldplug。我们的initramfs就是利用这一机制,加载硬件设备的驱动程序模块。

udev完成coldplug操作,需要下面三个程序:
udevd——作为deamon,记录hotplug事件,然后排队后再发送给udev,避免事件冲突(race conditions)。
udevtrigger——扫描sysfs文件系统,生成相应的硬件设备hotplug事件。
udevsettle——查看udev事件队列,等队列内事件全部处理完毕才退出。

在initramfs的init脚本中可以执行下面的语句实现coldplug功能:
mkdir -p /dev/.udev/db
udevd --daemon
mkdir -p /dev/.udev/queue
udevtrigger
udevsettle
许多文档提到的在udevd --daemon 命令前要执行
echo > /proc/sys/kernel/hotplug
命令,经验证,在我们的initramfs环境下的coldplug功能中并不需要。

二十六、试验:用udev自动加载设备驱动模块
了解了udev的coldplug的机理,我们就试验一下用udev自动加载设备驱动模块,并生成硬件设备文件。
(1)从 /sbin 目录下拷贝udevd、udevtrigger、udevsettle程序到image目录下的sbin目录下,并用ldd命令找到它们所需要的动态库文件,拷贝到image目录下的lib目录下。

(2)修改init脚本,增加coldplug功能:
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
#using udev autoload hard disk driver module
mkdir -p /dev/.udev/db
udevd --daemon
mkdir -p /dev/.udev/queue
udevtrigger
udevsettle
mount /dev/sda8 /mnt
killall udevd
exec switch_root /mnt /sbin/init

注意:在切换到真正根文件系统前,要把udevd进程杀掉,否则会和真正根文件系统中的udev脚本的执行相冲突。这就是上面killall udevd 语句的作用。

(3)编写udev规则文件
规则文件是udev的灵魂,没有规则文件,udev无法自动加载硬件设备的驱动模块。为了简单,我们直接使用CLFS中的40-modprobe.rules,把它拷贝到image目录下的etc/udev/rules.d目录。有关udev的规则文件编写,已超出了本文的范围,后续我有可能专文描述。
########################################################################
#
# Description : 40-modprobe.rules
#
# Authors   : Based on Open Suse Udev Rules
#           
[EMAIL="kay.sievers@suse.de"]kay.sievers@suse.de[/EMAIL]
#
# Adapted to : Jim Gifford
# LFS     : Alexander E. Patrakov
#
# Version   : 00.01
#
# Notes     :
#
########################################################################

# hotplug
ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}"


# scsi
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="0|7|14", RUN+="/sbin/modprobe sd_mod"
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="1", SYSFS{device/vendor}=="On[sS]tream", RUN+="/sbin/modprobe osst"
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="1", RUN+="/sbin/modprobe st"
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="[45]", RUN+="/sbin/modprobe sr_mod"
SUBSYSTEM=="scsi_device", ACTION=="add", RUN+="/sbin/modprobe sg"


# floppy
KERNEL=="nvram", ACTION=="add", RUN+="load_floppy_module.sh"

注意:上面的
ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}"
语句是实现自动加载硬件设备驱动模块功能的关键,它根据sysfs文件系统中记录的模块aliases数据,用modprobe命令加载对应的内核模块。有关模块aliases的进一步说明,可参考CLFS手册(CLFS-1.0.0-x86)中的11.5.2.4. Module Loading一节的描述。

(4)拷贝modprobe命令
前一节提到过,busybox的modprobe命令不能正常使用,所以我们需要拷贝 /sbin 目录下的modprobe命令到image目录下的sbin目录,供udev加载内核模块使用。再用ldd命令检查一下 /sbin/modprobe 命令所需的动态库文件,如果有则拷贝到image/lib目录下。(我的检查结果是,除了libc6外,不需要其他动态库,所以不需要拷贝)

好了,重新生成initramfs,启动CLFS系统,initramfs能够自动加载硬盘设备的驱动模块,系统顺利地从initramfs切换到了真正的CLFS的根文件系统。



---下节预告---

现在,initramfs构建中最困难的识别并自动加载硬件设备的驱动模块的问题已经解决了,我们稍稍停顿一下,回头再看看在内核编译时构建initramfs的另外两种方式。那么,就请看下一个step:
精通initramfs构建step by step (九):内核编译时构建initramfs补遗
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-11-22 23:37:43 | 显示全部楼层

(九)内核编译时构建initramfs补遗

二十七、直接把cpio打包文件编译进内核
如果我们有一个已经做好的cpio格式的initramfs,可以在内核编译时直接编译进内核。回忆一下第一节的内容,我们在内核配置参数中的initramfs sources配置项下输入构建initramfs的目录路径。其实我们也可以直接输出现成的initramfs的文件名,这样在内核编译时,就可以把它编译进内核了。
使用这种方法,有两点需要注意:
(1)cpio文件不能压缩。一般作为initrd的cpio文件都经过了压缩,所以编译前需要先把压缩过的文件解压。
(2)cpio文件的后缀名必须是 .cpio。内核编译通过 .cpio的后缀名来识别此文件是cpio打包文件,而其他文件后缀名则会被认为是initramfs构建的描述文件(关于描述文件,下面后详细说明)。

二十八、用描述文件构建initramfs
用内核编译工具构建initramfs的第三种方法是使用描述文件。在内核配置参数中的initramfs sources配置项下可以输入initramfs构建描述文件的文件名,内核编译工具根据描述文件完成initramfs的构建。
描述文件的语法格式的说明如下:
# a comment
file <name> <location> <mode> <uid> <gid>
dir <name> <mode> <uid> <gid>
nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>
slink <name> <target> <mode> <uid> <gid>
pipe <name> <mode> <uid> <gid>
sock <name> <mode> <uid> <gid>


<name>     name of the file/dir/nod/etc in the archive
<location> location of the file in the current filesystem
<target>   link target
<mode>     mode/permissions of the file
<uid>     user id (0=root)
<gid>     group id (0=root)
<dev_type> device type (b=block, c=character)
<maj>     major number of nod
<min>     minor number of nod


例子:
我们用描述文件的方式,构建第一节中的hello world的initramfs。
hello-init.desp:
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
file /init /home/wyk/initramfs-test/hello_static 0755 0 0

在内核配置项initramfs sources中指定描述文件hello-init.desp,编译内核时就会生成hello world的initramfs,运行效果与第一节用指定构建目录的方法构建的initramfs的完全相同。

注意:在内核帮助文件中,提到initramfs sources配置项可以指定多个目录或描述文件,内核会汇集这些目录或文件生成一个initramfs。但从我的试验来看,initramfs sources只接受单一的目录名或文件名,输出多个目录名或文件名(之间用空格分隔),内核编译时就会出错。也许是我的方法有误,还望读者指正。


---下节预告---

前面编译busybox时,都是连接到了glibc库。但是glibc库比较大,而且busybox不能静态连接(有资料讲可以通过修改busybox源码来静态连接glibc),在initramfs容量受限或需要静态连接的情况下,使用uclibc库可能是个很好的选择。在initramfs中如何使用uclibc库,这就需要看看下一个step:
精通initramfs构建step by step (十):uclibc
回复 支持 反对

使用道具 举报

发表于 2008-11-26 14:34:35 | 显示全部楼层
非常感谢楼主~!
学习中……
回复 支持 反对

使用道具 举报

发表于 2008-11-30 12:53:20 | 显示全部楼层
期待lz继续
回复 支持 反对

使用道具 举报

发表于 2008-12-4 21:59:45 | 显示全部楼层
呼唤lz继续
回复 支持 反对

使用道具 举报

发表于 2008-12-5 02:35:21 | 显示全部楼层
非常感谢这么好的文章
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-12-12 00:26:58 | 显示全部楼层

(十)uclibc

二十九、toolchain
在initramfs中使用uclibc库,关键是构建uclibc的工具链toolchain。构建uclibc 的 toolchain 有两种主要方式:(1)用buildroot工具( http://buildroot.uclibc.org/)自动构建,这也是uclibc的官方标准做法。(2)用CLFS Embedded手册的方法手工创建。目前CLFS Embedded还在开发中,可在http://cross-lfs.org/view/clfs-embedded/x86/中查阅。

我们简单地说明用buildroot工具构建uclbic的toolchain的步骤:
(1)获取buildroot。
推荐用svn命令从它的版本库中下载:
svn co svn://uclibc.org/trunk/buildroot
要求使用svn命令,需要先安装subversion软件包。下载过程中,可能会出现连接异常中断的情况,这时重新执行上述命令,继续进行下载,有可能要重复多次。
(2)配置buildroot
因为我们只是创建toolchain,所以需要做相应的配置。在buildroot的顶层目录下,执行
make menuconfig
命令,在缺省配置的基础上做如下配置
Target Architecture: i386
Target Architecture Variant: i686
Package Selection for the target: 取消BusyBox的选项(缺省是选中的)
Target filesystem options: 取消 ext2 root filesystem(缺省是选中的)
Toolchain --> Toolchain type: Buildroot toolchain
(3)编译
执行
make
命令,buildroot工具会自动下载所需要的源文件并自动编译,等一两个小时后,toolchain就编译好了。编译好的toolchain位于
buildroot/build_i686/staging_dir/usr/bin/
目录下。工具命令的前缀是 i686-linux- 。

三十、编译Busybox静态连接uclibc库
一般而言,使用uclibc库是为了把它静态连接到busybox中。具体步骤是:
(1)把uclibc toolchain的目录添加到PATH中。
在~/.bash_profile文件中添加:
#set PATH so it includes uclibc toolchain if it exist
if [ -d ~/buildroot/build_i686/staging_dir/usr/bin ] ; then
PATH="${PATH}":~/buildroot/build_i686/staging_dir/usr/bin
fi

(2)配置busybox静态连接库。
在busybox的配置界面中,选择:
Build Options --> Build BusyBox as a static binary (no shared libs)
(3)编译
执行
make CROSS_COMPILE=i686-linux-
命令“交叉编译”busybox。
最后编译生成的是静态连接的可执行文件,不需要在initramfs中拷贝库文件。

三十一、用buildroot自动构建initramfs
buildroot工具实际是一个功能强大的根文件系统构建工具,它以uclibc和busybox作为系统构建的基础,toolchain只是它构建系统的中间产品。initramfs是一种特殊的根文件系统,当然也可以用buildroot工具自动构建,下面是构建方法的简要描述:
(1)配置
在buildroot的配置界面下做如下的配置:
Package Selection for the target: 选择
  Busybox
  Run Busybox's own full installation

  Use minimal target skeleton
Target filesystem options --> cpio the root filesystem --> comprassion method: gzip
(2)编译
执行
make
命令,进行编译。
(3)输出
构建好的cpio文件是
buildroot/binaries/rootfs.i686.cpio.gz
同一目录下还包含一个未压缩的文件:rootfs.i686.cpio
构建目录则是
buildroot/project_build_i686/uclibc/root
可以在这个目录下对原始的initramfs进行修改调整,然后自己用cpio命令打包生成新的initramfs。
(4)调整
直接用buildroot生成的root.i686.cpio.gz作为initramfs,运行时会出现
can't open /dev/tty1: No such file or directory
can't open /dev/tty2: No such file or directory
can't open /dev/tty3: No such file or directory
错误信息的循环输出,系统不能正常运行。
错误的原因是没有在initramfs的/dev目录下生成相应的设备文件。需要做如下的调整:
1)在构建目录(buildroot/project_build_i686/uclibc/root)下的etc/init.d目录中新增一个初始化脚本文件S10mountfs
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s

2)更改busybox的setuid属性,否则无法执行mount命令。在构建目录(buildroot/project_build_i686/uclibc/root)下执行
chmod -s bin/busybox
命令。
这两项调整工作做完后,在构建目录(buildroot/project_build_i686/uclibc/root)下执行
find . | cpio -o -H newc |gzip > ../initramfs.cpio.gz
命令,重新生成initramfs的cpio打包文件。
(5)运行效果
运行新的initramfs,系统出现登录提示。输入用户名 root,密码为空,即可进入一个mini的linux系统。

buildroot是功能强大、配置灵活的自动化构建工具,它的详细使用和配置方法超出了本文的范围,后续可能会专文描述,此处就从略了。


---下节预告---

截至到目前,我们都是基于busybox来构建initramfs。还有一种广泛使用的initramfs构建方式是使用klibc软件包,包括一个小巧的C库和常用命令和工具。Debian的initramfs构建工具就综合应用了busybox、udev、klibc。有关klibc的使用方法,请看下一个step:
精通initramfs构建step by step (十一):klibc
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表