- bootloader将kernel从flash中拷贝到RAM以后,则其他的事情交给了kernel。
- 原生的Linux内核默认启动的第一个用户空间进程是(busybox)/sbin/init,openwrt则是/etc/preinit,但不是1号进程,而是/sbin/procd
- /etc/preinit源码(/package/base-files/files/etc),一般刚刚启动会执行/sbin/init:
#!/bin/sh
# Copyright (C) 2006-2016 OpenWrt.org
# Copyright (C) 2010 Vertical Communications
[ -z "$PREINIT" ] && exec /sbin/init # $PREINIT 为空则为真,$$前面为真则继续执行后面的。
export PATH="%PATH%"
# /package/base-files/files/etc/preinit
. /lib/functions.sh
. /lib/functions/preinit.sh
. /lib/functions/system.sh
# boot_hook_init(/package/base-files/files/lib/functions/preinit.sh)初始化一个函数队列,
boot_hook_init preinit_essential
boot_hook_init preinit_main
boot_hook_init failsafe
boot_hook_init initramfs
boot_hook_init preinit_mount_root
# 目录/package/base-files/files/lib/preinit下的所有脚本
for pi_source_file in /lib/preinit/*; do
. $pi_source_file
done
# boot_run_hook(/package/base-files/files/lib/functions/preinit.sh)
boot_run_hook preinit_essential
pi_mount_skip_next=false
pi_jffs2_mount_success=false
pi_failsafe_net_message=false
boot_run_hook preinit_main
- /sbin/init(/build_dir/target-i386_pentium4_musl-1.1.16/procd-2017-08-08-66be6a23/initd)初始化各种,如环境变量设置、文件系统挂载、内核模块加载等,之后创建两个进程(/etc/preinit 中创建/sbin/procd)
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/reboot.h>
#include <libubox/uloop.h>
#include <libubus.h>
#include <limits.h>
#include <stdlib.h>
#include <fcntl.h>
#include <getopt.h>
#include <libgen.h>
#include <regex.h>
#include <unistd.h>
#include <stdio.h>
#include "../utils/utils.h"
#include "init.h"
#include "../watchdog.h"
unsigned int debug = 0;
static void
signal_shutdown(int signal, siginfo_t *siginfo, void *data)
{
fprintf(stderr, "reboot\n");
fflush(stderr);
sync();
sleep(2);
reboot(RB_AUTOBOOT);
while (1)
;
}
static struct sigaction sa_shutdown = {
.sa_sigaction = signal_shutdown,
.sa_flags = SA_SIGINFO
};
static void
cmdline(void)
{
char line[20];
char* res;
long r;
res = get_cmdline_val("init_debug", line, sizeof(line));
if (res != NULL) {
r = strtol(line, NULL, 10);
if ((r != LONG_MIN) && (r != LONG_MAX))
debug = (int) r;
}
}
int
main(int argc, char **argv)
{
pid_t pid;
//设置日志打印级别和输出日志方式
ulog_open(ULOG_KMSG, LOG_DAEMON, "init");
//设置信号处理函数,SIGTERM、SIGUSR1、SIGUSR2信号的处理是重启系统
sigaction(SIGTERM, &sa_shutdown, NULL);
sigaction(SIGUSR1, &sa_shutdown, NULL);
sigaction(SIGUSR2, &sa_shutdown, NULL);
early();// /build_dir/target-i386_pentium4_musl-1.1.16/procd-2017-08-08-66be6a23/initd
cmdline();
watchdog_init(1);// /build_dir/target-i386_pentium4_musl-1.1.16/procd-2017-08-08-66be6a23
/*创建子进程运行/sbin/kmodloader,加载一些ko模块(先在/etc/modules-boot.d目录下遍历所以的文件内容)*/
pid = fork();
if (!pid) { // 子进程 pid=0
char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NUL
L };
if (debug < 3)
patch_stdio("/dev/null");
execvp(kmod[0], kmod);
ERROR("Failed to start kmodloader\n");
exit(-1);
}
if (pid <= 0) {
ERROR("Failed to start kmodloader instance\n");
} else {
int i;
for (i = 0; i < 1200; i++) {
if (waitpid(pid, NULL, WNOHANG) > 0)
break;
usleep(10 * 1000);
watchdog_ping();
}
}
uloop_init();
///build_dir/target-i386_pentium4_musl-1.1.16/procd-2017-08-08-66be6a23/initd
preinit();
uloop_run();
return 0;
}
- /etc/preinit(/build_dir/target-i386_pentium4_musl-1.1.16/procd-2017-08-08-66be6a23/initd)
#define _GNU_SOURCE
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <fcntl.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <stdio.h>
#include <unistd.h>
#include "init.h"
#include "../watchdog.h"
static struct uloop_process preinit_proc;
static struct uloop_process plugd_proc;
static void
check_dbglvl(void)
{
FILE *fp = fopen("/tmp/debug_level", "r");
int lvl = 0;
if (!fp)
return;
if (fscanf(fp, "%d", &lvl) == EOF)
ERROR("failed to read debug level\n");
fclose(fp);
unlink("/tmp/debug_level");
if (lvl > 0 && lvl < 5)
debug = lvl;
}
static void
spawn_procd(struct uloop_process *proc, int ret)
{
char *wdt_fd = watchdog_fd();
char *argv[] = { "/sbin/procd", NULL};
struct stat s;
char dbg[2];
if (plugd_proc.pid > 0)
kill(plugd_proc.pid, SIGKILL);
if (!stat("/tmp/sysupgrade", &s))
while (true)
sleep(1);
unsetenv("INITRAMFS");
unsetenv("PREINIT");
unlink("/tmp/.preinit");
DEBUG(2, "Exec to real procd now\n");
if (wdt_fd)
setenv("WDTFD", wdt_fd, 1);
check_dbglvl();
if (debug > 0) {
snprintf(dbg, 2, "%d", debug);
setenv("DBGLVL", dbg, 1);
}
execvp(argv[0], argv);
}
static void
plugd_proc_cb(struct uloop_process *proc, int ret)
{
proc->pid = 0;
}
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); //子进程注册热插拔事件处理函数,即/etc/hotplug-preinit.json文件为接收到热插拔时间后所需要调用的脚本。
ERROR("Failed to start plugd\n");
exit(-1);
}
if (plugd_proc.pid <= 0) {
ERROR("Failed to start new plugd instance\n");
return;
}
uloop_process_add(&plugd_proc);
setenv("PREINIT", "1", 1); //设置PREINIT变量为1
fd = creat("/tmp/.preinit", 0600);
if (fd < 0)
ERROR("Failed to create sentinel file\n");
else
close(fd);
preinit_proc.cb = spawn_procd;// 在init执行完毕后执行该回调函数,就由procd来继续执行初始化(该函数会用procd将/sbin/init进程替换,从而procd的进程号为1)
preinit_proc.pid = fork();
if (!preinit_proc.pid) {
execvp(init[0], init);// 回去执行/etc/preinit 脚本的后半部分
ERROR("Failed to start preinit\n");
exit(-1);
}
if (preinit_proc.pid <= 0) {
ERROR("Failed to start new preinit instance\n");
return;
}
uloop_process_add(&preinit_proc);
DEBUG(4, "Launched preinit instance, pid=%d\n", (int) preinit_proc.pid);
}
- /sbin/procd(/build_dir/target-i386_pentium4_musl-1.1.16/procd-2017-08-08-66be6a23)
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/reboot.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>
#include "procd.h"
#include "watchdog.h"
#include "plug/hotplug.h"
unsigned int debug;
static int usage(const char *prog)
{
fprintf(stderr, "Usage: %s [options]\n"
"Options:\n"
" -s <path> Path to ubus socket\n"
" -h <path> run as hotplug daemon\n"
" -d <level> Enable debug messages\n"
" -S Print messages to stdout\n"
"\n", prog);
return 1;
}
int main(int argc, char **argv)
{
int ch;
char *dbglvl = getenv("DBGLVL");
int ulog_channels = ULOG_KMSG;
if (dbglvl) {
debug = atoi(dbglvl);
unsetenv("DBGLVL");
}
while ((ch = getopt(argc, argv, "d:s:h:S")) != -1) {
switch (ch) {
case 'h':
return hotplug_run(optarg);
case 's':
ubus_socket = optarg;
break;
case 'd':
debug = atoi(optarg);
break;
case 'S':
ulog_channels = ULOG_STDIO;
break;
default:
return usage(argv[0]);
}
}
ulog_open(ulog_channels, LOG_DAEMON, "procd");
setsid();
uloop_init();
procd_signal();
if (getpid() != 1)
procd_connect_ubus();
else
procd_state_next();
uloop_run();
uloop_done();
return 0;
}
- /etc/inittab(/target/linux/ramips/base-files/etc)是由/sbin/procd来解释,目的是初始化文件系统,负责设置init初始化程序初始化脚本在哪里,每个运行级别初始化时运行的命令。开机、关机重启对应的命令,各运行级登录时所使用的命令。
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
::askconsole:/usr/libexec/login.sh