想知道这个这个文件系统里面有哪些东西,我们需要跟着init进程继续分析,看这个进程需要哪些东西。
在根文件系统里面有很多命令,比如cp,ls等等,这些命令也就相对一个应用程序,这些命令可能有成千上百种。
对于这么多的命令,在嵌入式领域中,我们使用busybox,它是ls,cp,cd这些命令的组合,我们编译busybox的时候得到一个应用程序叫busybox,我们执行的ls,cp这些指令,实际上执行的都是这些指令到busybox的链接,比如在shell中输入ls,其实就是执行了busybox ls。
可以看到,ls和copy都是到busybox的链接。
使用ls和busybox ls是一样的。
事实上,在嵌入式系统,这个/sbin/init也是到busybox的链接。
所以说,我们想要确定这个init进程做了哪些事情,就需要找到busybox的源码,来分析一下。
找到配套的busybox,解压并用SI创建一个工程来分析一下。
在SI中导入busybox源码,可以看到文件中有一个cp.c,cp.c中有一个cp_main函数,可以推测,使用cp命令就是调用了cp_main函数。
同样,也有一个init.c文件,init.c文件中有一个init_main函数。
我们需要分一下这个init_main。
先猜测一下,init程序会做什么事情呢?
答:回顾一下,u-boot的目的是启动内核,内核的目的是启动应用程序。
内核怎么启动应用程序呢?
答:内核首先启动了init进程,它位于/sbin/init或其他,由这个进程来启动其他的应用程序。
不同的客户要启动的应用程序各不相同,我们怎么知道要启动哪些应用程序呢?
答:应该还要有一个配置文件,这个配置文件决定了后续还要启动哪些应用程序。
所以,init进程至少要做以下三件事:
读取配置文件;
解析配置文件;
执行用户程序。
阅读源码看下是不是这样。
打开init.c,查看init_main函数,前面是设置信号,先不管。
继续向下看,初始化控制台(之前打开了控制台,这里初始化一下)等等。
接着向下看,我们启动内核时没有传入任何参数,所以argc小于1,执行parse_inittab,从名字就可以看出来,这个是解析init table。
打开parse_inittab,可以看到首先是打开一个文件/etc/inittab,显然这个就是配置文件,在linux系统中,配置文件一般都放在etc目录下。打开配置文件后面就是解析了。
解析之前,我们先看一下/etc/inittab,打开/etc/inittab,可以看到inittab的格式。
这些配置文件的目的是启动不同的应用程序,根据这个目的,我们可以猜测一下配置文件里面肯定会有:
指定执行哪些应用程序;
指定何时执行这些应用程序。
根据文档说明可以知道,id项会被加上/dev/,也就是/dev/id,用作控制终端(也就是我们的标准输入,标准输出,标准错误,stdin,stdout,stderr : printf,scanf,err)。
然后是runlevels,这里说可以完全忽略掉。
然后是action,指示何时执行,process,指示执行的应用程序或脚本。红框是action的取值范围。
继续分析源码,可以看到如果没有这个文件,那么就执行默认项,否则解析配置文件。
下面是对配置文件的一些解析,如果是#开头的就忽略掉。
然后id是加上/dev/前缀,解析完最终要调用new_init_action。显然,要了解这些配置项怎么起作用,需要来解析一下这个new_init_action。
以默认配置(即fopen INITTAB失败时)的new_init_action为例,来解析一下。
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
在源码里面搜索一下bb_default_login_shell,可以看到,就是"-/bin/sh",VC_2就是"/dev/tty2"。
所以
new_init_action(ASKFIRST, "-/bin/sh", "/dev/tty2");
就等于
new_init_action(ASKFIRST, "-/bin/sh", "/dev/tty2");
从函数定义可以看出,action(执行时机)是ASKFIRST,command(应用程序或脚本)是-/bin/sh,id(终端)是/dev/tty2。
接着分析new_init_action,可以看到有一个init_action_list,可以猜测这是一个初始化动作的链表。
查看init_action_list的定义,可以看到init_action_list是一个指向init_action结构体的指针。
可以猜测,这个new_init_action函数的作用:
创建一个init_action结构,并填充;
把这个结构放入init_action_list链表。
现在,假设没有配置文件,根据默认的配置来反推出默认的配置文件,看看它是什么内容。
其中,773行可以省略,这是因为在PC上,如果系统资源不足,就会把某些不用的进程释放到硬盘中去,将内存空间空出来,但是在嵌入式系统中一般不会这样做,所以if不会进去。
/* Swapoff on halt/reboot */
if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
最终,反推出来的默认的配置文件如下:
那么,构建了这样一个链表,之后要怎么使用呢?继续往下看。
构建链表之后就是执行,先执行SYSINIT这一类的动作,然后是WAIT,然后是ONCE。
再接下来是一个死循环。
前面说过,action决定了执行时机,它的取值范围有八个,但是inittab中没有说明这些值代表什么意思,我们结合代码分析一下。
首先分析一下run_action函数,可以看到,上面说的8个action在这里都有对应的执行程序。
首先看一下SYSINIT,它调用了两个函数waitfor和delete_init_action,这两个函数的作用分别是:
执行应用程序,并等待它执行完毕(waitfor);
在链表中删除执行完的init程序(delete_init_action)。
waitfor的源码如下,可以看到先执行run(a),该函数会创建process子进程,然后在while循环中waitpid,等待a执行完毕。
delete_init_action则是在链表中删除执行完的init程序。
再看一下run函数,函数前面执行了很多系统调用,最后是一个BB_EXECVP,它也是一个系统调用,用来执行子进程。
所以,SYSINIT的功能就是系统初始化,在系统最开始的时候执行,并且执行的时候,系统会等待它执行完毕,执行完毕后就会从链表删除。
然后是WAIT,它的作用和SYSINIT类似,只是它是在SYSINIT之后执行,先执行SYSINIT然后再执行WAIT。
然后是ONCE,可以看到它和SYSINIT也是类似的,只是系统不会等待它执行完毕,同样是执行一次后从链表删除。
然后是RESPAWN和ASKFIRST,只有在pid等于0时才执行。
系统一开始的时候,pid都是0,只有开始执行该进程后,系统才会给它分配一个非0的pid。
可以看到,在init_main的while循环中,会不断的执行RESPAWN和ASKFIRST,然后在它们执行完毕后再将它们的pid清零,这样它们才可以再次执行。
那么,RESPAWN和ASKFIRST有什么区别呢?
这就要从run函数来分析了,在run函数中,当action包含ASKFIRST时,在执行该process前,会先在标准输出输出一条询问信息,此时只有输入回车才会继续向下运行,
这就是RESPAWN和ASKFIRST的差别,ASKFIRST从名字也可以看出来,在执行前先询问一下。
八个action,我们分析了SYSINIT,WAIT,ONCE,RESPAWN,ASKFIRST五个,还有restart,ctrlaltdel,shutdown是做什么用的呢?
在init_main函数的开头,我们看到有一些信号量,这些信号量调用的函数里面,就会用到这些action。
ctrlaltdel_signal就会来执行ctrlaltdel这一类的函数,restart和shutdown也是类似的。
回顾一下,init_main函数至少需要这三项:
打开终端/dev/console;(还需要一个/dev/null)
需要配置文件/etc/inittab;
配置文件里指定的应用程序;
需要注意的是,如果在配置文件中不设置id的话,那么该进程的标准输入,标准输出,标准错误,就都定义到/dev/null里去。
假设我们写一个简单的应用程序,这些函数都不是它自己实现的,而是C库实现的,所以还需要一个库文件。
int main(void)
{
printf("hello, world!n"); //C库
fopen(); //C库
fwrite(); //C库
fclose(); //C库
}
另外,init程序本身也是busybox,所以一个最小的根文件系统,需要以下五种组成:
/dev/console和/dev/null;
init应用程序 ==> busybox;
配置文件/etc/inittab;
配置文件指定的应用程序;
C库;
构建这五种东西,就可以构建出一个根文件系统了,下一节我们就来创建它。
上一篇:1_5.4.1_根文件系统_构建根文件系统之启动第1个程序_P
下一篇:1_5.4.3_根文件系统_构建根文件系统之busybox_P
推荐阅读最新更新时间:2024-10-25 08:32
设计资源 培训 开发板 精华推荐
- DC2035A,使用 LTC5551、300MHz 至 3.5GHz 超高动态范围下变频混频器的演示板
- AD9680-1250EBZ、9680CE04B AD9680-1250高速ADC全输入带宽评估板
- DC2672A-A,使用 LTM4664 48VIN、双路 25A 稳压器和数字电源系统管理的演示板
- LTC3892EFE-1 高效率、双路 5V/12V 输出同步降压型 DC/DC 控制器的典型应用电路
- 焊接烟雾净化器
- LTC2313-12、12 位、2.5Msps 串行采样 ADC 的典型应用
- 爱葬式1号 - FS312快充诱骗扩展板
- Si535x-B20QFN-EVB,用于 Si5351 任意频率、0.008-160 MHz CMOS 时钟发生器 + VCXO 的评估板
- DER-580 - 118W高线输入非PFC CV/CC反激式充电器电源
- 具有短路保护功能的 NCV78M05BTG 5V 电流升压的典型应用