android init进程分析 基本流程

(懒人最近想起我还有csdn好久没打理了,这个android init躺在我的草稿箱中快5年了,稍微改改发出来吧)

android设备上电,引导程序引导进入boot(通常是uboot),加载initramfs、kernel镜像,启动kernel后,进入用户态程序。第一个用户空间程序是init, PID固定是1.在android系统上,init的代码位于/system/core/init下,基本功能有:

  • 管理设备
  • 解析并处理启动脚本init.rc
  • 实时维护这个init.rc中的服务

init进程的系统初始化和服务流程,可以简单的看下init.c中的main函数。这里简要分析一下这个函数的主要作用,细节不展开或后继再展开。

简要说下main函数的各项操作吧:

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

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

以上这段,是main函数入口的第一句,这是判断是跑那个程序。

/system/core/init 这里面的一份代码,编出的二进制可执行程序init,实际是分为三个程序运行的,指示大家共享一份代码而已。三份分别是:

/init: 实际init可执行程序

/sbin/ueventd:ueventd daemon程序,是init的软链接

/sbin/watchdogd: watchdogd daemon程序,也是init的软链接。

我们都知道,main函数的参数argv的第一个,argv[0]为自身运行目录路径和程序名,这里就根据这个条件来判断代码走的路径。为何这样做?其实完全可以再另外写一个ueventd或watchdogd的一套程序,定义main函数啊。其实这里原因也是很简单,他们共享了太多东西,直接写到一起多简单!就像busybox或toolbox集成那么多命令工具一样的道理。

    /* clear the umask */
    umask(0);

清理umask,这个主要是文件权限的问题,设了umask,可以全局mask掉一些文件权限。

        /* Get the basic filesystem setup we need put
         * together in the initramdisk on / and then we'll
         * let the rc file figure out the rest.
         */
    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,proc, sys等文件系统的加载。

        /* indicate that booting is in progress to background fw loaders, etc */
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));

创建一个/dev/.booting文件。 /dev是内存文件系统,不会保存的,每次开机都要重新创建。这个是指示,目前在booting过程中,具体干什么用的,介绍ueventd的时候就清楚了。就是加载设备的fimware用的。

    open_devnull_stdio();
    klog_init();
    property_init();

这三句,分别是将 stdin,stdout和stderr先初始化到/dev/__null__,这样用printf或其他打印的,都输出不了,也不会引起其它异常(这个阶段,其实不能用,会出错的)。

注意这个/dev/__null__是临时起的名字,创建node后删了,不影响系统真正的/dev/null,这里只需要fd即可,有了fd后,文件名就无用了,有了还会有干扰。

klog_init,是将init进程中的log,打印到内核printk的log buffer中的方法。这对调试init很有帮助,毕竟此时没有shell,通用的log输出方法,如printf等还不能工作,借助底层已有的内核调试功能当然是最好的了。

property的初始化也是在这里,property读取可以在各个用户进程中做,但设置的入口必须是到init进程中来。在4.4上,property这块修改了好多,现在是通过字典树的方式存储,可以支持更多的property属性。

 get_hardware_name(hardware, &revision);

从 /proc/cpuinfo中读到hardware信息,设置到ro.hardware属性中,便于后面解析 init.${ro.hardware}.rc使用

    process_kernel_cmdline();

kernel的command参数,解析后也放到property中,供以后的子进程或其他服务等使用。

    union selinux_callback cb;
    cb.func_log = klog_write;
    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");

selinux的初始化和检查,没仔细研究selinux,则个检查过程有时候还挺耗费时间的。

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

这个是加载并设置properties。这些是预置的property,通常是/system/build.prop和default.prop文件中预置的那些property。

init_parse_config_file("/init.rc");

这是开始解析init.rc文件了。细节和init.rc的格式、写法不说了,网上到处都是。主要说一下常见问题:

1. 下载代码的服务器,umask有时候可能会有影响,init.rc文件other和group用户是不能有写权限的,编译的PC的umask最好设置成0022。

2. init.rc文件严格来说只是配置文件,不算脚本,循环、条件判断等等都不支持的,不要想这里能干太多事情。

    action_for_each_trigger("early-init", action_add_queue_tail);

    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");

    /* execute all the boot actions to get us started */
    action_for_each_trigger("init", action_add_queue_tail);
    ...

这是开始处理init.rc中parser好的各个命令了。

action_for_each_trigger是对此类trigger所在呃所有命令,都加入到actions的list中去。对实际代码或项目上要全局的看一下,所有的*.rc的同一个trigger都一起处理的。

queue_builtin_action这是内建的actions,也是将actions动作加入到actions list中。

这里需要注意的是,各个trigger的加载顺序,先加入的先执行,后加入的后执行,要特别注意,尤其是要修改init.rc文件的时候,不了解这个容易因为前后依赖关系造成问题。

都准备好了的话,就到了服务处理阶段了,这是一个死循环,主要工作就是:

1. 将init.rc及内建的actions命令,一条一条执行

2. 负责对service的管理。

3. 对signal及进程退出的处理

4. 响应property设置的请求(设置都在init中统一设置,读取进程可以自己读共享内存)

    for(;;) {
        int nr, i, timeout = -1;

        execute_one_command();
        restart_processes();
	...

有时候需要优化开机的话,可以测量一下execute_on_command中的命令执行时间,把较长的(比如大于50ms)的打印出来,再想办法优化一下。

每次循环,执行一条命令,检查是否有需要重启的服务..

        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();
            }
        }

多路polling,当polling到东西,就执行相应的动作。

timeout时间到,执行下轮循环。init.rc中的command没处理完的话,timeout是0,这样之前的actions list可以一顺下来都执行掉。

注意,init.rc中定义的服务,是在class_start这个command中做的。



版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-04 04:09:37

android init进程分析 基本流程的相关文章

android init进程分析 init脚本解析和处理

(懒人近期想起我还有csdn好久没打理了.这个android init躺在我的草稿箱中快5年了.略微改改发出来吧) RC文件格式 rc文件是linux中常见的启动载入阶段运行的文件.rc是run commands的缩写.基本上能够理解为在启动阶段运行的一些列命令.android init进程启动时,也会运行此启动脚本文件,init.rc.init.rc的写法稍有点复杂,具体可參考 /system/core/init下的readme文件.脚本基本组成是由四类,为: commands: 命令 act

Android Init进程分析番外篇:9.0的init进程

1 前言 在之前的几篇文章中,我们基于Android 4.4源码分析了init进程的启动及事件处理逻辑.目前Android版本已然更新到Android 10了,很多代码及服务架构都做了大范围的调整重构,这其中也包括init相关的代码. 但是,万变不离其宗,很多核心思想和处理逻辑Android还是都保留了下来,只是在写作上做了更好的功能封装和逻辑分割. 接下来笔者就非常简略的说一下Android 9.0 的init进程. 2 Android 9.0 init 分析 对于9.0的init笔者不想做过

android init进程分析

android的init进程用来启动zygote进程,用来启动android世界.init进程的源码在顶层目录的/system/core/init使用 find -name Android.mk -exec grep -l "init" {} \;来查找源码,接下来的android服务程序也是使用这个指令来查找源码. /system/core/init/init.c 整个init进程的入口函数669 int main(int argc, char **argv) init_parse_

android init进程分析 ueventd

转自:http://blog.csdn.net/freshui/article/details/2132299 (懒人最近想起我还有csdn好久没打理了,这个Android init躺在我的草稿箱中快5年了,稍微改改发出来吧) ueventd主要是负责设备节点的创建.权限设定等一些列工作.服务通过使用uevent,监控驱动发送的消息,做进一步处理.ueventd实际和init是同一个binary,只是走了不同分支,可参看前一部分. ueventd的整体代码比较简单,主要是三部分: 解析ueven

Android -- Init进程对信号的处理流程

Android -- Init进程对信号的处理流程 在Android中,当一个进程退出(exit())时,会向它的父进程发送一个SIGCHLD信号.父进程收到该信号后,会释放分配给该子进程的系统资源:并且父进程需要调用wait()或waitpid()等待子进程结束.如果父进程没有做这种处理,且父进程初始化时也没有调用signal(SIGCHLD, SIG_IGN)来显示忽略对SIGCHLD的处理,这时子进程将一直保持当前的退出状态,不会完全退出.这样的子进程不能被调度,所做的只是在进程列表中占据

Android init源代码分析(2)init.rc解析

本文描述init.rc脚本解析以及执行过程,读完本章后,读者应能 (1) 了解init.rc解析过程 (2) 定制init.rc init.rc介绍 init.rc是一个文本文件,可认为它是Android系统启动脚本.init.rc文件中定义了环境变量配置.系统进程启动,分区挂载,属性配置等诸多内容.init.rc具有特殊的语法.init源码目录下的readme.txt中详细的描述了init启动脚本的语法规则,是试图定制init.rc的开发者的必读资料. Android启动脚本包括一组文件,包括

Android init源代码分析(1)概要分析

功能概述 init进程是Android内核启动的第一个进程,其进程号(pid)为1,是Android系统所有进程的祖先,因此它肩负着系统启动的重要责任.Android的init源代码位于system/core/init/目录下,伴随Android系统多个版本的迭代,init源代码也几经重构. 目前Android4.4源代码中,init目录编译后生成如下Android系统的三个文件,分别是 /init /sbin/ueventd-->/init /sbin/watchdogd-->/init 其

Android init进程——源码分析

概述 Android本质上是一个基于Linux内核的开源操作系统,与我现在用的Ubuntu系统类似,但是所有的Android设备都是运行在ARM处理器(ARM源自进阶精简指令集机器,源自ARM架构)上,而像Ubuntu操作系统是x86(x86是一系列的基于intel 8086 CPU计算机微处理器指令集架构)系统.不过既然Android也是基于Linux内核的系统,那么基本的启动过程也应该符合Linux的规则.下图基本描述了当你按下电源开关后Android设备的执行步骤: 一个完整的Linux系

Android之init进程分析(1)

本文介绍init进程中的action触发方式 一,什么是action 在android中,使用action来管理并执行命令.action是一个数据结构,里面包含了命令集合command,action的名字等.Android系统通过action来执行一组命令. struct action { /* node in list of all actions */ struct listnode alist; /* node in the queue of pending actions */ stru