SlideShare uma empresa Scribd logo
1 de 7
Baixar para ler offline
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 filen");




     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     += -DBOOTCHART=1
                      endif
                 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;
}

Mais conteúdo relacionado

Mais procurados

ch13-pv1-system-calls
ch13-pv1-system-callsch13-pv1-system-calls
ch13-pv1-system-calls
yushiang fu
 
由一个简单的程序谈起――之五(精华)
由一个简单的程序谈起――之五(精华)由一个简单的程序谈起――之五(精华)
由一个简单的程序谈起――之五(精华)
yiditushe
 
Erlang jiacheng
Erlang jiachengErlang jiacheng
Erlang jiacheng
Air-Smile
 

Mais procurados (20)

Java Thread
Java ThreadJava Thread
Java Thread
 
JAVA内存泄漏及诊断
JAVA内存泄漏及诊断JAVA内存泄漏及诊断
JAVA内存泄漏及诊断
 
ch7-pv1-modules
ch7-pv1-modulesch7-pv1-modules
ch7-pv1-modules
 
ch13-pv1-system-calls
ch13-pv1-system-callsch13-pv1-system-calls
ch13-pv1-system-calls
 
MySQL aio
MySQL aioMySQL aio
MySQL aio
 
Handler socket测试报告 - 20110422
Handler socket测试报告 - 20110422Handler socket测试报告 - 20110422
Handler socket测试报告 - 20110422
 
由一个简单的程序谈起――之五(精华)
由一个简单的程序谈起――之五(精华)由一个简单的程序谈起――之五(精华)
由一个简单的程序谈起――之五(精华)
 
180518 ntut js and node
180518 ntut js and node180518 ntut js and node
180518 ntut js and node
 
Showinnodbstatus公开
Showinnodbstatus公开Showinnodbstatus公开
Showinnodbstatus公开
 
Python 于 webgame 的应用
Python 于 webgame 的应用Python 于 webgame 的应用
Python 于 webgame 的应用
 
揭秘家用路由器Ch10 sharing
揭秘家用路由器Ch10 sharing揭秘家用路由器Ch10 sharing
揭秘家用路由器Ch10 sharing
 
從 Singleton 談 constructor
從 Singleton 談 constructor從 Singleton 談 constructor
從 Singleton 談 constructor
 
论 Python 与设计模式。
论 Python 与设计模式。论 Python 与设计模式。
论 Python 与设计模式。
 
Erlang jiacheng
Erlang jiachengErlang jiacheng
Erlang jiacheng
 
110824 knoss-windows系统机制浅析
110824 knoss-windows系统机制浅析110824 knoss-windows系统机制浅析
110824 knoss-windows系统机制浅析
 
nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言nodeMCU IOT教學02 - Lua語言
nodeMCU IOT教學02 - Lua語言
 
現代 IT 人一定要知道的 Ansible 自動化組態技巧 Ⅱ - Roles & Windows
現代 IT 人一定要知道的 Ansible 自動化組態技巧 Ⅱ - Roles & Windows現代 IT 人一定要知道的 Ansible 自動化組態技巧 Ⅱ - Roles & Windows
現代 IT 人一定要知道的 Ansible 自動化組態技巧 Ⅱ - Roles & Windows
 
页游开发中的 Python 组件与模式
页游开发中的 Python 组件与模式页游开发中的 Python 组件与模式
页游开发中的 Python 组件与模式
 
181201_CoAP_coding365
181201_CoAP_coding365181201_CoAP_coding365
181201_CoAP_coding365
 
Mongo db架构之优先方案
Mongo db架构之优先方案Mongo db架构之优先方案
Mongo db架构之优先方案
 

Semelhante a Android 源码分析 -- (一) Android启动过程

Windowsîä¼þïµí³¹ýâëçý¶¯¿ª·¢½ì³ì(µú¶þ°æ)
Windowsîä¼þïµí³¹ýâëçý¶¯¿ª·¢½ì³ì(µú¶þ°æ)Windowsîä¼þïµí³¹ýâëçý¶¯¿ª·¢½ì³ì(µú¶þ°æ)
Windowsîä¼þïµí³¹ýâëçý¶¯¿ª·¢½ì³ì(µú¶þ°æ)
rvillegasg
 
unix toolbox 中文版
unix toolbox 中文版unix toolbox 中文版
unix toolbox 中文版
Jie Bao
 
尚观Linux研究室 linux驱动程序全解析
尚观Linux研究室   linux驱动程序全解析尚观Linux研究室   linux驱动程序全解析
尚观Linux研究室 linux驱动程序全解析
hangejnu
 
X64服务器 lamp服务器部署标准 new
X64服务器 lamp服务器部署标准 newX64服务器 lamp服务器部署标准 new
X64服务器 lamp服务器部署标准 new
Yiwei Ma
 
Install Oracle11g For Aix 5 L
Install Oracle11g For Aix 5 LInstall Oracle11g For Aix 5 L
Install Oracle11g For Aix 5 L
heima911
 
Erlang Practice
Erlang PracticeErlang Practice
Erlang Practice
litaocheng
 
康盛创想项目部Linux 服务器部署标准(最新版)
康盛创想项目部Linux 服务器部署标准(最新版)康盛创想项目部Linux 服务器部署标准(最新版)
康盛创想项目部Linux 服务器部署标准(最新版)
Yiwei Ma
 
Unixtoolbox zh cn
Unixtoolbox zh cnUnixtoolbox zh cn
Unixtoolbox zh cn
xdboy2006
 

Semelhante a Android 源码分析 -- (一) Android启动过程 (20)

Windowsîä¼þïµí³¹ýâëçý¶¯¿ª·¢½ì³ì(µú¶þ°æ)
Windowsîä¼þïµí³¹ýâëçý¶¯¿ª·¢½ì³ì(µú¶þ°æ)Windowsîä¼þïµí³¹ýâëçý¶¯¿ª·¢½ì³ì(µú¶þ°æ)
Windowsîä¼þïµí³¹ýâëçý¶¯¿ª·¢½ì³ì(µú¶þ°æ)
 
unix toolbox 中文版
unix toolbox 中文版unix toolbox 中文版
unix toolbox 中文版
 
Apache安装配置mod security
Apache安装配置mod securityApache安装配置mod security
Apache安装配置mod security
 
尚观Linux研究室 linux驱动程序全解析
尚观Linux研究室   linux驱动程序全解析尚观Linux研究室   linux驱动程序全解析
尚观Linux研究室 linux驱动程序全解析
 
基于 FRIDA 的全平台逆向分析
基于 FRIDA 的全平台逆向分析基于 FRIDA 的全平台逆向分析
基于 FRIDA 的全平台逆向分析
 
Asm+aix
Asm+aixAsm+aix
Asm+aix
 
构建ActionScript游戏服务器,支持超过15000并发连接
构建ActionScript游戏服务器,支持超过15000并发连接 构建ActionScript游戏服务器,支持超过15000并发连接
构建ActionScript游戏服务器,支持超过15000并发连接
 
Oraliux+mysql5单机多实例安装文档
Oraliux+mysql5单机多实例安装文档Oraliux+mysql5单机多实例安装文档
Oraliux+mysql5单机多实例安装文档
 
X64服务器 lamp服务器部署标准 new
X64服务器 lamp服务器部署标准 newX64服务器 lamp服务器部署标准 new
X64服务器 lamp服务器部署标准 new
 
基于Eclipse和hadoop平台应用开发入门手册
基于Eclipse和hadoop平台应用开发入门手册基于Eclipse和hadoop平台应用开发入门手册
基于Eclipse和hadoop平台应用开发入门手册
 
Glider
GliderGlider
Glider
 
Install Oracle11g For Aix 5 L
Install Oracle11g For Aix 5 LInstall Oracle11g For Aix 5 L
Install Oracle11g For Aix 5 L
 
5, system admin
5, system admin5, system admin
5, system admin
 
Tcfsh bootcamp day2
 Tcfsh bootcamp day2 Tcfsh bootcamp day2
Tcfsh bootcamp day2
 
Optimzing mysql
Optimzing mysqlOptimzing mysql
Optimzing mysql
 
Erlang Practice
Erlang PracticeErlang Practice
Erlang Practice
 
康盛创想项目部Linux 服务器部署标准(最新版)
康盛创想项目部Linux 服务器部署标准(最新版)康盛创想项目部Linux 服务器部署标准(最新版)
康盛创想项目部Linux 服务器部署标准(最新版)
 
Unixtoolbox zh cn
Unixtoolbox zh cnUnixtoolbox zh cn
Unixtoolbox zh cn
 
unixtoolbox_zh_CN
unixtoolbox_zh_CNunixtoolbox_zh_CN
unixtoolbox_zh_CN
 
Cfengine培训文档 刘天斯
Cfengine培训文档 刘天斯Cfengine培训文档 刘天斯
Cfengine培训文档 刘天斯
 

Android 源码分析 -- (一) Android启动过程

  • 1. 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"))
  • 2. 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 filen"); 9) 解析 init.rc (platform/system/core/rootdir/init.rc)。
  • 3. 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);
  • 4. /* 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();
  • 5. 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 处理的实时性。
  • 6. 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 += -DBOOTCHART=1 endif 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())
  • 7. 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; }