Android init 启动流程

Init 进程

介绍

  1. init 进程是 Android 系统启动后,由内核启动的第一个用户级进程,其进程号为 1,是所有进程的父进程。
  2. 在 Android 系统中,可以使用命令 pstree -p 查看系统的进程树,可以在结果中直观的看到 init 作为所有进程的父进程。
  3. init 进程执行的代码位于文件 /system/core/init/init.cpp

代码

init 进程执行后会做以下几件事情

  1. 创建和挂载系统启动后所需要的文件目录
  2. 初始化系统服务
  3. 设置子进程信号处理函数
  4. 启动属性服务
  5. 解析 init.rc
  6. 循环等待

1. 创建和挂在文件目录

  • init 进程挂载了tmpfs、devpts、proc、sysfs、selinuxfs 五种文件系统。
  • 这些文件系统都是系统运行时目录,如果系统停止运行,则会删除这些目录。

挂载文件系统

2. 初始化系统服务

  • 在 Android 系统中,所有的进程共享系统设置值,为此提供了一个名称为 property 的保存空间
  • init 进程调用 property_init 函数,在共享内存区域内,创建并初始化属性值,而后再通过执行中进程所提供的 API,访问属性域中的设置值。
  • 如果更改属性值,只能在 init 进程中修改
  • 当修改属性值时,要预先向 init 进程提交值变更申请,然后 init 进程处理该申请,并修改属性值

image

3. 设置子进程信号处理函数

  • 创建子进程信号处理函数的目的是为了防止 init 进程创建的子进程成为僵尸进程
  • 系统会在子进程暂停和终止时发送 SIGHILD 信号,子进程信号处理函数就是用来监听该信号,当进程收到该信号后,可以去做对应的处理。
  • 具体实现是创建一对套接字,然后注册信号处理函数和要监听的信号,最后将该套接字放入到 epoll 句柄中去监听。类同于普通的信号监听和收到信号后的函数处理步骤一致。

image

  • 僵尸进程的危害
    • 在 Unix/Linux 系统中,父进程使用 fork 创建子进程,如果子进程终止后,父进程并不知道子进程已经结束,此时虽然子进程已经退出了,但是系统进程表中还为它保留了一定信息(如进程号,退出状态,运行时间等),此时这个已经结束但是系统进程表中还未清除的进程就叫做僵尸进程。
    • 系统进程表是一项有限资源,如果系统进程表被僵尸进程耗尽,系统就可能无法再创建新的进程。

4. 启动属性服务

  • 启动属性服务是 init 最主要的功能之一。
  • init 调用 start_property_service() 启动属性服务。

image

5. 解析 init.rc

init.rc 介绍

  • init.rc 在 Android 是一个配置文件,负责在系统启动过程中初始化各种服务和设置。
  • 该文件位于目录 /system/etc 下,该文件包含了系统启动过程中需要执行的命令和初始化脚本,用于设置系统环境、启动服务和应用进程。
  • 该文件时定义系统启动时需要执行的一些服务和进程,以及一些系统属性的设置。文件中的命令最终会映射到对应的函数执行,如 cmd mount_all 最终会映射到 do_mount_all() 函数

init 解析 init.rc 文件

  • init 调用 LoadBootScripts() 来解析 init.rc
    image

init.rc 文件格式介绍

  • init.rc 文件由 Android 初始化语言编写,文件大体分为三个部分,以 import 关键字开头的文件导入,一部分是以 on 关键字开头的,一部分则以 service 关键字开头
    image

Android Init Language

  • Android 初始化语言包含 5 中类型语句,Action、Command、Service、Option 和 Import
介绍
  • 所有类型的语句基本基于行,一个语句包含多个单词,单词之间通过空格符分隔
  • 如果需要在单词内使用空格,则需要反斜杠转移,或者使用双引号包含有空格的文本
  • 反斜杠出现在一行的末尾,表示下一行内容仍属当前语句
  • 以 # 开头表示注释
  • 所有的 Action 和 Service 语句隐含表示表示一个段落的开始,所有的 Command 和 Option 语句从属于上方最近的一个段落
  • Action 和 Service 拥有唯一的名字,如果出现重名,则后出现的定义的则视为错误而被忽略
Action
  • Action 是一个有名字的命令谢列
  • 每个 Action 都定义了一个触发条件,用于指示什么时候执行这个动作
  • 当与 Action 的 Trigger 匹配的事件发生后,这个 Action 就会被加入到一个执行队列的队尾(除非这个 Action 已经位于队列中了)
  • 队列中的每一个 Action 都会被依次取出,而每个 Action 中的每个 Command 也将会依次执行
  • Action 的语法格式如下:
    image
Service
  • Service 是 init 进程需要启动的一些服务,init 进程可能在这些程序推出后重启它们
  • Service 的语法格式如下:
    image
Option
  • Option 是 service 的配置参数,它们影响 Service 在何时并以何种方式运行
参数 说明
cirtical 表示这个一项设备关键型服务,如果在 4 分钟退出超过 4 此,设备将重新启动进入 recovery 模式
disabled 这个服务不能同它的类自动启动,必须按名称显示启动
setenv 在启动的进程中,将环境变量 设置为
socket [ [ []]] 创建一个 Unix 域套接字,命名为 /dev/socket/,并传递它的文件描述符 fd 到它的启动进程中。 必须是 “dgram”,“stream”,或者 “seqpacket”。user 和 group 默认为 0。 seclabel 是套接字 SELinux 安全上下文,他默认为服务安全上下文,由 seclabel 指定或者是基于服务可执行文件安全上下文计算得来
user 在运行这个服务前将该服务的用户名改为 ,默认为 root
group []* 运行这个服务之前将该服务的组名改为 ,除了必须的第一个组名,附加的组名通常被用于设置进程的补充组。默认为 root
seclabel 在运行这个服务之前将该服务的安全上下文改为 ,主要从 rootfs 等中被服务使用。位于系统分区的服务可以基于他们的文件安全上下文使用策略定义的转换。如果未被指定或者在策略转换未被定义,默认为初始化上下文
oneshot 当服务退出时不重启
class 为该服务指定一个类名称。所有同一个类名的服务可以同时启动和停止。如果 class 选项指定一个类名,则默认类名 “default”

6. 循环等待

上述所有步骤完成后,init 会进入一个循环中。每次循环都会去调用 ExecuteOneCommand() 执行命令列表中的一条命令,如果服务挂了还会调用 RestartProcesses() 重启服务。

image

posted @ 2024-03-26 14:02  王清河  阅读(558)  评论(0编辑  收藏  举报