全部博文(685)
分类: 嵌入式
2014-10-22 11:00:36
昨天在分析procd如何工作的时候,有找到过下面这样一段描述新架构boot过程的文字:
1) Bootloader (uboot, redboot, adam2, grub, …) 2) Linux kernel starts, tries to find the mtd partition called “rootfs”, mounts it 3) Linux executes /etc/preinit 4) Preinit waits a few seconds for failsafe triggering 5) Preinit mounts or initializes the jffs2 overlay 6) Preinit loads kernel modules specified in /etc/modules.d/ 7) Preinit executes hotplug2, a kernel uevent handler 8) Preinit hands control off to init to start the main boot 9) The init process executes all /etc/rc.d/ scripts to bring up services
今天发现,事实并不是这样的,至少在我手上的系统中并非如此。首先,我在uboot中打印了uboot传给kernel的参数(著名的cmdline参数):
bootargs=console=ttyS0,115200 root=31:02 rootfstype=squashfs init=/sbin/init mtdparts=ar7240) bootcmd=bootm 0x9F020000
其中,root=31:02是告诉kernel知道rootfs是在mtdblock2上面。使用这种挂载rootfs的方式时,kernel必须包含有加载rootfs所需要的全部驱动,即,这里的kernel在编译时是加入了mtd设备相关驱动程式的。
kernel在挂载完rootfs之后,根据uboot告诉它的init=path_to_init_file参数,kernel会找到path_to_init_file并执行这个文件。
所以,这里,我们的系统在挂载文件系统之后,是先执行的/sbin/init。
init是一个用C写的程序,同样也是在luci2新框架下面。找了一下它的代码,在luci2/procd/initd/init.c,具体如下(只是main):
int main(int argc, char **argv) { pid_t pid; sigaction(SIGTERM, &sa_shutdown, NULL); sigaction(SIGUSR1, &sa_shutdown, NULL); sigaction(SIGUSR2, &sa_shutdown, NULL); early(); cmdline(); watchdog_init(1); pid = fork(); if (!pid) { char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL }; if (debug < 3) { int fd = open("/dev/null", O_RDWR); if (fd > -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > STDERR_FILENO) close(fd); } } execvp(kmod[0], kmod); ERROR("Failed to start kmodloader\n"); exit(-1); } if (pid <= 0) ERROR("Failed to start kmodloader instance\n"); else waitpid(pid, NULL, 0); uloop_init(); preinit(); uloop_run(); return 0; }
我们看到,这里其实一直到最后才进行了preinit。
不过,在此之前进行的操作都和用户没多大关系,所以,似乎直接认为是从preinit好像也没什么不妥。
唔,忍不住对main里面调用的几个函数进行了进一步挖掘。
early() 是在luci2/procd/initd/early.c中,可以在这里看到,它打印了一条消息:“Console is alive”。这条信息在启动打印信息里面是可以明显看到的。它在后台做的事情还包括了(这些是在打印Console is alive前就完成的):
1. 挂载proc,sysfs,tmpfs。
2. 添加设备/dev/null。
3. 将stdin,stdout,stderr都设置为我们指定的设备,比如“/dev/console”;如果我们没有指定设备,会被设置为/dev/nul,即被扔掉。
cmdline() 是在luci2/procd/initd/init.c中,它是从/proc/cmdline中取出init_debug,并设置debug等级。
watchdog() 就不用说了。
然后后面的,通过execvp来执行“ /sbin/kmodloader /etc/modules.d/”,则是把分离编译的kernel module设备挂载上来。
这里就到最后了,就是preinit()。这里其实也是先执行了“ /sbin/procd -h /etc/hotplug-preinit.json ”,然后才执行的“/bin/sh /etc/preinit”。具体在preinit中,则又是去执行在/lib/preinit下的一个个脚本。这里有个神奇的/lib/functions.sh,比较好奇,但是看了一眼有些晕,功夫不够,哪天心情好翻开看看再。
到此,init过程结束。
参考:http://lirobo.blogspot.tw/2014/07/openwrt-boot.html