Android起步流程分析(四) init进程分析

Android启动流程分析(四) init进程分析

#############################################

本文为极度寒冰原创,转载请注明出处

#############################################
前面讲到了,我们init进程执行的话,首先执行的会是init的main函数。
那我们就先来看看init的main函数做了什么事情。
首先,我们看到init最先做的事情是创建一些文件夹,并且挂载设备,这些函数都是linux的常见函数:
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);

    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);
这里,我们正好介绍一些这里涉及到的两个目录在linux系统中的作用:
/dev:dev这个目录非常的重要,这个目录里面存放了linux的所有外部设备的节点,提供了访问外部设备的接口
/proc: 用户和应用程序可以通过proc来得到系统的信息,并可以改变内核的某些参数。常用的命令比如我们前面介绍过的cat /proc/kmsg等

mount函数的作用也一起介绍一下吧:
比如这个:
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
这个的意思就是说:将devpts挂载到/dev/pts,文件系统的类型是devpts,指定文件的读写类型是0,文件系统没有特殊的参数即为NULL,

在执行了一些根文件系统的创建和挂载之后,有一个重要的函数出现在了我们的眼前。而这个,就是我们提到的cat /proc/kmsg
来看一下:
    open_devnull_stdio();
    klog_init();
    property_init();
open_devnull_stdio()是将标准输入,标准输出以及错误输出都重定向到了/dev/_null_的设备节点。
也就是说,在android启动的这个过程中,屏蔽了stdin,stdout,stderror的操作。在这个过程中,禁止读取文件向屏幕输出等操作。

klog_init就是我们说的init的输出设备为kmsg了,我们来看一下klog_init的实现:
首先,klog_init的实现位于:system/core/libcutils/klog.c
39void klog_init(void)
40{
41    static const char *name = "/dev/__kmsg__";
42
43    if (klog_fd >= 0) return; /* Already initialized */
44
45    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
46        klog_fd = open(name, O_WRONLY);
47        if (klog_fd < 0)
48                return;
49        fcntl(klog_fd, F_SETFD, FD_CLOEXEC);
50        unlink(name);
51    }
52}
我们可以看到,这边打开了一个/dev/__kmsg__的节点,
然后调用fcntl设定init的输出设备为/dev/__kmsg__

然后会去进行如下的操作:
    get_hardware_name(hardware, &revision);

    process_kernel_cmdline();

    union selinux_callback cb;
    cb.func_log = log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    selinux_initialize();
    /* These directories were necessarily created before initial policy load
     * and therefore need their security context restored to the proper value.
     * This must happen before /dev is populated by ueventd.
     */
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon_recursive("/sys");

    is_charger = !strcmp(bootmode, "charger");

    INFO("property init\n");
    property_load_boot_defaults();
这里,进行的都是linux的一些系统操作。
比如get_hardware_name,是从/proc/cpuinfo里面读取的
可以简单看一下cat /cpuinfo里面的内容:
445    hw = strstr(data, "\nHardware");
446    rev = strstr(data, "\nRevision");
而 cpuinfo的显示的为(笔者为nexus机器):
root@mako:/ # cat /proc/cpuinfo                                                
Processor	: ARMv7 Processor rev 2 (v7l)
processor	: 0
BogoMIPS	: 13.53

processor	: 1
BogoMIPS	: 13.53

processor	: 2
BogoMIPS	: 13.53

processor	: 3
BogoMIPS	: 13.53

Features	: swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 
CPU implementer	: 0x51
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0x06f
CPU revision	: 2

Hardware	: QCT APQ8064 MAKO
Revision	: 000b
Serial		: 0000000000000000
读取的Hardware即为MAKO的hardware

SELINUX这个,比较复杂。是美国国家安全局研究的增强型的linux,主要为了保护信息安全。

这里,自动忽略掉这个模块,因为并不阻碍我们的分析。

在执行完这些之后,接下来的函数映入眼帘估计大家会眼前一亮。因为我们看到了一个熟悉的名字,init.rc
    INFO("reading config file\n");
    init_parse_config_file("/init.rc");

init.rc的文件是如何被解析的呢?
我们接下来慢慢分析