busybox:reboot 命令流程

执行 reboot 命令,触发 halt_main() 调用:

// include/applets.h
// applets/applet_tables.c

static struct bb_applet applets[] = { /*    name, main, location, need_suid */
	...
	IF_HALT(APPLET_ODDNAME(reboot, halt, BB_DIR_SBIN, BB_SUID_DROP, reboot))
	...
};

init 进程发送 SIGTERM 信号:

// init/halt.c

int halt_main(int argc UNUSED_PARAM, char **argv)
{
	...
	static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };

	int delay = 0;
	int which, flags, rc;

	/* Figure out which applet we're running */
	for (which = 0; "hpr"[which] != applet_name[0]; which++) // which = 2
		continue;

	...

	/* We support -w even if !ENABLE_FEATURE_WTMP,
	 * in order to not break scripts.
	 * -i (shut down network interfaces) is ignored.
	 */
	flags = getopt32(argv, "d:nfwi", &delay); // flags = 0

	...

	if (!(flags & 2)) /* no -n */
		sync();

	/* Perform action. */
	rc = 1;
	if (!(flags & 4)) { /* no -f */
//TODO: I tend to think that signalling linuxrc is wrong
// pity original author didn't comment on it...
		if (ENABLE_LINUXRC) {
			/* talk to linuxrc */
			/* bbox init/linuxrc assumed */
			pid_t *pidlist = find_pid_by_name("linuxrc");
			if (pidlist[0] > 0)
				rc = kill(pidlist[0], signals[which]);
			if (ENABLE_FEATURE_CLEAN_UP)
				free(pidlist);
		}
		if (rc) {
			/* talk to init */
			if (!ENABLE_FEATURE_CALL_TELINIT) {
				/* bbox init assumed */
				rc = kill(1, signals[which]); // 向 init 进程发送 SIGTERM 信号
			} else {
				...
			}
		}
	} else {
		...
	}

	...
	return rc;
}

init 进程处理 SIGTERM 信号:

// init/init.c

// 启动 init 进程进入 init_main() 函数,注册信号处理接口,
// 然后在 reboot 执行时处理 SIGTERM 信号
int init_main(int argc UNUSED_PARAM, char **argv)
{
	...
	/* Check if we are supposed to be in single user mode */
	if (argv[1]
	 && (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1'))
	) {
		...
	} else {
		/* Not in single user mode - see what inittab says */

		/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
		 * then parse_inittab() simply adds in some default
		 * actions (i.e., INIT_SCRIPT and a pair
		 * of "askfirst" shells) */
		parse_inittab(); // 解析 /etc/inittab
	}
	...
	
	/* Set up signal handlers */
	if (!DEBUG_INIT) { // 设置信号处理接口为 record_signo()
		struct sigaction sa;

		/* Stop handler must allow only SIGCONT inside itself */
		memset(&sa, 0, sizeof(sa));
		sigfillset(&sa.sa_mask);
		sigdelset(&sa.sa_mask, SIGCONT);
		sa.sa_handler = stop_handler;
		/* NB: sa_flags doesn't have SA_RESTART.
		 * It must be able to interrupt wait().
		 */
		sigaction_set(SIGTSTP, &sa); /* pause */
		/* Does not work as intended, at least in 2.6.20.
		 * SIGSTOP is simply ignored by init:
		 */
		sigaction_set(SIGSTOP, &sa); /* pause */

		/* These signals must interrupt wait(),
		 * setting handler without SA_RESTART flag.
		 */
		bb_signals_recursive_norestart(0
			+ (1 << SIGINT)  /* Ctrl-Alt-Del */
			+ (1 << SIGQUIT) /* re-exec another init */
#ifdef SIGPWR
			+ (1 << SIGPWR)  /* halt */
#endif
			+ (1 << SIGUSR1) /* halt */
			+ (1 << SIGTERM) /* reboot */
			+ (1 << SIGUSR2) /* poweroff */
#if ENABLE_FEATURE_USE_INITTAB
			+ (1 << SIGHUP)  /* reread /etc/inittab */
#endif
			, record_signo);
	}
	
	/* Now run the looping stuff for the rest of forever.
	 */
	while (1) {
		...
		maybe_WNOHANG |= check_delayed_sigs(); // 处理信号,包括 SIGTERM
		...
	}

	...
}

先看下 parse_inittab() 解析 /etc/inittab 的过程:

// init/init.c

static void parse_inittab(void)
{
#if ENABLE_FEATURE_USE_INITTAB
	char *token[4];
	parser_t *parser = config_open2("/etc/inittab", fopen_for_read);

	if (parser == NULL)
#endif
	{
		...
	}

	#if ENABLE_FEATURE_USE_INITTAB
	/* optional_tty:ignored_runlevel:action:command
	 * Delims are not to be collapsed and need exactly 4 tokens
	 */
	while (config_read(parser, token, 4, 0, "#:",
				PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
		/* order must correspond to SYSINIT..RESTART constants */
		static const char actions[] ALIGN1 =
			"sysinit\0""wait\0""once\0""respawn\0""askfirst\0"
			"ctrlaltdel\0""shutdown\0""restart\0";
		int action;
		char *tty = token[0];
		if (!token[3]) /* less than 4 tokens */
			goto bad_entry;
		action = index_in_strings(actions, token[2]);
		if (action < 0 || !token[3][0]) /* token[3]: command */
			goto bad_entry;
		/* turn .*TTY -> /dev/TTY */
		if (tty[0]) {
			tty = concat_path_file("/dev/", skip_dev_pfx(tty));
		}
		new_init_action(1 << action, token[3], tty);
		if (tty[0])
			free(tty);
		continue;
 bad_entry:
		message(L_LOG | L_CONSOLE, "Bad inittab entry at line %d",
				parser->lineno);
	}
	config_close(parser);
#endif

parse_inittab() 解析 /etc/inittab 中的 actions[] 定义的 action,包括 reboot 时的 shutdown,看一下 /etc/inittab 的内容:

# /etc/inittab
#
# Copyright (C) 2001 Erik Andersen <andersen@codepoet.org>
#
# Note: BusyBox init doesn't support runlevels.  The runlevels field is
# completely ignored by BusyBox init. If you want runlevels, use
# sysvinit.
#
# Format for each entry: <id>:<runlevels>:<action>:<process>
#
# id        == tty to run on, or empty for /dev/console
# runlevels == ignored
# action    == one of sysinit, respawn, askfirst, wait, and once
# process   == program to run

# Startup the system
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -o remount,rw /
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -a
::sysinit:/bin/hostname -F /etc/hostname
# now run any rc scripts
::sysinit:/etc/init.d/rcS

# Put a getty on the serial port
console::respawn:/sbin/getty -L  console 0 vt100 # GENERIC_SERIAL

# Stuff to do for the 3-finger salute
#::ctrlaltdel:/sbin/reboot

# Stuff to do before rebooting
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

看最后一行:

::shutdown:/bin/umount -a -r

指示在 reboot 时卸载所有的挂载的文件系统。看一下其过程。init 进程在 reboot 时收到了 SIGTERM 信号,然后处理信号:

static int check_delayed_sigs(void)
{
	...
	if ((1 << sig) & (0
#ifdef SIGPWR
		    + (1 << SIGPWR)
#endif
		    + (1 << SIGUSR1)
		    + (1 << SIGUSR2)
		    + (1 << SIGTERM)
		)) {
			halt_reboot_pwoff(sig);
		}
}

static void halt_reboot_pwoff(int sig)
{
	const char *m;
	unsigned rb;

	/* We may call run() and it unmasks signals,
	 * including the one masked inside this signal handler.
	 * Testcase which would start multiple reboot scripts:
	 *  while true; do reboot; done
	 * Preventing it:
	 */
	reset_sighandlers_and_unblock_sigs();

	run_shutdown_and_kill_processes(); // umount 文件系统,并向其进程发送终止信号 (SIGTERM + SIGKILL)

	m = "halt";
	rb = RB_HALT_SYSTEM;
	if (sig == SIGTERM) {
		m = "reboot";
		rb = RB_AUTOBOOT;
	} else if (sig == SIGUSR2) {
		m = "poweroff";
		rb = RB_POWER_OFF;
	}
	message(L_CONSOLE, "Requesting system %s", m);
	pause_and_low_level_reboot(rb); // 调用 sys_reboot 重启系统
	/* not reached */
}

static void run_shutdown_and_kill_processes(void)
{
	/* Run everything to be run at "shutdown".  This is done _prior_
	 * to killing everything, in case people wish to use scripts to
	 * shut things down gracefully... */
	// 执行 /etc/inittab 中定义的 shutdown 动作:
	// ::shutdown:/etc/init.d/rcK
	// ::shutdown:/sbin/swapoff -a
	// ::shutdown:/bin/umount -a -r
	run_actions(SHUTDOWN);

	message(L_CONSOLE | L_LOG, "The system is going down NOW!");

	/* Send signals to every process _except_ pid 1 */
	kill(-1, SIGTERM);
	message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
	sync();
	sleep(1);

	kill(-1, SIGKILL);
	message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
	sync();
	/*sleep(1); - callers take care about making a pause */
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值