Linux启动之后运行的第一个进程init会从/etc/inittab 文件中读取配置并做一些初始化的工作
格式:
id:runlevels:action:process
大致的含义就是init初始化程序以一定的方式(action)在哪些运行级别(runlevels)下执行某个程序(process)
id
用于表示该配置项的id,长度为1~4个字符,id必须在本文件内唯一,如果有重复id,则init启动的时候会报duplicate id的错误,且后面的重复项配置将无效。这边可以将每一条配置项看作数据库中的一条记录,分号分割的部分为各个字段,将id号则可以看作记录的Primary Key。
runlevels
程序执行的运行级别。
action
常见的action如下所示:
* initdefault: 该配置项指定系统的默认运行级。
* respawn: 该配置项所指定的进程如果被结束,则重新启动该进程。
* wait: 该配置项指定的进程在运行结束之前不要执行下一条配置项。
* once: 切换到对应的运行级之后,仅执行指定的进程一次。
* sysinit: 无论以什么运行级启动,系统启动时都要执行该配置项指定的进程。
* boot: 仅在系统启动时执行一次。
* bootwait: 仅在系统启动时执行一次,在执行结束之前不执行下一条配置项
* powerfail: 当接收到UPS的断电通知时执行该项指定的进程。
* powerwait: 与powerfail相同,但init会等待进程执行结束。
* powerokwait: 接收到UPS的供电通知时执行。
* ctrlaltdel: 当用户同时按下 Ctrl+Alt+Del 时执行该项指定的进程。
process:
指定的程序路径和参数
例子:
举一个最常见的例子,就是默认地我们可以使用Ctrl+Alt+F1~F6切换的虚拟终端功能就是在此文件中定义的:
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
id列可以用数字,也可以用字符,当然数字也是当字符来看,只要确保本文件唯一即可。
runlevel定义了在2~5的运行级别都存在对应的虚拟终端,所以在2~5的运行级别下可以通过Ctrl+Alt+F1~F6进行终端的切换,而在s[单用户模式]和运行级别为1的时候则无法使用虚拟终端[虽然可以通过Ctrl+Alt+F1~F6进行切换,但终端已经被挂起]。
respawn则定义了如果mingetty进程结束,则重启该进程。
/sbin/mingetty ttyn定义了执行的程序和参数。
要想/etc/inittab即时生效,可以通过以下命令让init进程重新读取此文件:
init q
###############################
内核函数:要执行的第一个应程序函数:
###############################
static int noinline init_post(void)
{
free_initmem();
unlock_kernel();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
}
###############################################
《大致过程》
第一、open()所以创建/dev/console与/dev/null目录
因为执行应用程序,就得先打开设备节点
/dev/null是用来不设置ID是(在inittab格式)时,标准输入,输出,错误等久会定位到它去;
第二、设置配置文件/etc/inittab
第三、配置文件里指定用户应用程序
第四、C库(因为我们写应用程序时,printf,read等都是C库实现的)
第五、init(busybox编译)
###############################################
《busybox分析》
busybox->init.c->init_main()函数
---------------------------------------------------------
init_mian(){
parse_inittab()
1.parser_t *parser =config_open2("/etc/inittab", fopen_for_read);
2.new_init_action()
/*1.创建init_action结构体,用inittab内容去填充
*2.然后把这个结构体放入链表init_action_list中去
*/
run_actions(SYSINIT);
1.waitfor(a,0); /*执行应用程序,等待它执行完毕*/
run(a); /*创建process子进程*/
waitpid(runpid,&status,0);/*等待它结束*/
2.delete_init_actin(a);/*在init_action_list链表里删除*/
run_actions(WAIT);
1.waitfor(a,0); /*执行应用程序,等待它执行完毕*/
run(a); /*创建process子进程*/
waitpid(runpid,&status,0);/*等待它结束*/
2.delete_init_actin(a);/*在init_action_list链表里删除*/
run_actions(ONCE);
1.run(a); /*创建process子进程*/
2.delete_init_actin(a);/*在init_action_list链表里删除*/
while (1) {
run_actions(RESPAWN | ASKFIRST);
{
if (a->pid == 0){
a->pid = run(a);}
附:RESPAWN | ASKFIRST区别:
ASKFIRST:1.打印信息 2.等待回车 3.创建进程
wpid=wait(NULL);//等待子进程退出
whlie(wpid>0){
a->pid=0;//退出后,设置pid=0; }
}
}
########################################
《int_mian()->parse_inittab源代码分析》
static void parse_inittab(void)
{
#if ENABLE_FEATURE_USE_INITTAB
char *token[4];
**************第一,打开配置文件(/etc/inittab)************
**********************************************************
parser_t *parser = config_open2("/etc/inittab", fopen_for_read);
-----------------------------------------------
/*打开配置文件----配置文件有什么内容(就是按什么格式来写的)*/
-----------------------------------------------
《inittab配置文件格式》:
<id>:<runlevels>:<action>:<process>
<id:>: 用作终端;如标准IO(stdin,stdout,stderr,printf,scanf等)
<renlevles>: 运行级别->完全可以忽略
<action>: 执行时机(包含的选项:sysinit ,respawn ,askfirst,wait,oncerestart,ctrlaltdel, shutdown)
<process>: 应用程序或脚本(都为可执行文件)
---------------------------------------------
****************第二、解析配置文件**********************
********************************************************
《如何解析配置文件》
如:new_init_action(ASKFIRST, bb_default_login_shell, VC_2 );
==new_init_action(ASKFIRST, "-/bin/sh", "/dev/tty2");
new_init_action作用:
1.创建init_action结构体,用inittab内容去填充
2.然后把这个结构体放入链表init_action_list中去
********************************************************
if (parser == NULL)
/////*1.若没有配置文件传进来,则就执行<系统默认inittab配置文件>*/////
#endif
{
new_init_action(CTRLALTDEL, "reboot", "");
new_init_action(SHUTDOWN, "umount -a -r", "");
if (ENABLE_SWAPONOFF)
new_init_action(SHUTDOWN, "swapoff -a", "");
new_init_action(RESTART, "init", "");
new_init_action(ASKFIRST, bb_default_login_shell, "");
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/* sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
return;
}
---------------------------------------------
<系统默认的inittab配置文件>内容:
---------------------------------------------
::ctrlaltdel:reboot
::shutdown:umount -a -r
::restart:init
::askfirst:-/bin/sh
tty2:askfirst:-/bin/sh
tty3:askfirst:-/bin/sh
tty4:askfirst:-/bin/sh
---------------------------------------------
////*2.若有配置传进来,则就执行如下*/////
#if ENABLE_FEATURE_USE_INITTAB
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
}
########################