Android init初始化概念介绍

init进程源于Linux,是Linux内核启动后运行的第一个进程。其他所有系统运行所需的进程都由init进程来创建。

在系统启动完成后,init进程会作为守护进程监视其他进程。

对于终结或者进入僵死状态的进程,init会强制释放该进程所占用的所有系统资源。

Android系统以Linux内核为基础,不可避免地集成了init进程的很多特点。

概述

与Linux类似,init是Android系统启动后由内核运行的第一个进程,启动过程如下图所示:

Linux系统启动时依次执行start_kernel()函数、init_post()函数和run_init_process()函数,之后开始运行init进程。Android的init进程主要提供4个功能:

  1. 分析启动init.rc脚本文件
  2. 为APP访问设备驱动生成设备节点文件
  3. 子进程的创建和终止
  4. 提供property service属性服务,进行Android系统的属性管理
static noinline int init_post(void)
{
if(execute_command){
run_init_process(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" 
              "See Linux Documentation/init.txt for guidance.");
}

init_post()函数通过if判断语句调用run_init_process()函数获取execute_command中注册的进程文件路径。

后续的run_init_process()函数分别在各个子目录下查找init文件,并启动init进程。

 

init进程

本小节参考链接:https://www.cnblogs.com/nokiaguy/archive/2013/04/14/3020774.html

增加了一点代码注释

init进程与其他进程一样,由一个main()函数作为入口。

int main(int argc, char **argv)
{
    int fd_count = 0;
    struct pollfd ufds[4];
    char *tmpdev;
    char* debuggable;
    char tmp[32];
    int property_set_fd_init = 0;
    int signal_fd_init = 0;
    int keychord_fd_init = 0;
    bool is_charger = false;

    if (!strcmp(basename(argv[0]), "ueventd"))
        return ueventd_main(argc, argv);

    if (!strcmp(basename(argv[0]), "watchdogd"))
        return watchdogd_main(argc, argv);

    /* clear the umask */
    umask(0);
    //  下面的代码开始建立各种用户空间的目录,如/dev、/proc、/sys等
    //   这些目录由init进程生成,当系统终止时被撤销
    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);

        /* 检测/dev/.booting文件是否可读写和创建*/
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));

    open_devnull_stdio();
    //  设置init的日志输出设备文件为/dev/_kmsg_
    klog_init();
    //  初始化属性
    property_init();
    //  通过读取/proc/cpuinfo获得硬件的hardware名称
    get_hardware_name(hardware, &revision);
    //  处理内核命令行
    process_kernel_cmdline();
    … …

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

    INFO("property init\n");
    if (!is_charger)
        property_load_boot_defaults();

    INFO("reading config file\n");
    //  分析/init.rc文件的内容
    init_parse_config_file("/init.rc");
    ... ...//  执行初始化文件中的动作
    action_for_each_trigger("init", action_add_queue_tail);
    //  在charger模式下略过mount文件系统的工作
    if (!is_charger) {
        action_for_each_trigger("early-fs", action_add_queue_tail);
        action_for_each_trigger("fs", action_add_queue_tail);
        action_for_each_trigger("post-fs", action_add_queue_tail);
        action_for_each_trigger("post-fs-data", action_add_queue_tail);
    }

    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(signal_init_action, "signal_init");
    queue_builtin_action(check_startup_action, "check_startup");

    if (is_charger) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("early-boot", action_add_queue_tail);
        action_for_each_trigger("boot", action_add_queue_tail);
    }

        /* run all property triggers based on current state of the properties */
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");


#if BOOTCHART
    queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
    //  进入无限循环,建立init的子进程(init是所有进程的父进程)
    for(;;) {
        int nr, i, timeout = -1;
        //  执行命令(子进程对应的命令)
        execute_one_command();
        restart_processes();

        if (!property_set_fd_init && get_property_set_fd() > 0) {
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
        }
        if (!signal_fd_init && get_signal_fd() > 0) {
            ufds[fd_count].fd = get_signal_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            signal_fd_init = 1;
        }
        if (!keychord_fd_init && get_keychord_fd() > 0) {
            ufds[fd_count].fd = get_keychord_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            keychord_fd_init = 1;
        }

        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

        if (!action_queue_empty() || cur_action)
            timeout = 0;
//  bootchart是一个性能统计工具,用于搜集硬件和系统的信息,并将其写入磁盘,以便其
//  他程序使用
#if BOOTCHART
        if (bootchart_count > 0) {
            if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
                timeout = BOOTCHART_POLLING_MS;
            if (bootchart_step() < 0 || --bootchart_count == 0) {
                bootchart_finish();
                bootchart_count = 0;
            }
        }
#endif
        //  等待下一个命令的提交
        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)
            continue;

        for (i = 0; i < fd_count; i++) {
            if (ufds[i].revents == POLLIN) {
                if (ufds[i].fd == get_property_set_fd())
                    handle_property_set_fd();
                else if (ufds[i].fd == get_keychord_fd())
                    handle_keychord();
                else if (ufds[i].fd == get_signal_fd())
                    handle_signal();
            }
        }
    }

    return 0;
}

       根目录有一个init.rc文件。该文件是只读的,即使有了root权限,可以修改该文件也没有。因为我们在根目录看到的文件只是内存文件的镜像。也就是说,android启动后,会将init.rc文件装载到内存。而修改init.rc文件的内容实际上只是修改内存中的init.rc文件的内容。一旦重启android,init.rc文件的内容又会恢复到最初的装载。想彻底修改init.rc文件内容的唯一方式是修改Android的ROM中的内核镜像(boot.img)。其实boot.img名曰内核镜像,不过该文件除了包含完整的Linux内核文件(zImage)外,还包括另外一个镜像文件(ramdisk.img)。ramdisk.img就包含了init.rc文件和init命令。所以只有修改ramdisk.img文件中的init.rc文件,并且重新打包boot.img文件,并刷机,才能彻底修改init.rc文件。如果读者有Android源代码,编译后,就会看到out目录中的相关子目录会生成一个root目录,该目录实际上就是ramdisk.img解压后的内容。会看到有init命令和init.rc文件。

 

脚本文件init.rc和init.{hardware}.rc

init.rc在系统运行过程中用于通用的环境变量设置,以及与进程相关的定义。

init.{hardware}.rc文件用于定义Android在不同硬件平台下的特定进程和环境设置等。

这两个文件的处理方式和语法是相同的。

init.rc文件可以分为两部分

  • on开头的动作列表 action list。用于创建所需目录,以及为特定文件指定权限
  • service开头的服务列表,用来记录初始化程序所需要启动的一些程序。 service用来记录init进程启动。由init进程启动的子进程——daemon进程启动
# on init的环境变量设置部分主要设置运行根文件系统命令的目录,以及程序编译时需要的库目录

on init
sysclktz 0

loglevel 3


# setup the global environment

export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
export LD_LIBRARY_PATH /vendor/lib/:/system/lib
... ...


# 在根文件系统主要挂载/system与/data两个目录
on fs
# mount mtd partitions
# mount /system rw first to give the filesystem a chance to save a checkpoint
mount yaffs2 mtd@system /system
mount yaffs2 mtd@system /system ro remount
mount yaffs2 mtd@userdata /data nosuid nodev
mount yaffs2 mtd@cache /cache nosuid nodev
on post-fs-data

# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
chown 0771 data
## Daemon processes to be run by init
##

# 第一个字符串console是服务的名称
# 第二个字符串表示服务的路径
# 第二行是服务的内容,主要包含运行权限、条件及重启等相关选项
service console /system/bin/sh
    class core
    console
    disabled
    user shell
    group log
on property:ro.debuggable=1
    start console

# adb is controlled via property triggers in init.<platform>.usb.rc
service adbd /sbin/adbd
    class core
    socket adbd stream 660 system system
    disabled
    seclable u:r:adbd:s0
# adbd on at boot in emulator
on property:ro.kernel.qemu=1
    start adbd

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm
service vold /system/bin/vold
    class core
    socket vold stream 0660 root mount
    ioprio be 2

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值