• Android研究Android系统初始化程序init和初始化配置文件init.rc分析[zz]


    上篇中讲到linux内核启动与android启动的衔接是linux内核根据约定,在启动的最后执行android的init进程。init进程主要工作是首先是解析init.rc等配置文件,之后充当property service。本文收集两篇文章,能够很好的理解init的逻辑和init.rc配置文件的语法和使用方法。

    1. init程序逻辑

    文章引用地址:一篇pdf文档,来自百度文库

    Android 源码分析 --
    (一) Android 启动过程
    royalxw@gmail.com
    1. 源码文件路径:
    platform/system/core/init/init.c
    0) 这个代码文件主要用于实现 Android 系统中 init 进程 (init 进程为 Android 系统中用
    户空间启动的第一个进程,其作用相当于 Linux 系统中的 init 进程)
    NOTE: 如果调用此文件生成的可执行文件的第一个参数为"ueventd",
    那么此文件
    将实现 Android 系统中的 "ueventd" 进程。
    1) 在进行编译后,此文件生成的可执行程序名称为"init",同时会生成一个指向此文
    件的软链接: "ueventd"
    int main(int argc, char **argv)
    {
    2) 基于 C 语言的风格,在函数入口处声明一些后续会使用的变量。
    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;
    3) 如果执行此文件生成的可执行程序的方式类似于: "ueventd xxx"。也即是基于执行
    此 文 件 对 应 的 软 链 接 : /sbin/ueventd 时 会 调 用 "ueventd_main" 函 数 , 进 而 生
    成"ueventd" 进程。
    4) Android 系统中的 ueventd 进程用于实现用户态进程与内核进行数据通信。
    5) 在 Android 系统的 init.rc 文件: platform/system/core/rootdir/init.rc 中通过如下方式
    来启动 ueventd 进程:
    on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_adj -16
    start ueventd
    if (!strcmp(basename(argv[0]), "ueventd"))

    return ueventd_main(argc, argv);
    6)
    生成 Android 系统中的一些基本的系统目录并挂载对应的文件系统。
    /* clear the umask */
    umask(0);
    /* 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);
    /* indicate that booting is in progress to background fw loaders, etc
    */
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
    /* We must have some place other than / to create the
    * device nodes for kmsg and null, otherwise we won't
    * be able to remount / read-only later on.
    * Now that tmpfs is mounted on /dev, we can actually
    * talk to the outside world.
    */
    7) 生成 "/dev/__null__" 虚拟设备(类似于 Linux 系统中的 /dev/null 设备)并将
    stdin/stdout/stderr 三个文件重定向到 "/dev/__null__"
    open_devnull_stdio();
    8)
    生成 " /dev/__kmsg__" 虚拟设备用于记录 log。
    Klog_init 实现文件: system/core//libcutils/klog.c
    klog_init();
    INFO("reading config file\n");
    9)
    解析 init.rc (platform/system/core/rootdir/init.rc)。

    init_parse_config_file("/init.rc");
    /* pull the kernel commandline and ramdisk properties file in */
    10) 从 "/proc/cmdline" 中读取内核命令行参数,
    对应函数实现路径: platform/system/core/init/util.c
    import_kernel_cmdline(0, import_kernel_nv);
    11) 在第 10 步读取完 /proc/cmdline 中的参数后,修改此文件的权限,禁止非授权
    用户操作此文件。
    /* don't expose the raw commandline to nonpriv processes */
    chmod("/proc/cmdline", 0440);
    12) 从 "/proc/cpuinfo" 中读取系统的 CPU 硬件信息。
    对应函数实现路径: platform/system/core/init/util.c
    get_hardware_name(hardware, &revision);
    13) 基于第 12 步中读取的硬件信息来解析特定于硬件的配置信息。
    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
    init_parse_config_file(tmp);
    14) 基 于 "early-init","property_init","keychord_init","console_init" 标 识 , 使 用 "
    queue_builtin_action"来过滤上述操作中解析的 init.rc 文件中的 action,
    并将符合
    条 件 的 action 添 加 到 对 应 的
    action_queue 中 , 然 后 调 用 "
    action_for_each_trigger"来运行这些 actions(实际上是 action 在 list 中的分类移
    动操作)。
    对应函数实现路径: platform/system/core/init/init_parser.c
    基于下面的运行逻辑可以看出,运行"init.rc"中的 action 的顺序为:
    "early-init" -> "init" -> "early-boot" -> "boot"
    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(property_init_action, "property_init");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");
    queue_builtin_action(set_init_properties_action,
    "set_init_properties");
    /* execute all the boot actions to get us started */
    action_for_each_trigger("init", action_add_queue_tail);

    /* skip mounting filesystems in charger mode */
    if (strcmp(bootmode, "charger") != 0)
    {
    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 (!strcmp(bootmode, "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_propety_triggers");
    #if BOOTCHART
    queue_builtin_action(bootchart_init_action, "bootchart_init");
    #endif
    15) "init" 进程开始进行"循环事件处理"逻辑。
    for (;;)
    {
    int nr, i, timeout = -1;
    16) 运行第 14 步操作中所分类整理的 action_queue 中对应的 action。
    execute_one_command();
    17) 查看当前的服务进程状态,如果有服务进程退出,重启对应服务进程。
    restart_processes();

    18) 基于 property_service 的事件句柄填充 poll event 结构体,用于后续 poll 操作。
    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;
    }
    19) 处理当前 init 进程上接收到的 signal,主要是处理 SIGCHLD。
    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;
    }
    20) 基于 keychord service 的事件句柄填充 poll event 结构体,用于后续 poll 操作。
    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;
    }
    21) 如果有所监控的子进程退出,此时 init 进程需要负责重新启动这些退出的服务进
    程,因此下面调用 poll 时的 timeout 设置为 0,这样就不会因为 poll 在无事件激发时而
    阻塞导致当前的 init 进程对"重启服务进程"处理的的延迟,从而可以尽快恢复退出的
    服务进程。
    if (process_needs_restart)
    {
    timeout = (process_needs_restart - gettime()) * 1000;
    if (timeout < 0)
    timeout = 0;
    }
    22) 如果当前的 action_queue 中有需要处理的 action, 么下面调用 poll 时的 timeout

    设置为 0,这样就不会因为 poll 在无事件激发时而阻塞导致当前的 init 进程对 action
    处理的的延迟,从而提高 init 进程对 action 处理的实时性。

    if (!action_queue_empty() || cur_action)
    timeout = 0;
    23) 关于" BOOTCHART"参数的解释:
    /*
    * 如果在编译选项中添加了 BOOTCHART 参数,那么意味着在系统的启动
    * 过程中需要生成 bootchart(http://www.bootchart.org/),用于后续
    * Android 启动过程中的性能分析并生成系统启动过程的可视图表。
    * makefile 中的编译选项如下:
    ifeq ($(strip $(INIT_BOOTCHART)),true)
    LOCAL_SRC_FILES += bootchart.c
    LOCAL_CFLAGS
    endif
    += -DBOOTCHART=1
    bootchart 的实现文件: platform/system/core/init/bootchart.c
    *
    #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
    24) 类似于网络服务器开发中常见的基于"poll"机制来检测所关注的句柄上是否有指定
    的事件激发。
    nr = poll(ufds, fd_count, timeout);
    if (nr <= 0)
    continue;
    for (i = 0; i < fd_count; i++)
    {
    25) 如果当前所关注的事件句柄上有事件发生,进行对应的事件处理。
    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;
    }

    2. init.rc类配置文件解析

    文章引用地址:一篇pdf文档,来自百度文库

    Android 的初始化语言脚本 init.rc 解析
    Android init.rc (Android init language)
    Android 初始化语言由四大类声明组成:行为类(Actions),命令类(Commands),服务类
    (Services),选项类(Options)。
    1)   初始化语言以行为单位,由以空格间隔的语言符号组成。C 风格的反斜杠转义符可以
    用来插入空白到语言符号。双引号也可以用来防止文本被空格分成多个语言符号。当
    反斜杠在行末时,作为折行符。
    2)   以#开始(前面允许有空格)的行为注释行。
    3)   Actions 和 Services 隐含声明一个新的段落。所有该段落下 Commands 或 Options 的
    声明属于该段落。第一段落前的 Commands 或 Options 被忽略。
    4)   Actions 和 Services 拥有独一无二的命名。在它们之后声明相同命名的类将被当作
    错误并忽略。
     
    行为类(Actions)
    Actions 是一系列命令的命名。
    Actions 拥有一个触发器(trigger)用来决定 action 何时执行。当一个 action 在符合触发
    条件被执行时,如果它还没被加入到待执行队列中的话,则加入到队列最后。
    队列中的 action 依次执行,action 中的命令也依次执行。Init 在执行命令的中间处理其它
    活动(设备创建/销毁,property 设置,进程重启)。
    Actions 表现形式为:
    on <trigger>
       <command>
       <command>
       <command>
     
    服务类(Services)
    Services 是由 init 启动,在它们退出时重启(可选)。
    Service 表现形式为:
    service <name> <pathname> [ <argument> ]*
       <option>
       <option>
       ...
       
    选项类(Options)
    Options 是 Services 的修饰,它们影响 init 何时、如何运行 service.
    PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
    critical
         这是一个设备关键服务(device-critical service) .如果它在 4 分钟内退出超过 4
    次,设备将重启并进入恢复模式。
     
    disabled
         这个服务的级别将不会自动启动,它必须被依照服务名指定启动才可以启动。
     
    setenv <name> <value>
         设置已启动的进程的环境变量<name>的值<value>
     
    socket <name> <type> <perm> [ <user> [ <group> ] ]
         创建一个名为/dev/socket/<name>的 unix domin socket,并传送它的 fd 到已启动的
    进程。<type>必须为"dgram"或"stream".用户和组默认为 0.
     
    user <username>
         在执行服务前改变用户名。当前默认为 root.如果你的进程需要 linux 能力,你不能
    使用这个命令。你必须在还是 root 时请求能力,并下降到你需要的 uid.
     
    group <groupname> [ <groupname> ]*
         在执行服务前改变组。在第一个组后的组将设为进程附加组(通过 setgroups()).当前
    默认为 root.
     
    oneshot
         在服务退出后不重启。
     
    class <name>
         为 service 指定一个类别名。同样类名的所有的服务可以一起启动或停止。如果没有
    指定类别的服务默认为"default"类。
     
    onrestart
           当服务重启时执行一个命令。
     
    PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
    Triggers
         Triggers(触发器)是一个字符串,可以用来匹配某种类型的事件并执行一个 action。
     
    boot
         这是当 init 开始后执行的第一个触发器(当/init.conf 被加载)
     
    <name>=<value>
         当 property <name>被设为指定的值<value>时触发。
     
    device-added-<path>
    device-removed-<path>
         当设备节点被添加或移除时触发。
     
    service-exited-<name>
         当指定的服务存在时触发
     
     
    命令类(Commands)
    exec <path> [ <argument> ]*
         Fork 并执行一个程序(<path>).这将被 block 直到程序执行完毕。最好避免执行例如
    内建命令以外的程序,它可能会导致 init 被阻塞不动。
    export <name> <value>
         设定全局环境变量<name>的值<value>,当这个命令执行后所有的进程都可以取得。
     
    ifup <interface>
         使网络接口<interface>联机。
     
    import <filename>
         解析一个 init 配置文件,扩展当前配置文件。
     
    hostname <name>
         设置主机名
     
    chmod <octal-mode> <path>
         改变文件访问权限
     
    chown <owner> <group> <path>
         改变文件所属和组
     
    class_start <serviceclass>
         当指定类别的服务没有运行,启动该类别所有的服务。
     
    class_stop <serviceclass>
         当指定类别的服务正在运行,停止该类别所有的服务。
     
    domainname <name>
         设置域名。
     
    insmod <path>
         加载该路径<path>的模块
     
    mkdir <path> [mode] [owner] [group]
         在<path>创建一个目录,可选选项:mod,owner,group.如果没有指定,目录以 755 权限,
    owner 为 root,group 为 root 创建.
     
    mount <type> <device> <dir> [ <mountoption> ]*
         尝试 mount <device>到目录<dir>. <device>可以用 mtd@name格式以命名指定一个 mtd
    块设备。<mountoption>包含"ro","rw","remount","noatime".
     
    setkey
    PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
         暂时没有
     
    setprop <name> <value>
         设置系统 property <name>的值<value>.
     
    setrlimit <resource> <cur> <max>
         设置 resource 的 rlimit.
     
    start <service>
         启动一个没有运行的服务。
     
    stop <service>
         停止一个正在运行的服务。
     
    symlink <target> <path>
         创建一个<path>的符号链接到<target>
     
    sysclktz <mins_west_of_gmt>
         设置系统时区(GMT 为 0)
     
    trigger <event>
         触发一个事件。用于调用其它 action。
     
    write <path> <string> [ <string> ]*
         打开<path>的文件并写入一个或多个字符串。
     
    Properties
    Init 会更新一些系统 property 以提供查看它正在干嘛。
    init.action
         当前正在执行的 action,如果没有则为""
    PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
     
    init.command
         被执行的命令,如果没有则为""
     
    init.svc.<name>
         命名为<name>的服务的状态("stopped", "running", "restarting")
     
    init.rc 示例:
     
    # not complete -- just providing some examples of usage
    #
    on boot
       export PATH /sbin:/system/sbin:/system/bin
       export LD_LIBRARY_PATH /system/lib
     
       mkdir /dev
       mkdir /proc
       mkdir /sys
     
       mount tmpfs tmpfs /dev
       mkdir /dev/pts
       mkdir /dev/socket
       mount devpts devpts /dev/pts
       mount proc proc /proc
       mount sysfs sysfs /sys
     
       write /proc/cpu/alignment 4
     
       ifup lo
     
       hostname localhost
       domainname localhost
     
       mount yaffs2 mtd@system /system
     
       mount yaffs2 mtd@userdata /data
     
       import /system/etc/init.conf
     
       class_start default
     
    service adbd /sbin/adbd
       user adb
       group adb
     
    service usbd /system/bin/usbd –r
       user usbd
       group usbd
       socket usbd 666
     
    service zygote /system/bin/app_process -Xzygote /system/bin –zygote
       socket zygote 666
     
    service runtime /system/bin/runtime
       user system
       group system
     
    on device-added-/dev/compass
       start akmd
     
    on device-removed-/dev/compass
       stop akmd
     
    service akmd /sbin/akmd
       disabled
       user akmd
       group akmd
     
    调试
    默认情况下,init 执行的程序输出的信息和错误到/dev/null.为了 debug,你可以通过
    Android 程序 logwrapper 执行你的程序。这将复位向输出/错误输出到 Android logging 系
    统(通过 logcat 访问)。
    例如
    service akmd /system/bin/logwrapper /sbin/akmd

    本文完...

  • 相关阅读:
    UML期末复习题——2.7:UML Sequence Diagram
    UML期末复习题——2.6:Package Diagram
    UML期末复习题——2.5:System Sequence Diagram & Post-condition
    UML期末复习题——2.4:Domain Model
    UML期末复习题——2.3:UML State Diagram
    UML期末复习题——2.2:UML Activity Diagram.
    UML期末复习题——2.1:Use Case Diagram
    UML期末复习题
    《C++之那些年踩过的坑(附录一)》
    《C++之那些年踩过的坑(三)》
  • 原文地址:https://www.cnblogs.com/linucos/p/2513046.html
Copyright © 2020-2023  润新知