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

来源:互联网 发布:如何查看服务器域名 编辑:程序博客网 时间:2024/05/18 01:29

      本节开始依次分析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进程创建系统运行所需的目录,形成下图所示的层次目录结构。在图中,[]内表示挂载在相应目录下的文件系统。

      下列代码用于生成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__设备中,如下图所示。

 

      由上图可见,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中定义。

0 0
原创粉丝点击