0x03 Openwrt系统---启动流程

  1. bootloader将kernel从flash中拷贝到RAM以后,则其他的事情交给了kernel。
  2. 原生的Linux内核默认启动的第一个用户空间进程是(busybox)/sbin/init,openwrt则是/etc/preinit,但不是1号进程,而是/sbin/procd
  3. /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   
  1. /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;
}
  1. /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);
}
  1. /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;
}
  1. /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
发布了31 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/q759451733/article/details/102710023
今日推荐