本系列导航:
OpenWRT 启动流程(一) /sbin/init 进程分析
OpenWRT 启动流程(二) /etc/preinit 脚本分析
OpenWRT 启动流程(三) /sbin/procd 分析
在 /sbin/init 执行完后,调用了 spawn_procd 函数 ,启动了 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);
}
procd 代码位于 procd-2018-03-28-dfb68f85\procd.c
跟踪代码:
main
procd_state_next
state_enter
这里主要来看 state_enter
static void state_enter(void)
{
char ubus_cmd[] = "/sbin/ubusd";
switch (state) {
case STATE_EARLY:
LOG("- early -\n");
watchdog_init(0);
hotplug("/etc/hotplug.json");
procd_coldplug();
break;
case STATE_UBUS:
// try to reopen incase the wdt was not available before coldplug
watchdog_init(0);
set_stdio("console");
LOG("- ubus -\n");
procd_connect_ubus();
service_start_early("ubus", ubus_cmd);
break;
case STATE_INIT:
LOG("- init -\n");
procd_inittab();
procd_inittab_run("respawn");
procd_inittab_run("askconsole");
procd_inittab_run("askfirst");
procd_inittab_run("sysinit");
// switch to syslog log channel
ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
break;
case STATE_RUNNING:
LOG("- init complete -\n");
procd_inittab_run("respawnlate");
procd_inittab_run("askconsolelate");
break;
case STATE_SHUTDOWN:
/* Redirect output to the console for the users' benefit */
set_console();
LOG("- shutdown -\n");
procd_inittab_run("shutdown");
sync();
break;
case STATE_HALT:
// To prevent killed processes from interrupting the sleep
signal(SIGCHLD, SIG_IGN);
LOG("- SIGTERM processes -\n");
kill(-1, SIGTERM);
sync();
sleep(1);
LOG("- SIGKILL processes -\n");
kill(-1, SIGKILL);
sync();
sleep(1);
#ifndef DISABLE_INIT
if (reboot_event == RB_POWER_OFF)
LOG("- power down -\n");
else
LOG("- reboot -\n");
/* Allow time for last message to reach serial console, etc */
sleep(1);
/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
* in linux/kernel/sys.c, which can cause the machine to panic when
* the init process exits... */
if (!vfork( )) { /* child */
reboot(reboot_event);
_exit(EXIT_SUCCESS);
}
while (1)
sleep(1);
#else
exit(0);
#endif
break;
default:
ERROR("Unhandled state %d\n", state);
return;
};
}
-
case STATE_EARLY
这里主要是 调用 hotplug procd_coldplug ,自动创建设备节点,生成/dev/xxx -
case STATE_UBUS
初始化 ubus 连接 -
case STATE_INIT
读取 /etc/inittab
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
tts/0::askfirst:/usr/libexec/login.sh
ttyS0::askfirst:/usr/libexec/login.sh
tty1::askfirst:/usr/libexec/login.sh
void procd_inittab(void)
if (add_action(a, tags[TAG_ACTION]))
for (i = 0; i < ARRAY_SIZE(handlers); i++) // 见 handlers 定义
if (!strcmp(handlers[i].name, name)) {
a->handler = &handlers[i];
list_add_tail(&a->list, &actions);
return 0;
}
// handlers 定义
static struct init_handler handlers[] = {
{
.name = "sysinit",
.cb = runrc,
}, {
.name = "shutdown",
.cb = runrc,
}, {
.name = "askfirst",
.cb = askfirst,
.multi = 1,
}
//...
}
static void runrc(struct init_action *a)
{
if (!a->argv[1] || !a->argv[2]) {
ERROR("valid format is rcS <S|K> <param>\n");
return;
}
/* proceed even if no init or shutdown scripts run */
if (rcS(a->argv[1], a->argv[2], rcdone))
rcdone(NULL);
}
int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *))
{
runqueue_init(&q);
q.empty_cb = q_empty;
q.max_running_tasks = 1;
return _rc(&q, "/etc/rc.d", pattern, "*", param);
}
最终其实就是 读取 /etc/rc.d/ 下的启动脚本,逐个执行。 /etc/rc.d/ 目录文件实例:
root@172:/etc/rc.d# ls |sort
K10gpio_switch
K10mysqld
K10openvpn
K50dropbear
K50lighttpd
K81redis
K85odhcpd
K89log
K90network
K90sysfixtime
K98boot
K99umount
S00sysfixtime
S10boot
S10system
S11sysctl
S12log
S19dnsmasq
S19firewall
S20network
S35odhcpd
S36asterisk
S50cron
S50dropbear
S50lighttpd
S50odbc
S50php5-fastcgi
S50php5-fpm
S50yate
S90openvpn
S94gpio_switch
S95done
S95mysqld
S96led
S98redis
S98sysntpd
S99urandom_seed
-
case STATE_RUNNING:
初始化完成 -
case STATE_SHUTDOWN:
暂时不细究 -
case STATE_HALT:
暂时不细究
总结:
/sbin/procd 会读取 /etc/inittab ,然后执行 /etc/rc.d/目录下的应用启动脚本。