之前讲过虚拟文件系统挂载根文件系统之后,会执行用户程序,参见博文:

http://blog.chinaunix.net/uid-29401328-id-4909649.html

但只提了一下内核会启动init进程,没详细讲,今天就从这里开始讲下文件系统的启动过程

linux-2.6.30.4内核, busybox-1.16.0

根据之前那篇博文我们知道,文见系统挂载最后调用的函数为:init_post,源码如下:

点击(此处)折叠或打开

static noinline int init_post(void)

__releases(kernel_lock)

{

/* need to finish all async __init code before freeing the memory */

async_synchronize_full();

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);

current->signal->flags |= SIGNAL_UNKILLABLE;

if (ramdisk_execute_command) {

run_init_process(ramdisk_execute_command);

printk(KERN_WARNING "Failed to execute %s\n",

ramdisk_execute_command);

}

/*

* We try each of these until one succeeds.

*

* The Bourne shell can be used instead of init if we are

* trying to recover a really broken machine.

*/

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.");

}

20行:ramdisk_execute_command值通过“rdinit=”指定,如果未指定,往下执行;

这个参数解析是在:

linux-2.6.30.4\init\Main.c

static int __init rdinit_setup(char *str)

{

unsigned int i;

ramdisk_execute_command = str;

/* See "auto" comment in init_setup */

for (i = 1; i < MAX_INIT_ARGS; i++)

argv_init[i] = NULL;

return 1;

}

__setup("rdinit=", rdinit_setup);

32行:execute_command通过“init=”指定,最常见的是在uboot的参数中设置“init=/linuxrc”。如果设置了这个参数,则上述函数直接执行到:

run_init_process(execute_command);

后面的讲都不会再执行,因为run_init_process通过调用kernel_execve替换了整个进程。

init参数解析是在:

linux-2.6.30.4\init\Main.c

static int __init init_setup(char *str)

{

unsigned int i;

execute_command = str;

/*

* In case LILO is going to boot us with default command line,

* it prepends "auto" before the whole cmdline which makes

* the shell think it should execute a script with such name.

* So we ignore all arguments entered _before_ init=... [MJ]

*/

for (i = 1; i < MAX_INIT_ARGS; i++)

argv_init[i] = NULL;

return 1;

}

__setup("init=", init_setup);

37行:如果rdinit和init都未设置,则直接调用/sbin/init。如果没找到/sbin/init,则一次回去寻找/etc/init、/bin/init、/bin/sh,如果都没找到,则会打印“No init found.  Try passing init= option to kernel”

如果uboot设置了“init=/linuxrc”(我的yaffs2文件系统的uboot启动就设置了),那么将会执行linuxrc,但是后来我发现把linuxrc删除,系统也会正常启动,没任何影响,这是为什么?

经CU论坛“arm-linux-gcc”兄弟指点和网上查阅资料,总结如下:

linuxrc其实会被当作init来使用,在busybox中busybox-1.16.0\include\Applets.h232行有:

IF_FEATURE_INITRD(APPLET_ODDNAME(linuxrc, init, _BB_DIR_ROOT, _BB_SUID_DROP, linuxrc))

意思就是如果设置了“init=/linuxrc”,其实调用的也是/sbin/init,没有设置也会调用它。

所以我们的第一个程序就是/sbin/init。

后来又做了一个测试:把linuxrc文件改写成脚本文件,文件系统将不能正确启动,会提示“Kernel panic - not syncing: No init found”。

这里应该是因为执行了linuxrc,不再去执行/sbin/init,而linuxrc又被我们改写了,不会再被当成init使用了,所以/sbin/init得不到

执行,挂载失败,No init

下面说说/sbin/init这个进程:

init进程是第一个进程,所有进程的父进程,有了它,后面才能有shell等进程。

通过top查看:

1     0 root     S     2096  3.4   0  0.0 init

可以看出它的PID为1

根据之前对busybox的分析可知,它调用的其实就是busybox里的init_main函数

源码太长,这里就不贴了,而且大部分不会涉及到,涉及到文件系统启动的最主要函数就是parse_inittab。

parse_inittab函数主要涉及到的文件就是/etc/inittab,这里先说一下/etc/inittab的格式:

busybox-1.16.0\include\usage.h 1918行说明了它的格式:

:::

这段解释是被定义在一个宏里的,#define init_notes_usage

从这个宏里我们可以获得比较详细的解释,下面说一下(基本上是从这段英文翻译过来的):

:用来指定所启动进程的controlling tty,如果是空,会被默认设置为console

:init会忽略这一项,就是当做你不存在

:所执行的程序的属性说明,这么说有点拗口,看完它的各个值的含义就明白了。

有效的取值有8种,又可以分为两种,一种是只运行一次的程序,另一种是会重复运行的程序:

运行一次的:

sysinit:就是启动运行的第一个程序,直到它所指定的执行完,才进行下一步动作

wait:在sysinit指定的程序执行完,开始执行,等到它执行完,init才能继续

once:只执行一次的进程,而且它和init是异步的,意思就是,init的只负责启动它,不需等它执行完

restart:重启时运行的程序,默认所执行的进程是init本身

ctrlaltdel:同时按下CTRL-ALT-DEL键执行的程序,常用的是reboot

shutdown:系统关机时,执行的程序

运行多次的(在运行一次的程序运行完之后才运行这里的程序):

respawn:如果这里的程序被终止了,init会自动重启它的

askfirst:和respawn功能类似,但会在启动程序之前在控制台打印“Please press Enter to activate this console”。

和respawn不同在于,respawn能够直接启动一个程序,而askfirst要等用户敲了Enter键后才能启动程序

这里把我的开发板的inittab文件贴出来当做示例理解:

# /etc/inittab

::sysinit:/etc/init.d/rcS

console::askfirst:-/bin/sh

::once:/usr/sbin/telnetd -l /bin/login

::ctrlaltdel:/sbin/reboot

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

了解了inittab的格式,下面来看一下parse_inittab的源码:

点击(此处)折叠或打开

/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,

* then parse_inittab() simply adds in some default

* actions(i.e., runs INIT_SCRIPT and then starts a pair

* of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB

* _is_ defined, but /etc/inittab is missing, this

* results in the same set of default behaviors.

*/

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

{

/* No inittab file - set up some default behavior */

/* Reboot on Ctrl-Alt-Del */

new_init_action(CTRLALTDEL, "reboot", "");

/* Umount all filesystems on halt/reboot */

new_init_action(SHUTDOWN, "umount -a -r", "");

/* Swapoff on halt/reboot */

if (ENABLE_SWAPONOFF)

new_init_action(SHUTDOWN, "swapoff -a", "");

/* Prepare to restart init when a QUIT is received */

new_init_action(RESTART, "init", "");

/* Askfirst shell on tty1-4 */

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;

}

#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]) {

if (strncmp(tty, "/dev/", 5) == 0)

tty += 5;

tty = concat_path_file("/dev/", 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

}

12行:打开/etc/inittab文件

14行:如果没有/etc/inittab文件或打开失败,则系统会用new_init_action执行一些默认的配置,这个函数下面再说

42行:逐行去读取/etc/inittab文件,读出来之后同样用new_init_action去执行

来看一下new_init_action函数:

点击(此处)折叠或打开

static void new_init_action(uint8_t action_type, const char *command, const char *cons)

{

struct init_action *a, **nextp;

/* Scenario:

* old inittab:

* ::shutdown:umount -a -r

* ::shutdown:swapoff -a

* new inittab:

* ::shutdown:swapoff -a

* ::shutdown:umount -a -r

* On reload, we must ensure entries end up in correct order.

* To achieve that, if we find a matching entry, we move it

* to the end.

*/

nextp = &init_action_list;

while ((a = *nextp) != NULL) {

/* Don't enter action if it's already in the list,

* This prevents losing running RESPAWNs.

*/

if (strcmp(a->command, command) == 0

&& strcmp(a->terminal, cons) == 0

) {

/* Remove from list */

*nextp = a->next;

/* Find the end of the list */

while (*nextp != NULL)

nextp = &(*nextp)->next;

a->next = NULL;

break;

}

nextp = &a->next;

}

if (!a)

a = xzalloc(sizeof(*a));

/* Append to the end of the list */

*nextp = a;

a->action_type = action_type;

safe_strncpy(a->command, command, sizeof(a->command));

safe_strncpy(a->terminal, cons, sizeof(a->terminal));

dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",

a->command, a->action_type, a->terminal);

}

new_init_action函数主要做了这些工作:

1、创建init_action结构,并填充。

2、把这个结构放入init_action_list链表

现对parse_inittab函数总结如下:

如果我们没有配置文件inittab存在,会执行默认配置,根据new_init_action执行的默认配置,我们可以反推出默认inittab配置文件的内容:

::CTRLALTDEL:reboot

::SHUTDOWN:umount -a -r

::RESTART:init

::ASKFIRST:-/bin/sh

tty2::ASKFIRST:-/bin/sh

tty3::ASKFIRST:-/bin/sh

tty4::ASKFIRST:-/bin/sh

::SYSINIT:/etc/init.d/rcS

如果配置文件inittab存在,则会逐行读出,然后对每一行调用new_init_action函数在init_main函数里,执行完parse_inittab(),后续会顺序调用:

run_actions(SYSINIT);

run_actions(WAIT);

run_actions(ONCE);

然后才调用:

run_actions(RESPAWN | ASKFIRST);

无论对于默认的inittab配置文件,还是对于我们自己文件系统中的inittab配置文件,SYSINIT对应的程序一般是/etc/init.d/rcS,下面我们去看看这个文件

linux inittab 时间,linux根文件系统制作之inittab启动分析相关推荐

  1. Linux内核移植和根文件系统制作(详细步骤精讲)

    第一章移植内核 1.1 Linux内核基础知识 1.1.1 Linux版本 1.1.2 什么是标准内核 1.1.3 Linux操作系统的分类 1.1.4 linux内核的选择 1.2 Linux内核启 ...

  2. s5pv210 linux内核移植,简单根文件系统制作 - S5PV210 Linux3.8.3内核移植_Linux编程_Linux公社-Linux系统门户网站...

    1.这里为什么选nfs文件系统呢? 在产品开发阶段,因为nfs根文件系统并不需要编译进内核,方便调试. 2.制作根文件系统需要用到BusyBox 解压进入busybox目录: root@linuxid ...

  3. linux不启动修改rcs文件,linux根文件系统制作之rcS文件分析

    先把我的rcS文件贴出来,然后逐行解析: ***************************************************************************** # ...

  4. linux 内核移植和根文件系统的制作【转载】

    原文地址:http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076214.html 1.1 Linux内核基础知识 在动手进行Linux内核移植之 ...

  5. 嵌入式Linux根文件系统制作

    嵌入式Linux根文件系统制作 一.根文件系统简介 根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统而言它还是内核启动时所挂载(mount)的 ...

  6. 嵌入式linux启动根文件系统,嵌入式Linux根文件系统制作和挂载

    嵌入式Linux系统由三部分组成: uboot.kernel.根文件系统, 还是这张老图 这里的根文件系统可以说是包含两个部分: 一个是根,一个是文件系统 那么什么是根呢?哈哈 其实根表示的就是第一个 ...

  7. 嵌入式Linux内核以及根文件系统制作

    内核制作 注意: 我测试的使用nandflsh中bootloader启动,sd卡bootloader启动有问题 制作嵌入式平台使用的Linux内核,方法和制作PC平台的Linux内核基本一致. 清除原 ...

  8. linux开发 | nfs挂载根文件系统失败、处理过程

    NFS挂载根文件系统失败 [ 5.552903] s5p-tvout s5p-tvout: hpd status is cable removed [ 5.563919] DBUG_PORT must ...

  9. linux内核开文件系统,新手,Linux内核无法挂载根文件系统

    新手求助,Linux内核无法挂载根文件系统 一块开发板,厂商已经提供好了uboot,kernel,ramdisk文件系统跟安卓镜像 有:uboot.bin, zImage, ramdisk-uboot ...

最新文章

  1. vue element upload 控件用form-data上传方式导入xls文件
  2. 强化学习笔记5:learningplanning, explorationexploitation
  3. 【二分答案】【哈希表】【字符串哈希】bzoj2946 [Poi2000]公共串
  4. Android Pie 引入 Keystore 新特性,安全防护再升级
  5. 一种新的图像清晰度评价函数
  6. 微信上了一个新功能,吐槽的人有点多
  7. 周鸿祎IOT发布会思考
  8. docsys安装_DocSys文件系统部署
  9. android 编译 sdl,SDL编译 - Android本地视频播放器开发_Linux编程_Linux公社-Linux系统门户网站...
  10. SmtpClient 类
  11. 信息系统综合知识二 信息化基础知识
  12. 一些神奇的小函数(一)——gotoxy篇
  13. 西门子/软件/博途/TIA PORTAL V14 SP1
  14. correspondence analysis of drug and genotype(spss)
  15. Linux 自动挂载U盘 实现热插拔
  16. oracle11g认证,ORACLE11g-OCP认证(甲骨文专家级数据库工程师)
  17. 跟着小马哥学系列之 Spring AOP(Pointcut 组件详解)
  18. 用深度学习做命名实体识别(五)-模型使用
  19. QRCode插件的使用(二维码识别与生成)
  20. c语言 ll1文法实验报告,C语言文法 LL(1)文法

热门文章

  1. 医程通 服务器维护,启用医程通,妇幼版“抢车位”有望缓解
  2. python函数画圆_python圆形_python圆形绘制_python圆形函数 - 云+社区 - 腾讯云
  3. Linux 利器- Python 脚本编程入门(一)
  4. TX2 Linux配置组播(Multicast)
  5. jQuery水平手风琴图片插件
  6. combox绑定总结
  7. 【C++】黑马程序员-C++核心编程学习笔记
  8. css图片宽高自适应盒子(宽>高就限制宽,高>宽就限制高)
  9. Linux 查看网关
  10. 【计算机专业漫谈】【计算机系统基础学习笔记】W1-计算机系统概述