您的位置:首页 > 其它

initramfs的运行过程

2016-03-31 21:29 190 查看
转自: http://blog.chinaunix.net/uid-22101074-id-91125.html
首先说明我从事的是嵌入式行业,所以以后的linux文章都是嵌入式相关的,除非有特别说明。

我用的芯片是欧洲Gaisler Research公司的Leon3(Sparc架构),内核是Snapgear(包含ucLinux2.0和Linux2.6),已经做好移植工作。

周一的时候,把linux2.6.21下载到leon3的板子上运行,结果莫名退出。

检查的时候主要从配置出发,结果发现gaisler的默认配置有问题。它配置initial
root filesystem是romfs,其实从linux2.6内核开始这必须是initramfs。并且我们考虑到sram的大小可能不足以下载运行linux,所以我们使用了sdram。

经过一番更改后,编译下载运行,照样出错。没办法,必须要debug,可是kgdb没有for
sparc/leon的patch。所以只能printk了。错误提示如下:

grlib> lo image.dsu

......

IP route cache hash table entries: 1024 (order: 0, 4096 bytes)

TCP established hash table entries: 512 (order: 0, 4096 bytes)

TCP bind hash table entries: 512 (order: -1, 2048 bytes)

TCP: Hash tables configured (established 512 bind 512)

TCP reno registered

initramfs:populate_rootfs:first line:allen

按照提示发现代码在../linux2.6.21.1/init/initramfs.c

static int __init populate_rootfs(void)

{

printk("initramfs:populate_rootfs:first line:allen\n");

(stop in here)--> char *err = unpack_to_rootfs(__initramfs_start,

__initramfs_end - __initramfs_start, 0);

......

经过读源代码和看文档,最后终于把这个问题的过程搞清楚。

首先要清楚initrd 的英文含义是 boot
loader initialized RAM disk,就是由 boot loader 初始化的内存盘。在 linu2.6内核启动前, boot
loader 会将存储介质中的 initrd 文件加载到内存,内核启动时会在访问真正的根文件系统前先访问该内存中的 initrd 文件系统。在 boot
loader 配置了 initrd 的情况下,内核启动被分成了两个阶段,第一阶段先执行 initrd 文件系统中的init,完成加载驱动模块等任务,第二阶段才会执行真正的根文件系统中的 /sbin/init 进程。

linux2.6 内核使用的 initrd 是 cpio 格式,其核心文件是 /init。下面介绍 linux2.6 内核对initrd 的处理流程:

boot loader 把内核以及 initrd 文件加载到内存的特定位置。

内核判断initrd的文件格式,如果是cpio格式。

将initrd的内容释放到rootfs中。

执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给/init文件处理。

这里要清楚另一个概念:

initramfs:initramfs 是在 kernel
2.5中引入的技术,实际上它的含义就是:在内核镜像中附加一个cpio包,这个cpio包中包含了一个小型的文件系统,当内核启动时,内核将这个 cpio包解开,并且将其中包含的文件系统释放到rootfs中,内核中的一部分初始化代码会放到这个文件系统中,作为用户层进程来执行。这样带来的明显
的好处是精简了内核的初始化代码,而且使得内核的初始化过程更容易定制。

所以内核首先要生成一个cpio包,这个包中又包含了initramfs的文件系统,这个就是initfamfs.cpio.gz。生成这个包的文件是:usr/gen_init_cpio.c,编译时initramfs_data.cpio.gz被链接进内核中。这个包放在usr/下,我们的内核也已经生成了这个包。
而unpack_to_rootfs(__initramfs_start, __initramfs_end - __initramfs_start, 0)这句话的意思就是试图通过gunzip解压initfamfs.cpio.gz,在[__initramfs_start,
__initramfs_end]之间找到initramfs。现在我们的gz包有了,还停在这里,所以我有理由怀疑是内核的下载地址和sdram的映射地址不相符合而引发的问题。

进一步考证,需要下星期有硬件的情况下才能完成。

还有,这个星期和Gailer
Research的D哥通了一下email,他觉得内核没有问题,主要强调了grmon下运行linux2.6的配置参数,如下:

1.不要使用-u,使用minicom;

2.要加-nb;

3.要加-nosram,这样才能把sdram的地址映射到0x40000000。不然有可能映射地址0x40000000是sram;

4.配置initial
root filesystem为initramfs,内核要支持initramfs。

后记:

Ok,为了了解细节,Documentation/early-userspace/buffer-format.txt是必须要看看的。initramfs.cpio.gz是为内核准备一个包(这个包最终是连接在__initramfs_start处),其格式大致如下:

initramfs := ("\0" | cpio_archive | cpio_gzip_archive)*

cpio_gzip_archive := GZIP(cpio_archive)

cpio_archive := cpio_file* + ( | cpio_trailer)

cpio_file := ALGN(4) + cpio_header + filename + "\0" + ALGN(4) + data

cpio_trailer := ALGN(4) + cpio_header + "TRAILER!!!\0" + ALGN(4)

下面就简单讲一下如何生成:

usr/gen_init_cpio.c在编译后生成程序gen_init_cpio(这是一个host
program),它在主机上被执行,并产生一个cpio_archive(结果输出到stdout, 大家执行一下usr/gen_init_cpio就知道了)。2.6.x内核的KBUILD在调用它时用下列命令(用make
V=1可以看到)将其结果重定向到文件initramfs_data.cpio:

./usr/gen_init_cpio > usr/initramfs_data.cpio

这样就生成一个未压缩的,"newc"格式的CPIO
archive。然后,KBUILD再用gzip将initramfs_data.cpio压缩生成initramfs_data.cpio.gz文件。最后,KBUILD将initramfs_data.cpio.gz链接进内核中。

生成了文件就要使用了,回到../linux2.6.21.1/init/initramfs.c。至于这里为什么要解压两次,我就不太清楚了,感觉一次就能搞完了。

static int __init populate_rootfs(void)

{

char *err = unpack_to_rootfs(__initramfs_start,

__initramfs_end - __initramfs_start, 0);

if (err)

panic(err);

#ifdef CONFIG_BLK_DEV_INITRD

if (initrd_start) {

#ifdef CONFIG_BLK_DEV_RAM

int fd;

printk(KERN_INFO "checking if image is initramfs...");

err = unpack_to_rootfs((char *)initrd_start,

initrd_end - initrd_start, 1);

if (!err) {

unpack_to_rootfs((char *)initrd_start,

initrd_end - initrd_start, 0);

free_initrd();

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: