Linux根文件系统分析-init进程和busybox

前言

写文章的目的是想通过记录自己的学习过程,以便以后使用到相关的知识点可以回顾和参考。

一、运行流程框架

在这里插入图片描述

二、BusyBox下载

一讲到根文件系统,第一个就是想到BusyBox这个东东,它集成了BusyBox 是一个大的工具箱,这个工具箱里面集成了 Linux 的许多工具和命令,如 ls、mv、ifconfig 等命令 BusyBox 都会提供。等下分析的 init 进程函数也是在BusyBox的源码中的,所以要想知道init进程的运行流程,就必须把BusyBox源码下载下来。BusyBox可直接在官网下载:https://busybox.net/downloads/

解压后得到以下文件
在这里插入图片描述
而init进程函数在init文件夹的init.c中。

三、init进程分析,函数init_main

打开init文件夹,里面有init.c的c程序,init.c中有一个名字为init_main的函数,这个函数的内容就是init进程的执行过程了。内面的内容很多,简化后的代码如下:

int init_main(int argc UNUSED_PARAM, char **argv)
{
	parse_inittab();
	
	run_actions(SYSINIT);
	run_actions(WAIT);
	run_actions(ONCE);
	while (1) {
		run_actions(RESPAWN | ASKFIRST);
		while (1) {
			wpid = waitpid(-1, NULL, maybe_WNOHANG);
			if (wpid <= 0)
				break;
			maybe_WNOHANG = WNOHANG;
		}
	}
}

上面是简化后的init_main的程序结构,上面只有比较主要的几个函数。第一个函数parse_inittab完成了配置。那么下一步开始执行时机类型为sysinit, wait, once, respawn, askfirst等类型的应用程序。我从网上看到一张图,它概括了init_main函数的执行过程,如下所示:
在这里插入图片描述
其中,读取配置文件和解析配置文件在函数parse_inittab()中完成,执行应用程序则通过run_actions(xxx)函数(xxx为程序类型)中完成。

四、函数parse_inittab

在讲解parse_inittab函数之前,先说一下inittab文件。
inittab 的详细内容可以参考 busybox 下的文件 examples/inittab。init 程序会读取/etc/inittab这个文件,inittab 由若干条指令组成。每条指令的结构都是一样的,由以“:”分隔的 4 个段组成,格式如下:

<id>:<runlevels>:<action>:<process> 

<id>:每个指令的标识符,不能重复。但是对于 busybox 的 init 来说,<id>有着特殊意义。 对于 busybox 而言<id>用来指定启动进程的控制 tty,一般我们将串口或者 LCD 屏幕设置为控 制 tty。
<runlevels>:对 busybox 来说此项完全没用,所以空着。 
<action>:动作,用于指定<process>可能用到的动作。busybox 支持的动作如下图所示。 
<process>:具体的动作,比如程序、脚本或命令等。

在这里插入图片描述

下面分析一下parse_inittab函数是怎样读取配置文件和解析配置文件的,parse_inittab函数的内容如下:

static void parse_inittab(void)
{
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
	{
		new_init_action(SYSINIT, INIT_SCRIPT, "");    				//::sysinit:/etc/init.d/rcS, 系统启动以后运行/etc/init.d/rcS 这个脚本文件
		new_init_action(ASKFIRST, bb_default_login_shell, "");		//::askfirst:-/bin/sh
		new_init_action(ASKFIRST, bb_default_login_shell, VC_2);	//tty2::askfirst:-/bin/sh, 将tty2作为控制台终端
		new_init_action(ASKFIRST, bb_default_login_shell, VC_3);	//tty3::askfirst:-/bin/sh
		new_init_action(ASKFIRST, bb_default_login_shell, VC_4);	//tty4::askfirst:-/bin/sh
		new_init_action(CTRLALTDEL, "reboot", "");					//::ctrlaltdel:/sbin/reboot, 按下 ctrl+alt+del 组合键的话就运行/sbin/reboot,看来 ctrl+alt+del 组合键用于重
启系统
		new_init_action(SHUTDOWN, "umount -a -r", "");				//::shutdown:/bin/umount -a -r, 关机的时候执行/bin/umount,也就是卸载各个文件系统
		new_init_action(SHUTDOWN, "swapoff -a", "");				//::shutdown:/sbin/swapoff -a, 关机的时候执行/sbin/swapoff,也就是关闭交换分区
		new_init_action(RESTART, "init", "");						//::restart:/sbin/init, 重启的话运行/sbin/init
		return;
	}

#if ENABLE_FEATURE_USE_INITTAB
	while (config_read(parser, token, 4, 0, "#:",
				PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
		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;
		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
}

可以看出它先通过打开/etc/inittab文件,如果根目录中没有/etc/inittab文件,则使用默认的inittab条目(默认的inittab条目我在上面代码中已通过注释解释出来),如果根目录中有/etc/inittab文件,则把inittab文件中的内容解析出来。

五、函数run_actions

再回到init_main函数往下看,当执行完parse_inittab函数,即读取配置文件和解析配置文件完成,将会根据配置文件调用run_actions函数来执行应用程序,run_actions函数内容如下:

static void run_actions(int action_type)
{
	struct init_action *a;

	for (a = G.init_action_list; a; a = a->next) {
		if (!(a->action_type & action_type))
			continue;

		if (a->action_type & (SYSINIT | WAIT | ONCE | CTRLALTDEL | SHUTDOWN)) {
			pid_t pid = run(a);
			if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN))
				waitfor(pid);
		}
		if (a->action_type & (RESPAWN | ASKFIRST)) {
			/* Only run stuff with pid == 0. If pid != 0,
			 * it is already running
			 */
			if (a->pid == 0)
				a->pid = run(a);
		}
	}
}

可以看出 :
ONCE 类型的应用程序仅执行一次,而且不会等待 process 执行完成。
SYSINIT, WAIT ,CTRLALTDEL ,SHUTDOWN 类型的应用程序执行完以后,会等待 process 执行结束。
RESPAWN ,ASKFIRST类型的应用程序当 process 执行完毕退出时,设置其PID为0,然后再次执行该 process ,循环执行。

六、总结

最小的根文件系统由下面5部分组成:

(1)dev/console 和 dev/null,回顾内核启动init程序时,open("/dev/console") 尝试打开/dev/console设备文件,如果成功即为init进程标准输入设备
(2)init应用程序(来源于bustbox)
(3)init程序运行时,要读配置文件 /etc/inittab
(4)配置文件指定的应用程序
(5)C库,即使用c库函数fopen ,fwrite 等函数时使用到的c库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值