文档章节

鸟人的Android揭秘(10)——Init进程源代码分析(一)

 鸟人部落
发布于 2016/12/29 22:33
字数 1412
阅读 63
收藏 0

      本节开始依次分析init进程源代码中main()函数内的代码。受限于篇幅,我们无法将所有源代码一一列出讲解,这里分析主要流程和思路,希望读者能够参考init进程的实际代码,一起研究学习。

      init进程分析init.rc启动脚本文件,并根据相关文件中包含的内容,执行相应的功能。另外,init进程提供属性服务,保存系统运行所需的环境变量。此外,其还负责监视子进程的运行,处理子进程的终止和重启。当应用程序访问设备驱动时,还会生成设备节点文件。接下来我们参考main()函数逐一分析代码。

      init进程的运行分成两阶段,第一阶段只完成最主要的初始化工作,如目录生成和挂载、日志初始化和设置、SELinux初始化等,之后第二阶段才完成余下的初始化过程,如属性的初始化、属性服务启动、init.rc文件分析和相关服务的启动等。如下代码所示,当第一阶段完成后,init进程调用execv[1]函数并带上“--second-stag”标志切换到第二阶段。

bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
...
if (is_first_stage) {
    ...
    char* path = argv[0];
    char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
    if (execv(path, args) == -1) {
        ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
        security_failure();
    }
}

      编译Android系统源代码时,在生成的根文件系统中,并不存在/dev、/proc、/sys这类目录,它们是系统运行时的目录,由init进程启动后,在运行过程中创建和挂载的,如下代码所示。当系统终止时,这类目录就会消失。

mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");[2]
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);[3]
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0,
 "hidepid=2,gid=" MAKE_STR(AID_READPROC));[4]
mount("sysfs", "/sys", "sysfs", 0, NULL);[5]

      init进程创建系统运行所需的目录,形成下图所示的层次目录结构。在图中,[]内表示挂载在相应目录下的文件系统。

init进程创建的目录层次结构

      下列代码用于生成log设备并设置日志输出级别,以便输出init进程的运行信息。

open_devnull_stdio();[6]
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);

      init进程通过执行前面的代码生成/dev目录,包含系统中使用的设备,而后调用open_devnull_stdio()函数,创建运行日志输出设备。open_devnull_stdio()函数会优先使用/sys/fs/selinux/null设备节点文件,如果该节点不可用,则在/dev目录下生成__null__设备节点文件,最后再将标准输入、标准输出和标准错误输出全部重定向到__null__设备中,如下图所示。

 main()-改变log信息输出设备

      由上图可见,open_devnull_stdio()函数将标准输入输出全都重定向到__null__设备中,为了查看进程输出的日志,init进程调用klog_init()[7]函数提供输出日志信息的设备,如下面的代码所示。

void klog_init(void) {
    if (klog_fd >= 0) return; /* Already initialized */
    klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
    if (klog_fd >= 0) {
        return;
    }
    static const char* name = "/dev/__kmsg__";
    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
        klog_fd = open(name, O_WRONLY | O_CLOEXEC);
        unlink(name);
    }
}

 

      init进程通过调用klog_init()函数初始化日志输出设备节点。klog_init()函数优先尝试打开“/dev/kmsg”节点文件,如果尝试失败,则生成“/dev/__kmsg__”设备节点文件。在底层该设备调用内核信息输出函数printk(),init进程最终是通过该函数来输出日志信息。以下代码所示是可用于输出信息的宏定义。

 

#define ERROR(x...)   init_klog_write(KLOG_ERROR_LEVEL, x)
#define WARNING(x...) init_klog_write(KLOG_WARNING_LEVEL, x)
#define NOTICE(x...)  init_klog_write(KLOG_NOTICE_LEVEL, x)
#define INFO(x...)    init_klog_write(KLOG_INFO_LEVEL, x)
#define DEBUG(x...)   init_klog_write(KLOG_DEBUG_LEVEL, x)
#define VERBOSE(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)

      这一小节我们先讲解完init进程对目录生成和挂载、日志初始化和设置,接下来的一小节我们将继续讲解SELinux的初始化过程。

 

 

 

[1] execv会停止执行当前的进程,并且以progname应用进程替换被停止执行的进程,进程ID保持不变。

[2] tmpfs是一种虚拟内存的文件系统,典型的tmpfs文件系统完全驻留在RAM中,读写速度远快于内存或硬盘系统。/dev目录保存着硬件设备访问所需的设备驱动程序。在Android中,将相关目录用作tmpfs,可以大幅提升设备访问的速度。

[3] devpts是一种虚拟终端文件系统,用来支持外部网络链接虚拟终端。

[4] proc是一种虚拟文件系统,只存在于内存中,而不占用外存空间。借助此文件系统,应用程序可以与内核内部数据结构进行交互。

[5] sysfs是一种特殊的文件系统,在Linux Kernel 2.6中引入,用于将系统中的设备组织成层次结构,统一proc、devfs、devpts这些文件系统,并将内核数据结构信息输出到用户空间。

[6] open_devnull_stdio()函数在system/core/init/util.cpp中定义。

[7] klog_init()函数在system/core/libcutils/klog.cpp中定义。

 

© 著作权归作者所有

粉丝 1
博文 14
码字总数 16642
作品 0
海淀
私信 提问
鸟人的Android揭秘(2)——通过启动过程分析 Android Framework

Android源代码数量极其庞大,以最新的 Android 7.1 为例,代码数量超过 50G。若想理解和掌握如此庞大的 Android 系统,将需要耗费大量的时间和精力,付出极大的努力。关键是到目前为止也没有...

鸟人部落
2016/12/12
30
0
鸟人的Android揭秘(9)——Init进程运行过程

众所周知,Linux中所有进程都是由init进程创建并运行起来的。首先Linux加载内核启动,然后在用户空间中启动init进程,之后init进程再依次启动系统运行的其它进程。在系统启动完成后,init进程...

鸟人部落
2016/12/25
32
0
android手机打电话代码分析

Linux手机打电话代码分析(Android) Android 2009-03-16 17:02 阅读705 评论0 字号: 大大 中中 小小 Android源代码分析之电话功能 RIL 根据:《Android 源码分析-打电话和发短信》一文(时...

迷糊
2009/10/10
7.8K
1
鸟人的Android揭秘(4)——Android 源代码编译

前文主要讲解了 Android 编译环境的搭建,接下来我们正式开始 Android 源代码的编译,主要涉及两部分内容:源代码的下载和源代码编译。 一、Android源代码下载 1) 配置 Git 信息 将自己的姓名...

鸟人部落
2016/12/15
10
0
[RK3288][Android6.0] 调试笔记 --- 增加操作系统开机时间

Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 背景: 由于系统有模块需要和外部硬件做同步,因此要延长开机时间。 调试思路: 由于系统只显示了开机动画,没有开机Logo。因此一开始的思...

kris_fei
2018/04/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

一起来学Java8(三)——方法引用

在一起来学Java8(一)——函数式编程中有一个简单的函数式编程的例子: import java.util.function.Consumer;class Person { public static void sayHello(String name) { S...

猿敲月下码
12分钟前
6
0
读书笔记:深入理解ES6(十一)

第十一章 Promise与异步编程   Promise可以实现其他语言中类似Future和Deferred一样的功能,是另一种异步编程的选择,它既可以像事件和回调函数一样指定稍后执行的代码,也可以明确指示代码...

张森ZS
36分钟前
11
0
面试官,Java8 JVM内存结构变了,永久代到元空间

在文章《JVM之内存结构详解》中我们描述了Java7以前的JVM内存结构,但在Java8和以后版本中JVM的内存结构慢慢发生了变化。作为面试官如果你还不知道,那么面试过程中是不是有些露怯?作为面试...

程序新视界
44分钟前
26
0
Elasticsearch 实战(一) - 简介

官腔 Elasticsearch,分布式,高性能,高可用,可伸缩的搜索和分析系统 基本等于没说,咱们慢慢看 1 概述 百度:我们比如说想找寻任何的信息的时候,就会上百度去搜索一下,比如说找一部自己喜...

JavaEdge
49分钟前
18
0
【jQuery基础学习】11 jQuery性能简单优化

本文转载于:专业的前端网站➦【jQuery基础学习】11 jQuery性能简单优化 关于性能优化 合适的选择器 $("#id")会直接调用底层方法,所以这是最快的。如果这样不能直接找到,也可以用find方法继...

前端老手
57分钟前
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部