Linux启动分析——init进程与app启动(init进程源码分析)
Linux启动分析——init进程与app启动
clinux
发布于 2017-08-29
概述
本文通过简要分析init进程源码,梳理其处理流程,重点关注init进程如何启动应用程序,总结启动脚本文件的编写思路
init进程源码分析
init进程是linux内核启动的第一个进程,怎么知道的?从内核源码linux-2.6.xxx/init/main.c代码的kernel_init()
函数分析,可以发现,内核会根据uboot传入的参数来启动第一个进程,一般都是init
怎么启动的呢,调用kernel_execve()
函数完成的,猜测是从根文件系统的/sbin/init来启动的,linux的任何应用程序都是基于文件系统的,启动应用程序前提是根文件系统已经挂载好了。好,那么根文件系统又是从哪里来的呢,是由busybox这个工具配置编译生成的,所以要分析init源码,要去busybox里找init的源码
源码位置:/busybox/init/init.c,在其中查找main()
函数,发现只有init_main()
,没有main()
,可以猜测busybox是通过一些方法将init进程的入口修改为init_main()
,实际上所有busybox的命令工具都是一个到busybox程序的链接,
cd /sbin
ls -l init
lrwxrwxrwx 1 root 0 14 Nov 16 2016 init -> ../bin/busybox
可以看到,init进程其实是到busybox的链接,不用管它,知道init进程的入口是init_main()函数就行了
#if DEBUG_SEGV_HANDLER{struct sigaction sa;memset(&sa, 0, sizeof(sa));sa.sa_sigaction = handle_sigsegv;sa.sa_flags = SA_SIGINFO;sigaction(SIGSEGV, &sa, NULL);......}
#endif
......
console_init();
set_sane_term();
......
/* Make sure environs is set to something sane */
putenv((char *) "HOME=/");
putenv((char *) bb_PATH_root_path);
putenv((char *) "SHELL=/bin/sh");
putenv((char *) "USER=root"); /* needed? why? */
这一段是init进程最开始要做的事情,设置一些信号相关的东西,初始化console,然后设置环境变量,跟启动app似乎没有什么关系,不用管,继续往下看
/* Check if we are supposed to be in single user mode */
if (argv[1]&& (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1'))
) {/* ??? shouldn't we set RUNLEVEL="b" here? *//* Start a shell on console */new_init_action(RESPAWN, bb_default_login_shell, "");
} else {/* Not in single user mode - see what inittab says *//* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,* then parse_inittab() simply adds in some default* actions (i.e., INIT_SCRIPT and a pair* of "askfirst" shells) */parse_inittab();
}
这一段代码是一个if判断,注释说如果是single user mode,则走上半段代码,如果不是single user mode,则调用parse_inittab()
函数,因为内核启动init进程没有传入附加参数,所以argv[1]不存在,程序走parse_inittab()
注释还说如果没有定义CONFIG_FEATURE_USE_INITTAB
这个宏,程序会执行一些默认的action,那怎么知道这个宏定义了没有呢,猜测这个宏应该是对busybox配置时的选项,好,怎么查看busybox配置呢,和linux内核配置一样的道理,结合make menuconfig和各级config文件来看
是否定义了宏CONFIG_FEATURE_USE_INITTAB?
在busybox中执行make meunconfig,进入熟悉的配置界面
大概浏览一下,和init有关系的好像有个Init Utilities项,进去
这里面有一项“Support reading an inittab file”,这个配置项是选中的,描述中有“inittab”这个单词,和init源码中说到的parse_inittab()很相似,好,make menuconfig先放到一边,来看看配置文件,打开顶层目录的Config.in,全局搜一下"init",发现只有最下面有:
source init/Config.in
进入init文件夹,打开其中的Config.in文件,发现配置项
config FEATURE_USE_INITTABbool "Support reading an inittab file"default ydepends on INIThelpAllow init to read an inittab file when the system boot.
猜测没错,CONFIG_FEATURE_USE_INITTAB
这个宏确实定义了,回到init源码分析,进入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.*/
前面的话和之前的if判断意思差不多,如果定义了XXX这个宏,但是/etc/inittab这个文件没有,也会走默认的action,好,大概猜想一下,parse_inttab()
函数好像和要分析的app启动有点关系了,如果定义了XXX宏,就去解析/etc/inittab这个文件,执行里面的东西,如果没有定义XXX宏或者/etc/inittab文件不存在,执行一些默认的东西
好,搞清楚一件事,/etc/inittab这个文件很重要,可能需要自己来创建这个文件,往里面写东西,但是写什么内容呢?目前还不知道。那如果不走/etc/inittab这一条路呢,默认会执行的action又是什么意思?来分析一下parse_inittab()这个函数
static void parse_inittab(void)
{
#if ENABLE_FEATURE_USE_INITTABchar *token[4];parser_t *parser = config_open2("/etc/inittab", fopen_for_read);if (parser == NULL)
#endif{/* No inittab file - set up some default behavior *//* Sysinit */new_init_action(SYSINIT, INIT_SCRIPT, "");/* Askfirst shell on tty1-4 */new_init_action(ASKFIRST, bb_default_login_shell, "");
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry usersnew_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);/* 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 */new_init_action(SHUTDOWN, "swapoff -a", "");/* Restart init when a QUIT is received */new_init_action(RESTART, "init", "");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]) {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
这个文件,如果不存在,执行了很多new_init_action()
,如果存在,就走了一个while()
循环,猜测应该是解析/etc/inittab
文件的内容,根据文件的内容执行new_init_action()
。好,那么inittab文件到底写什么格式,什么东西呢,while()循环里面有一个static const char actions[]
数组看起来像是和inittab的内容有关系,里面有“sysinit”等字符串,但是还是没办法搞清楚怎么写inittab文件
inittab文件怎么写
/busybox/examples/
下面找到一个inittab
脚本的例子,打开,看到一个类似格式说明的句子:
Format for each entry: <id>:<runlevels>:<action>:<process>
猜测inittab文件里可以有多条entry,每条entry格式中有id、runlevels、action和process这四项内容,这里也出现了action,和代码里的action数组很像。文件里又说id和runlevels无关紧要,好,要搞清楚inittab怎么写,关键在于理解action和process,继续看说明
action
action包括:sysinit、respawn、askfirst、wait、once、restart、ctrlaltdel、和shutdown共八种,
process
指定要运行的程序和它的参数
然后还说了如果没有inittab文件,则运行以下内容
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
这应该就是代码中如果读不到inittab文件,则执行的一系列net_init_action
的内容
再往下看,出现的第一条示例entry
::sysinit:/etc/init.d/rcS
是不是很熟悉,linux系统嵌入式设备里通常会有/etc/init.d/rcS
这个文件,它是一个shell脚本,根据前面的格式,分析一下,id和runlevel为空,action为sysinit
,process为/etc/init.d/rcS
,所以第一件要干的事情是去执行rcS脚本,而rcS脚本里可以做自己想做的任何事情了
下一条示例是
::askfirst:-/bin/sh
注释说的是启动shell到console口,不管,继续看
tty4::respawn:/sbin/getty 38400 tty5
tty5::respawn:/sbin/getty 38400 tty6
开启getty
::restart:/sbin/init
指定init进程的重启位置
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
在重启之前要做的事情
再回到代码上,这个while()循环遍历了inittab文件的每一个entry,解析出entry的四个部分:id、runlevel、action和process,放到一个指针数组char *token[4]
中,则token[2]
和token[3]
代表action和process,程序里调用index_in_strings()
函数将token[2]
转成字符串,即“sysinit”等值,再调用net_init_action()
,分析net_init_action()
源码可以看出,其实只是把这些action和process添加到一个链表中,并没有做实际的处理,真正的处理在后续的代码中,parse_inittab()函数返回,
....../* Now run everything that needs to be run *//* First run the sysinit command */run_actions(SYSINIT);check_delayed_sigs();/* Next run anything that wants to block */run_actions(WAIT);check_delayed_sigs();/* Next run anything to be run only once */run_actions(ONCE);/* Now run the looping stuff for the rest of forever.*/while (1) {......
这里调用run_action()
运行链表中每一个entry,并且首先运行的是action为sysinit的动作
总结
到这里,大致搞清楚了init进程是怎么启动app的了,上流程图
简单来说,init进程首先分析/etc/inittab文件,当然,可以自己修改busybox源码,让它从任意文件开始分析,如果不存在inittab文件,则执行默认的action;如果inittab文件存在,则根据inittab文件中的条目执行,通常是去/etc/init.d/rcS文件中执行脚本命令,当然,修改源码,你也可以让它执行别的脚本
rcS脚本是以shell脚本语言编写,一般的套路是
- 加载驱动模块
- 配置网络,建桥、配网卡地址
- 启动app
Linux启动分析——init进程与app启动(init进程源码分析)相关推荐
- Android uevent进程源码分析
在Android Init进程源码分析中讲到init进程会依次执行被加入到待执行队列action_queue中的Action,在init.rc中我们有这么一段配置: 11 on early-init1 ...
- Android7.0 init进程源码分析
目的 看过一些blog和相关的书籍,大多数文章在介绍init进程时,参考的代码比较久远,同时不同文章行文的重点不太一样,因此决定自己试着来分析一下,并作相应的记录. 背景 当linux内核启动之后,运 ...
- linux进程源码分析,Linux内核源代码分析——口述程序猿如何意淫进程(一)
Jack:hi,淫龙,有空吗?我们来讨论一下Linux的进程吧. 我:没空.不要烦我,最近正在郁闷. Jack:郁闷啥呀? 我:最近大学城通了轻轨,房价涨得厉害,骂了隔壁. Jack:不要郁闷了,来研 ...
- nuwa创建新进程源码分析
nuwa.cpp的作用,fork 对线程进行相关冻结设置复制等.线程级别的设置等.... https://developer.mozilla.org/en-US/docs/Archive/B2G_OS ...
- kubernetes-kubelet进程源码分析(二)
kubelet关键代码分析 在上篇博文,我们分析了kubelet进程的启动流程,大致明白了kubelet的核心个哦你工作流程就是不断从Pod Source中获取与本节点相关的Pod,然后开始加工处理, ...
- Android应用程序启动Binder线程源码分析
Android的应用程序包括Java应用及本地应用,Java应用运行在davik虚拟机中,由zygote进程来创建启动,而本地服务应用在Android系统启动时,通过配置init.rc文件来由Init ...
- Android服务查询完整过程源码分析
Android服务注册完整过程源码分析中从上到下详细分析了Android系统的服务注册过程,本文同样针对AudioService服务来介绍Android服务的查询过程. 客户端进程数据发送过程 pri ...
- Android程序暂停sh,init进程 解析Android启动脚本init.rc 修改它使不启动android init.rc中启动一个sh文件...
Android启动后,系统执行的第一个进程是一个名称为init 的可执行程序.提供了以下的功能:设备管理.解析启动脚本.执行基本的功能.启动各种服务.代码的路径: system/core/init,编 ...
- Activity启动流程源码分析(基于Android N)
Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...
- Android系统默认Home应用程序(Launcher)的启动过程源码分析
在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...
最新文章
- mysql localhost无法登陆_MySQL 'root'@'localhost'无法登录
- 看不见的攻击面:查看 SQLite 数据库就中招?
- java读写锁降级_java的读写锁中锁降级的问题
- SQLServer存储过程/函数加/解密(轉)
- C# 基础知识 (二).独特的知识及用法篇
- 选择结构_扩展if-else语句
- memcached client for java api,Memcached Client Forjava API
- combinatorial_identities习题1.1分析与解答
- 文本编辑软件哪个好_过年倒计时软件哪个好 过年倒计时软件推荐
- linux 6.5端口开启关闭,linux CentOS6.5 防火墙(关闭除提供系统服务以外的端口)...
- 用photoshop制作草莓
- 19.UNIX 环境高级编程--伪终端
- Django——日志
- java 遍历json串_Java遍历Json数据
- 黑马程序员——递归与枚举
- 学生宿舍(寝室)管理系统设计与实现(JavaWeb 附 演示、源码下载地址)
- C++解决一元多次不等式和多元一次不等式
- c语言怎么编程24小时制,[C语言][PTA] 7-7 12-24小时制
- 2022年长三角地区数学建模B题:齿轮箱故障诊断
- VARIANT数据类型详解