先来介绍一下什么是Busybox:它是将众多的UNIX命令集合进一个很小的可执行程序中。
在制作根文件系统之内核如何启动init进程中遗留了一个问题是/linuxrc是内核启动的第一个应用程序,那么它是什么?我们看到移植好的根文件系统环境,输入ls -al /linuxrc。可以看到:
lrwxrwxrwx 1 1000 1000 11 Jul 26 2018 linuxrc -> bin/busybox
/linuxrc其实是指向Busybox的,它其实是Busybox下的一个程序,所以我们需要分析Busybox的源码来研究/linuxrc这个程序做的事情,当在移植好的根文件系统环境下输入/linuxrc可以看到:
# busybox linuxrc init started: BusyBox v1.7.0 (2018-07-26 12:50:40 CST) starting pid 796, tty '': '/etc/init.d/rcS' Please press Enter to activate this console.
这就是执行linuxrc程序之后出现的界面,现在还不确定/linuxrc是Busybox下哪个文件,我们搜索“init started”,在busybox-1.7.0\init\Init.c中找到了它
935 message(MAYBE_CONSOLE | L_LOG, "init started: %s", bb_banner);
我们看到调用它的是init_main函数,接下去就是分析这个函数,init_main函数的功能大概可以概括为
1、设置处理信号函数
2、读取配置文件、解析配置文件
3、根据配置文件执行用户的程序或脚本
下面分别介绍
1、设置处理信号函数,restart, ctrlaltdel, and shutdown是一些按键事件,这些处理函数在接收到按下相关按键的信号后,进行处理
905 signal(SIGHUP, exec_signal); 906 signal(SIGQUIT, exec_signal); 907 signal(SIGUSR1, shutdown_signal); 908 signal(SIGUSR2, shutdown_signal); 909 signal(SIGINT, ctrlaltdel_signal); 910 signal(SIGTERM, shutdown_signal); 911 signal(SIGCONT, cont_handler); 912 signal(SIGSTOP, stop_handler); 913 signal(SIGTSTP, stop_handler);
2、读取和解析配置文件,先看一下配置文件的格式,文档位于example/inittab中
# inittab的格式为: # <id>:<runlevels>:<action>:<process> # id => /dev/id,用作终端:stdin、stdout、stderr:printf、scanf、err # runlevels:忽略 # action :执行时机Valid actions include: sysinit, respawn, askfirst, wait, once, restart, ctrlaltdel, and shutdown. # process :应用程序或脚本
接着回到init_main函数,看到
967 parse_inittab();//解析inittab参数
找到parse_inittab函数,位于busybox-1.7.0\init\Init.c中
755 static void parse_inittab(void) 756 { ... ... 764 file = fopen(INITTAB, "r");//以只读方式打开/etc/inittab ... 788 while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) { ... 836 for (a = actions; a->name != 0; a++) { 837 if (strcmp(a->name, action) == 0) { 838 if (*id != '\0') { 839 if (strncmp(id, "/dev/", 5) == 0)//去掉开头的/dev/ 840 id += 5; 841 strcpy(tmpConsole, "/dev/"); 842 safe_strncpy(tmpConsole + 5, id, 843 sizeof(tmpConsole) - 5); 844 id = tmpConsole; 845 } 846 new_init_action(a->action, command, id);//创建一个init_action结构,以解析出来的参数填充它,然后将这个结构放入init_action_list链表 847 break; 848 } 849 } ... } 857 }
这个函数的功能解释为打开/etc/inittab文件然后处理它,这个文件是以配置文件的格式存放的。看到函数最后会创建一个init_action结构,这个结构就是将配置文件里的内容一个个取出来,然后放在里面。最后链接到init_action_list链表中
72 struct init_action { 73 struct init_action *next; 74 int action; 75 pid_t pid;//进程号 by andy 76 char command[INIT_BUFFS_SIZE];//要执行的脚本命令 77 char terminal[CONSOLE_NAME_SIZE];//终端 78 };
这里再举个例子,下面是某个配置文件。以第一个为例子。action为askfirst执行时机;command为执行的命令或脚本为-/bin/sh;terminal为使用的中断为console
console::askfirst:-/bin/sh
::sysinit:/etc/init.d/rcS
3、根据配置文件执行用户的程序或脚本
接着看到,每个执行时机执行的内容,以下均为简写。
busybox-> init_main parse_inittab file = fopen(INITTAB, "r");//打开配置文件/etc/inittab new_init_action //a、创建一个init_action结构,填充 //b、将这个结构放入init_action_list链表 run_actions(SYSINIT); waitfor(a, 0); //执行应用程序,等待它执行完毕 run(a) //创建process子进程 waitpid(runpid, &status, 0);//等待它结束 delete_init_action(a);//在init_action_list链表里删除 run_actions(WAIT); waitfor(a, 0); //执行应用程序,等待它执行完毕 run(a) //创建process子进程 waitpid(runpid, &status, 0);//等待它结束 delete_init_action(a);//在init_action_list链表里删除 run_actions(ONCE); run(a); //创建process子进程 delete_init_action(a);//在init_action_list链表里删除 while(1) { run_actions(RESPAWN); if (a->pid == 0) { a->pid = run(a); } run_actions(ASKFIRST); if (a->pid == 0) { a->pid = run(a); 打印:Please press Enter to activate this console. 等待回车 创建子进程 } wpid = wait(NULL);//等待子进程退出 while (wpid > 0) { a->pid = 0;//退出后就设置pid=0 } 其余三个restart, ctrlaltdel, and shutdown执行时机在按下按键的时候发生事件 }
大致总结一下每个执行时机:
SYSINIT:第一个执行,创建子进程,等待进程结束,将它从init_action_list链表里删除
WAIT:第二个执行,创建子进程,等待进程结束,将它从init_action_list链表里删除
ONCE:第三个执行,创建子进程,将它从init_action_list链表里删除
RESPAWN:循环执行
ASKFIRST:循环执行,打印:Please press Enter to activate this console,等待回车后,创建子程序-/bin/sh,就不再返回了
restart, ctrlaltdel, and shutdown:在按键按下的时候随时执行
这样就分析完了Busybox的init程序,ASKFIRST时机相关的脚本或命令执行以后,因为脚本有-/bin/sh,所以最终会进入sh进程。它位于shell\Ash.c下的ash_main函数,这个函数就不分析了。到这里从uboot开始一直到根文件系统下的第一个用户程序全部分析完成。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步