linux内核init进程函数的部分代码如下:
01 if (execute_command)
02 run_init_process(execute_command);
03
04 run_init_process("/sbin/init");
05 run_init_process("/etc/init");
06 run_init_process("/bin/init");
07 run_init_process("/bin/sh");
08
09 panic("No init found. Try passing init= option to kernel.");
10 }
代码中我们看出,linux内核在初始化的最末段,也就是挂载了跟文件系统之后,开始了与根文件系统,也就是用户应用的沟通,我们看到:内核分别尝试了/sbin/init, /etc/init, /bin/init, /bin/sh四个应用的执行,由此可以想到,只要我们准备相应的应用,并且只要满足其中之一,就可以启动系统了,如果任何的一个都没有满足,那么久会出现很经典的内核panic:No init found. Try passing init= option to kernel.
在此,我们利用现有分析,构建一个可以说是很小的根文件系统,busybox是针对嵌入式开发需求,集各种unix工具于一身的很小很强大的工具集。busybox的编译过程不再赘述,现假设busybox编译后已经生成如下文件夹:
bin linuxrc sbin usr
其中,linuxrc为程序,bin、sbin、usr为文件夹,其实三个文件夹内绝大部分是程序,并且是指向bin/busybox的链接,也就是所有的命令均由bin/busybox执行
我们看到,内核初始化最后一次尝试的bin/sh, 猜想上来sh并不会跟其它的程序产生关联,所以干脆删除sbin和usr再说,同时linuxrc似乎也不是必须,最多内核报告错误,并不会产生panic,所以精简后,根目录如下(有点裸了:)):
bin
呵呵,现在想,估计是启动不起来的,为什么呢?起码sh程序或者说busybox要依赖一些动态库,当然可以编译选择静态编译了,那么似乎连什么库都不要,但是我们这里说的最小,并不是文件最少,而是最必须的意思,同时默认busybox编译,采用动态库,所以无论怎么说,lib库是最小根文件系统必须的。所以,添加lib目录,其中的动态库文件来自交叉编译器的lib目录。现在的最小根文件系统如下:
bin lib
这时候,先别急,我们发现bin目录下,仍然有很多文件,当然也是链向bin/busybox的链接,也就是说,有没有他们没什么关系,那么如果删除他们也没什么影响咯,开始精简bin目录,精简后目录如下:
busybox sh
sh程序(到busybox的链接),以及busybox,现在发现,直接输入rm等命令已经不管用了,那么是不是意味着我们的精简结束了呢?因为诸如rm这样的命令只是一个指向busybox的链接,那么就是说我们直接busybox rm这样调用,也可以使用咯,测试一下,果然好用。现在bin目录做到了最小。
回顾一下,我们现在的根文件系统目录如下:其中,bin目录已经最小,下面看看lib目录有没有什么压缩的空间。
bin lib
lib目录之所以保留到现在,是因为总是怀疑busybox可能用到这个那个库的,所以看看busybox到底用到些什么动态库,pc上执行命令arm-linux-gcc –d busybox,看到以下结果:
01 Dynamic section at offset 0xb5014 contains 22 entries:
02 Tag Type Name/Value
03 0x00000001 (NEEDED) Shared library: [libcrypt.so.1]
04 0x00000001 (NEEDED) Shared library: [libm.so.6]
05 0x00000001 (NEEDED) Shared library: [libc.so.6]
06 0x0000000c (INIT) 0xc18c
07 0x0000000d (FINI) 0x9e8dc
08 0x00000004 (HASH) 0x80e8
09 0x00000005 (STRTAB) 0xa410
10 0x00000006 (SYMTAB) 0x8b40
11 0x0000000a (STRSZ) 3410 (bytes)
12 0x0000000b (SYMENT) 16 (bytes)
13 0x00000015 (DEBUG) 0x0
14 0x00000003 (PLTGOT) 0xc50ec
15 0x00000002 (PLTRELSZ) 3040 (bytes)
16 0x00000014 (PLTREL) REL
17 0x00000017 (JMPREL) 0xb5ac
18 0x00000011 (REL) 0xb55c
19 0x00000012 (RELSZ) 80 (bytes)
20 0x00000013 (RELENT) 8 (bytes)
21 0x6ffffffe (VERNEED) 0xb47c
22 0x6fffffff (VERNEEDNUM) 3
23 0x6ffffff0 (VERSYM) 0xb162
24 0x00000000 (NULL) 0x0
我们发现,busybox只使用了libcrypt.so.1 libm.so.6 libc.so.6三个共享库,所以马上精简lib库,考虑可以留下的库文件,以上三个是必须的,那么还有没有必须的呢?经过试验,我们发现,除了上边的3个库以外,ld-linux.so.2不能缺少,猜想可能用于装在,毕竟ld~load,呵呵!到此,lib库精简完成。
这时候,会有人问,那么是不是现在的根文件系统已经可以被挂载,并且最小了呢?现在可以重启开发板了,此时busybox reboot命令已经不能使用,具体原因看输出就知道,重启,下面是结果:
01 Kernel panic - not syncing: Attempted to kill init!
02 Backtrace:
03 [] (dump_backtrace+0x0/0x10c) from [] (dump_stack+0x18/0x1c)
04
05 r6:00000000 r5:c3812c40 r4:c3816000
06 [] (dump_stack+0x0/0x1c) from [] (panic+0x48/0x11c)
07 [] (panic+0x0/0x11c) from [] (do_exit+0x64/0x59c)
08 r3:c0349c50 r2:c38194e0 r1:c3817f2c r0:c02fcf60
09 r4:c3816000
10 [] (do_exit+0x0/0x59c) from [] (do_group_exit+0x90/0xc4)
11 [] (do_group_exit+0x0/0xc4) from [] (sys_exit_group+0x18/0x2
12 0)
13 r4:000c622c
14 [] (sys_exit_group+0x0/0x20) from [] (ret_fast_syscall+0x0/0
15 x2c)
结果是不是算灾难性的?至少系统启动失败了!经过无数次烧写文件系统,逐步rm文件,发现console设备文件是必须的,至少bin/sh启动需要,所以添加dev/console,最终的最小根文件系统如下:
1 # ls
2 bin dev lib
3 # ls bin
4 busybox sh
5 # ls dev
6 console
7 # ls lib
8 ld-linux.so.2 libcrypt.so.1 libc.so.6 libm.so.6
9 #
好了,最小的根文件系统已经做好了!方法是:rm reboot rm reboot……分析原因,总结!期待下一篇:为linux内核构建最小的根文件系统-一步一步添加~
---------------------------------------------------------------------------
本篇是 为linux内核构建最小的根文件系统-一步一步精简 的试验篇,上篇中我们得到了启动bin/sh情况下最小的根文件系统,如下:
1 # ls
2 bin dev lib
3 # ls bin
4 busybox sh
5 # ls dev
6 console
7 # ls lib
8 ld-linux.so.2 libcrypt.so.1 libc.so.6 libm.so.6
9 #
既然内核寻找的是bin/sh,那么我们想法是替换busybox的sh链接,而是自己建立sh应用程序,我们不知道sh必须做什么,但是我们可以保证sh不退出,马上动手,做个hello wolrd也好,替换之后,出现了一个问题,明明hello程序在bin目录下,但是sh说找不到,后来一比较,区别是两次编译hello用的交叉工具不相同,内核当时用的是gcc 4.0,刚刚hello用的是gcc4.1,马上跟换,可以运行hello,打印:hello world,之后死循环,我们的办法是重启,修改hello成sh,再重启,试验结果如下:
01 Failed to execute /linuxrc. Attempting defaults...
02 Kernel panic - not syncing: No init found. Try passing init= option to kernel.
03 Backtrace:
04 [] (dump_backtrace+0x0/0x10c) from [] (dump_stack+0x18/0x1c)
05
06 r6:00000000 r5:c0022e54 r4:c036a4fc
07 [] (dump_stack+0x0/0x1c) from [] (panic+0x48/0x11c)
08 [] (panic+0x0/0x11c) from [] (init_post+0xd4/0x104)
09 r3:c3805e38 r2:0000000f r1:0000000e r0:c02fa02c
10 r4:c036a4fc
11 [] (init_post+0x0/0x104) from [] (kernel_init+0xbc/0xe4)
12 r4:c036a4f8
13 [] (kernel_init+0x0/0xe4) from [] (do_exit+0x0/0x59c)
14 r5:00000000 r4:00000000
看来并不是我们想象的那么简单,要是前几天我还会怀疑是因为文件系统烧制的问题,要么是内核没找到文件系统的问题,现在想来不是的,文件系统正常挂载了,sh也存在(被我们用hello替换),所以问题出在sh上,执行的过程中出问题了,是不是没有打开console就打印造成的呢?所以,索性去掉printf语句,启动后直接进入死循环了,果然如此。但是,我们并满足,死循环了,我们要打印出东西,表明我们的存在,注意,可能我们的最小文件系统还可以精简,或许现在连console设备文件都不用,因为我们可以不输出,根据以上猜想测试,结果如下:
1 VFS: Mounted root (jffs2 filesystem) on device 31:0.
2 Freeing init memory: 124K
3 Warning: unable to open an initial console.
4 Failed to execute /linuxrc. Attempting defaults...
说明,我们的猜想正确,console设备文件用于输出,被真正的sh所使用,现在的最小根文件系统是如下:
1 # ls
2 bin lib
3 # ls bin
4 sh
5 # ls lib
6 ld-linux.so.2 libcrypt.so.1 libc.so.6 libm.so.6
7 #
其中的bin/sh是我们自己的hello改写的死循环,并且没有输出,只有while(1).现在猜想,sh重定向了输出到console,console正是我们现在看到的控制台了。
至此,试验结束,最小的跟文件系统,不错猜想还可以精简的,不过已经没有意义了,最小的跟文件系统已经探求成功。