本系列导航:
OpenWRT 启动流程(一) /sbin/init 进程分析
OpenWRT 启动流程(二) /etc/preinit 脚本分析
OpenWRT 启动流程(三) /sbin/procd 分析
OpenWRT /sbin/init 进程分析
之前分析内核启动流程,知道了内核加载完根文件系统后启动的第一个进程是/sbin/init
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
接下来分析一下/sbin/init 进程。
围绕以下两个问题:
1./sbin/init 怎么来的?
2./sbin/init 做了些什么?
1./sbin/init 怎么来的?
一般是来自busybox,但是这里面的不是(因为一般busybox来的会是指向/bin/busybox的软连接)。那它到底从哪里来的呢?
找到一个可能的地方,procd
root@localhost:/home2/lql/openwrt/build_dir# find . -name init
...省略...
./target-arm_cortex-a7+neon-vfpv4_musl_eabi/procd-2018-03-28-dfb68f85/ipkg-install/usr/sbin/init
./target-arm_cortex-a7+neon-vfpv4_musl_eabi/procd-2018-03-28-dfb68f85/.pkgdir/procd/sbin/init
./target-arm_cortex-a7+neon-vfpv4_musl_eabi/procd-2018-03-28-dfb68f85/ipkg-arm_cortex-a7_neon-vfpv4/procd/sbin/init
./target-arm_cortex-a7+neon-vfpv4_musl_eabi/procd-2018-03-28-dfb68f85/init
./target-arm_cortex-a7+neon-vfpv4_musl_eabi/root.orig-sunxi/sbin/init
root@localhost:/home2/lql/openwrt/build_dir#
2./sbin/init 做了些什么?
那么来看一下procd 的源码。
initd/init.c
int
main(int argc, char **argv)
{
pid_t pid;
ulog_open(ULOG_KMSG, LOG_DAEMON, "init");
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)
patch_stdio("/dev/null");
execvp(kmod[0], kmod);
ERROR("Failed to start kmodloader: %m\n");
exit(-1);
}
if (pid <= 0) {
ERROR("Failed to start kmodloader instance: %m\n");
} else {
int i;
for (i = 0; i < 1200; i++) {
if (waitpid(pid, NULL, WNOHANG) > 0)
break;
usleep(10 * 1000);
watchdog_ping();
}
}
uloop_init();
preinit();
uloop_run();
return 0;
}
1.early() 主要是挂载了 /proc 、/sys 、/dev 、/tmp …等目录,并设置 PATH 环境变量
early()
early_mounts();
early_env();
2.cmdline() 是设置了 debug 全局变量
cmdline()
debug = (int) r;
3.watchdog_init(1) 开启看门狗
4./sbin/kmodloader /etc/modules-boot.d/ 加载模块
5.preinit() 启动/sbin/procd进程 ,执行/etc/preinit 脚本
void
preinit(void)
{
char *init[] = { "/bin/sh", "/etc/preinit", NULL };
char *plug[] = { "/sbin/procd", "-h", "/etc/hotplug-preinit.json", NULL };
int fd;
LOG("- preinit -\n");
plugd_proc.cb = plugd_proc_cb;
plugd_proc.pid = fork();
if (!plugd_proc.pid) {
execvp(plug[0], plug);
ERROR("Failed to start plugd: %m\n");
exit(-1);
}
if (plugd_proc.pid <= 0) {
ERROR("Failed to start new plugd instance: %m\n");
return;
}
uloop_process_add(&plugd_proc);
setenv("PREINIT", "1", 1);
fd = creat("/tmp/.preinit", 0600);
if (fd < 0)
ERROR("Failed to create sentinel file: %m\n");
else
close(fd);
preinit_proc.cb = spawn_procd;
preinit_proc.pid = fork();
if (!preinit_proc.pid) {
execvp(init[0], init);
ERROR("Failed to start preinit: %m\n");
exit(-1);
}
if (preinit_proc.pid <= 0) {
ERROR("Failed to start new preinit instance: %m\n");
return;
}
uloop_process_add(&preinit_proc);
DEBUG(4, "Launched preinit instance, pid=%d\n", (int) preinit_proc.pid);
}
6.执行完 /etc/preinit 脚本 之后,启动 procd
static void
spawn_procd(struct uloop_process *proc, int ret)
{
char *wdt_fd = watchdog_fd();
char *argv[] = { "/sbin/procd", NULL};
char dbg[2];
//......
execvp(argv[0], argv);
}
小结:
1./sbin/init 怎么来的?来自procd
2. /sbin/init 做了些什么?/sbin/init 进程最终是启动了/sbin/procd 进程,并执行/etc/preinit 脚本
3. 执行完 /etc/preinit 脚本 之后,启动 procd
todo:
后续分析/etc/preinit 脚本 ,procd 进程