Android原生流程
Init进程 主要流程及分支梳理
ueventd_main()
watchdogd_main()
主要流程
a) 公共部分
增加PATH 环境变量
初始化内核日志,打开/dev/kmsg,此时logcat还未初始化,使用内核的log系统
设置selinux
b) First_stage
Mount 和mkdir 例如/dev /proc /sys
在当前是内核态的状态下设置selinux。是否enforceing(开启)或关闭,取决于配置,若编译时允许内核指定,那么从androidboot.selinux 的取值决定开关,否则默认打开,编译配置ALLOW_PERMISSIVE_SELINUX为false的话,那么需要开启selinux到enforceing状态(在system/core/init/Android.mk中)
Execv() 重新执行当前 init进程的镜像,并带入 –-second-stage 参数重新执行main()进入第二阶段
c) Second_stage
property_init() 初始化property_server,读/property_contexts文件按照链表方式构造到内存分别存为prefix 和 context,建立/dev/__propertes__/* (*为context名称),然后映射这些文件的内存地址到各context 的_pa成员上,初始化__system_property__ 为 /dev/__property__/property_servial,并完成此部分初始化
process_kernel_dt() 处理内核设备树,读取/proc/device-tree/firmware/android/compatible内容,从这文件夹中读其他文件,通过property_set()写入一堆ro.boot.* 的属性
process_kernel_cmdline() 处理内核cmdline,通过读取/proc/cmdline获取信息,第一遍处理常规的一些事情(用空格分隔,key=val,androidboot.*被设置为ro.boot.*作为属性),并且看看当前是否在虚拟机中,第二遍仅仅是给虚拟机导出一些内核参数作为属性(只要是ro.kernel.*的参数都导出为属性,是否是虚拟机状态?当static char qemu[32] 不为空则是.哪里设置的?第一遍以非虚拟机状态处理/proc/cmdline时,若发现key为qemu的value,则写入char qemu[32],代表有必要进行第二遍读取解析)
export_kernel_boot_prop() 导出内核启动变量给内部使用。ro.boot.* 的一部分 变名为 ro.* 再设置为新的属性,若内核未给出的部分,则按表中给的默认值赋值
Signal_handler_init() 初始化信号处理机制,用sigaction设置接受SIGCHLD信号的处理函数(当某个子进程结束或终止,陷入,停止后又继续时会发出次信号),每当接收到这个信号时,通过向先创建好的1个SOCKET对的fd中写入1,转而用epoll的方式,在外层死循环里来处理业务逻辑。
Property_load_boot_defaults(); 从属性文件初始化一些属性-> load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT,NULL); 这个常量是”/default.prop”文件,相关的有5个PROP_PATH_* 定义在 /sys/_system_propertieis.h 中,但文件中有 import 特殊关键字则代表额外文件引入,逐个调用property_set() 加到内存(/dev/__propertes__/*)。
start_property_service();打开属性服务并持续监听。 创建一个 UNIX DOMAIN 的socket (在/dev/socket/下,用property_service命名,由bind()创建,然后将所有者和所有组改为root,将文件权限改为rw-rw-rw-)并 listen,用epoll注册可读回调,回调到handle_property_set_fd(),socket中发送prop_msg结构,由命令,属性名,属性值构成,若属性以 ctl.开头则进入handle_control_message 否则 set_property()
解析 init.rc ,主要将解析出的Server,action放入不同的集合.并且按顺序过滤特定trigger的action,添加到 序列化的action链表,以及添加一些。Rc文件中未指定的action到 序列化的action链表中
进入一个死循环,按先进先出的顺序一个个处理刚才构造好的序列化 action列表中的每个action,执行action的0个或多个command(包括fork出serviceManager和zygote),每执行完一个command,执行一些检查,如要重启的服务进程,或其他只进行1次的初始化动作(如将properte _server的socket set_fd 放入ufds[]管理,一共有3个 属性服务set fd,信号fd,keychord fd). 另外检测是否有进程需要重启,然后用poll(老版本系统,新版本用epoll,总之都是用IO复用的方式在这个循环里检测那3个业务的事件并处理),当fd有POLLIN可读事件时,分别进入 handle_property_set_fd(),handle_keychord(), handle_signal()。
9.1。子进程服务死亡管理。服务重启标志设置。
handle_signal() ,从fd中读出字符,然后进入wait_for_one_process() 检查是否有必要重启这些关掉的进程,他会使用waitpid 取出所有关闭的最后一个子进程pid,然后用service_find_by_pid() 找到对应的service对象,有设置SVC_ONESHOT的将pid所在进程组杀掉,然后进行清理unlink socket,去除SVC_RUNNING 标志等操作,对符合重启要求的,如设置为SVC_DISABLED | SVC_RESET 的不自动重启,对SVC_CRITICAL的在4分钟内崩溃4次以上的直接重启系统到recover模式,最后剩下的满足重启条件的,给设置上 SVC_RESTARING 标志。然后执行该service的每一条command,最后调用notify_service_state() 通知属性服务添加一条属性如 init.svc.servername, value为restarting 的属性后退出。(什么时候启动这个restaring的服务呢?在这个死循环的restart_processes()中,也就是下一轮循环)
9.2.属性设置服务。
handle_property_set_fd(),当有握手的server socket可读时,用accept获取client socket 的fd,然后通过getsocketopt() 获取对方的ucred,包括pid,uid,gid,然后通过recv 一个prop_msg 结构的消息,根据prop_msg.cmd == PROP_MSG_SETPROP时,获取msg.name作为属性名.
若是ctl.开头的非持久化控制命令,则判断对方权限,允许SYSTEM,ROOT操作,或服务为dumpstate 或ril-daemon时,要额外匹配UID和GID,然后根据操作要求调用对应服务的service_start/stop()
另外一种是非控制命令,先检查权限,若name开头是ro.则掠过这3个字符,然后根据name的前缀查表property_perms匹配,比对 UID,GID是否符合权限,如果符合权限就通过property_set()来设置属性。
9.3。多点触摸
handle_keychord()只在ro.debuggable != 1 或 init.svc.adbd != running 被设置时才进行处理
Init的主要职责:
系统初始化,解析.rc,按时机触发trigger让action执行command,启动服务,属性服务的set功能,keychord 多点触摸 功能,子进程发送SIGCHLD时
属性服务 ,property_service,持续监听/dev/socket/property_service 的UNIX DOMAIN socket写入
Service子进程状态管,理及重启(通过signal handler实现)
执行action_queue 的action队列
Keychord
解析rc
Parser如何解析init.rc
关键字分为3类,SECTION,COMMAND,OPTION
3个SECTION 关键字:service,on,import,对应parse_service(), parse_action(), parse_config_file(). 正常情况是解析器会解析到文件结束为止
这些是section,解析器会先判断是否遇到了3个secetion中的其中一种,如果是,那么就调用对应section的解析方法解析,然后如果这个section由多行构成,那么让指针parse_line指向对secetion内容进行解析的函数。例如 service就是parse_line_service,下次解析到新的一行时或文件结束时,会被调用,然后把解析到的如onrestart放入当前service.onrestart.commands中,其他还有如critical 等关键字属于service.flags 等等其他属性。然后把这个service加入到模块内全局变量service_list listnode类型的链表中。服务只是被通过定义-解析放在内存中,什么时候被service_start()调用,什么时候才算被启动。举个例子在 action 的额 start 中被指定某个server,并且trigger被系统激发,此时会调用service_start()从init进程中fork一个子进程 运行服务指定的program。
关键字 对象类型 模块内全局变量集合
Service service service_list
Actions action action_list action_queue
Import 会立刻执行导入指定.rc文件并解析
COMMAND 类型关键(用在action)
exec 等,主要是类似调用某个方法,对应函数do_exec
start do_start 用service_start() 打开某个给定名字的服务
OPTION 类型关键字(用在service)
disabled 等 主要是设定当前section的属性,如是否启用或关闭
user
具体要看/keywords.h 中的定义
.rc文件格式
service ueventd /sbin/ueventd //section
class core //sectionLine = command/option
critical
seclabel u:r:ueventd:s0
…
其中service 格式: service name program
Program 可以在后面携带参数
Service 下面可以接option定义和onrestart 的command定义
Name 会成为有点新进程的 进程名
一个service被解析完后会返回对应的 service对象
Action的格式:
On trigger
Command
…
关于ACTION
注意,action只能有command,不能有option的定义
Start command的解析:通过查表keyword_info 找到对应的函数指针并挂载
Action实际上是让init进程在一定时机去执行一些命令
Action的执行先后依赖于init中将筛选出的action加入到action_queue队列中的先后顺序决定(动态方式:某个action本身被触发后还会通过trigger命令又一次将符合条件的action筛选出后加入到action_queue队尾。)
当解析完rc文件后,初始化action_queue (通过特定trigger从init.rc中筛选出一批action或单独添加action和回调函数,将他们按操作顺序先后串行的放到action_queue中)
使用action_for_each_trigger(trigger,回调函数) 就可以从action_list中遍历出符合trigger的action,然后在回调函数里做自定义的操作
一个Action有1个trigger并且有0个或多个command组成
如:在init.c的代码中,调用action_for_each_trigger("early-init", action_add_queue_tail); 方法
会找出所有tigger为 early-init 的 action,执行action_add_queue_tail() 方法,这个方法将符合条件的action
加入到action_queue链表中。所以init中进行剩下一部分的初始化工作,实际上就是根据这个按顺序构造好的action_queue队列,以先进先出的顺序(以按特定trigger筛选的顺序),顺序执行从.rc中解析到的action,以及添加自定义action的顺序。
queue_builtin_action(func,name)
分配一个act指针,赋予name=name参数,初始化act->commands,在分配一个cmd指针,func=func参数,加入到act的commands里,然后把这个act加入到action_list,然后把这个act加到action_queue队尾
换句话说在action_queue队尾,加入了一个指定name,指定commands func的action
第一个由init.c添加的action是 queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");就是等待冷启动完成(/dev/.coldboot_done文件的创建,它由ueventd_main()里调用device_init()完成创建,uevent主要完成ueventd.rc文件中定义的文件权限和所有者(设备权限)的初始化及设备初始化等)
接着,property_init_action(初始化属性服务),keychord_init,console_init,set_init_properties(设置一些默认属性,如ro.serialno,ro.hardware等一些ro.属性) 。。。
然后init进入一个死循环,其中相关的有
执行execute_one_command(),这里会从action_queue的头开始一个个取出并删除,先入先出,返回action*,用全局变量cur_action记住,如果没有返回null,就获取这个action的第一个command,用cur_command记住,
然后restart_processes() ,每执行完一个command后就检测是否有需要重启的服务,办法是遍历service_list,找出flag有置SVC_RESTARTING位域的,就执行restart_service_if_needed()方法,这个方法最主要的是在当前时间超出或等于该服务指定的下次启动时间后,调用service_start(svc, NULL)通过fork 启动找出的服务
action的触发方式(trigger):
Action 的Trigger 分2类
property: trigger ,如:on property:sys.usb.config=accessory .这依赖init的属性服务
触发时机:
a) init.main() 进入最后死循环之前,用queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers"); 将从.rc文件中解析出的action中以 property: 开头的action筛选出,并加入到 action_queue队列.(启动完系统时全部执行一遍检测,对符合条件的action,执行其command)。并且打开property_triggers_enabled=1开关,让通过property_set()改变属性值的时候,可以回调到相关属性值的监听action(让下面这种触发时机的情况使能).
b) 当init进程的属性服务,接收到修改属性的消息时,在property_set() 中会调用property_changed(),将从action_list(所有.rc文件中解析到的action)删选出监听该属性名的action,并且修改后的属性值等于action给出的,那么将他们加入到action_queue,最后在init.main()的死循环中被执行
非property: trigger,如:on early-init
触发时机:
a) init.c 中通过手动筛选action_for_each_trigger(”trigger”,func)筛选出(并在最后的死循环中用execute_one_command() 执行action_queue序列)
b) 其他action配置的 trigger 命令触发,从action_list中筛选出指定trigger的action集合,并添加到action_queue队尾(例如:on property:vold.decrypt=trigger_post_fs_data trigger post-fs-data),最终还是在init.main() 最后的死循环中执行
Trigger在init中的顺序(android 4.0.4,新版有所不同)
early-init init bootmode为charger的话(early-fs fs post-fs post-fs-data) bootmode不为charger的话(charger)否则(early-boot boot)
还嵌有一些单独的action用于初始化一些东西
property_init keychord_init console_init set_init_properties property_service_init signal_init check_startup queue_propety_triggers bootchart_init
Android原生流程
Init进程 主要流程及分支梳理
ueventd_main()
watchdogd_main()
主要流程
a) 公共部分
增加PATH 环境变量
初始化内核日志,打开/dev/kmsg,此时logcat还未初始化,使用内核的log系统
设置selinux
b) First_stage
Mount 和mkdir 例如/dev /proc /sys
在当前是内核态的状态下设置selinux。是否enforceing(开启)或关闭,取决于配置,若编译时允许内核指定,那么从androidboot.selinux 的取值决定开关,否则默认打开,编译配置ALLOW_PERMISSIVE_SELINUX为false的话,那么需要开启selinux到enforceing状态(在system/core/init/Android.mk中)
Execv() 重新执行当前 init进程的镜像,并带入 –-second-stage 参数重新执行main()进入第二阶段
c) Second_stage
property_init() 初始化property_server,读/property_contexts文件按照链表方式构造到内存分别存为prefix 和 context,建立/dev/__propertes__/* (*为context名称),然后映射这些文件的内存地址到各context 的_pa成员上,初始化__system_property__ 为 /dev/__property__/property_servial,并完成此部分初始化
process_kernel_dt() 处理内核设备树,读取/proc/device-tree/firmware/android/compatible内容,从这文件夹中读其他文件,通过property_set()写入一堆ro.boot.* 的属性
process_kernel_cmdline() 处理内核cmdline,通过读取/proc/cmdline获取信息,第一遍处理常规的一些事情(用空格分隔,key=val,androidboot.*被设置为ro.boot.*作为属性),并且看看当前是否在虚拟机中,第二遍仅仅是给虚拟机导出一些内核参数作为属性(只要是ro.kernel.*的参数都导出为属性,是否是虚拟机状态?当static char qemu[32] 不为空则是.哪里设置的?第一遍以非虚拟机状态处理/proc/cmdline时,若发现key为qemu的value,则写入char qemu[32],代表有必要进行第二遍读取解析)
export_kernel_boot_prop() 导出内核启动变量给内部使用。ro.boot.* 的一部分 变名为 ro.* 再设置为新的属性,若内核未给出的部分,则按表中给的默认值赋值
Signal_handler_init() 初始化信号处理机制,用sigaction设置接受SIGCHLD信号的处理函数(当某个子进程结束或终止,陷入,停止后又继续时会发出次信号),每当接收到这个信号时,通过向先创建好的1个SOCKET对的fd中写入1,转而用epoll的方式,在外层死循环里来处理业务逻辑。
Property_load_boot_defaults(); 从属性文件初始化一些属性-> load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT,NULL); 这个常量是”/default.prop”文件,相关的有5个PROP_PATH_* 定义在 /sys/_system_propertieis.h 中,但文件中有 import 特殊关键字则代表额外文件引入,逐个调用property_set() 加到内存(/dev/__propertes__/*)。
start_property_service();打开属性服务并持续监听。 创建一个 UNIX DOMAIN 的socket (在/dev/socket/下,用property_service命名,由bind()创建,然后将所有者和所有组改为root,将文件权限改为rw-rw-rw-)并 listen,用epoll注册可读回调,回调到handle_property_set_fd(),socket中发送prop_msg结构,由命令,属性名,属性值构成,若属性以 ctl.开头则进入handle_control_message 否则 set_property()
解析 init.rc ,主要将解析出的Server,action放入不同的集合.并且按顺序过滤特定trigger的action,添加到 序列化的action链表,以及添加一些。Rc文件中未指定的action到 序列化的action链表中
进入一个死循环,按先进先出的顺序一个个处理刚才构造好的序列化 action列表中的每个action,执行action的0个或多个command(包括fork出serviceManager和zygote),每执行完一个command,执行一些检查,如要重启的服务进程,或其他只进行1次的初始化动作(如将properte _server的socket set_fd 放入ufds[]管理,一共有3个 属性服务set fd,信号fd,keychord fd). 另外检测是否有进程需要重启,然后用poll(老版本系统,新版本用epoll,总之都是用IO复用的方式在这个循环里检测那3个业务的事件并处理),当fd有POLLIN可读事件时,分别进入 handle_property_set_fd(),handle_keychord(), handle_signal()。
9.1。子进程服务死亡管理。服务重启标志设置。
handle_signal() ,从fd中读出字符,然后进入wait_for_one_process() 检查是否有必要重启这些关掉的进程,他会使用waitpid 取出所有关闭的最后一个子进程pid,然后用service_find_by_pid() 找到对应的service对象,有设置SVC_ONESHOT的将pid所在进程组杀掉,然后进行清理unlink socket,去除SVC_RUNNING 标志等操作,对符合重启要求的,如设置为SVC_DISABLED | SVC_RESET 的不自动重启,对SVC_CRITICAL的在4分钟内崩溃4次以上的直接重启系统到recover模式,最后剩下的满足重启条件的,给设置上 SVC_RESTARING 标志。然后执行该service的每一条command,最后调用notify_service_state() 通知属性服务添加一条属性如 init.svc.servername, value为restarting 的属性后退出。(什么时候启动这个restaring的服务呢?在这个死循环的restart_processes()中,也就是下一轮循环)
9.2.属性设置服务。
handle_property_set_fd(),当有握手的server socket可读时,用accept获取client socket 的fd,然后通过getsocketopt() 获取对方的ucred,包括pid,uid,gid,然后通过recv 一个prop_msg 结构的消息,根据prop_msg.cmd == PROP_MSG_SETPROP时,获取msg.name作为属性名.
若是ctl.开头的非持久化控制命令,则判断对方权限,允许SYSTEM,ROOT操作,或服务为dumpstate 或ril-daemon时,要额外匹配UID和GID,然后根据操作要求调用对应服务的service_start/stop()
另外一种是非控制命令,先检查权限,若name开头是ro.则掠过这3个字符,然后根据name的前缀查表property_perms匹配,比对 UID,GID是否符合权限,如果符合权限就通过property_set()来设置属性。
9.3。多点触摸
handle_keychord()只在ro.debuggable != 1 或 init.svc.adbd != running 被设置时才进行处理
Init的主要职责:
系统初始化,解析.rc,按时机触发trigger让action执行command,启动服务,属性服务的set功能,keychord 多点触摸 功能,子进程发送SIGCHLD时
属性服务 ,property_service,持续监听/dev/socket/property_service 的UNIX DOMAIN socket写入
Service子进程状态管,理及重启(通过signal handler实现)
执行action_queue 的action队列
Keychord
解析rc
Parser如何解析init.rc
关键字分为3类,SECTION,COMMAND,OPTION
3个SECTION 关键字:service,on,import,对应parse_service(), parse_action(), parse_config_file(). 正常情况是解析器会解析到文件结束为止
这些是section,解析器会先判断是否遇到了3个secetion中的其中一种,如果是,那么就调用对应section的解析方法解析,然后如果这个section由多行构成,那么让指针parse_line指向对secetion内容进行解析的函数。例如 service就是parse_line_service,下次解析到新的一行时或文件结束时,会被调用,然后把解析到的如onrestart放入当前service.onrestart.commands中,其他还有如critical 等关键字属于service.flags 等等其他属性。然后把这个service加入到模块内全局变量service_list listnode类型的链表中。服务只是被通过定义-解析放在内存中,什么时候被service_start()调用,什么时候才算被启动。举个例子在 action 的额 start 中被指定某个server,并且trigger被系统激发,此时会调用service_start()从init进程中fork一个子进程 运行服务指定的program。
关键字 对象类型 模块内全局变量集合
Service service service_list
Actions action action_list action_queue
Import 会立刻执行导入指定.rc文件并解析
COMMAND 类型关键(用在action)
exec 等,主要是类似调用某个方法,对应函数do_exec
start do_start 用service_start() 打开某个给定名字的服务
OPTION 类型关键字(用在service)
disabled 等 主要是设定当前section的属性,如是否启用或关闭
user
具体要看/keywords.h 中的定义
.rc文件格式
service ueventd /sbin/ueventd //section
class core //sectionLine = command/option
critical
seclabel u:r:ueventd:s0
…
其中service 格式: service name program
Program 可以在后面携带参数
Service 下面可以接option定义和onrestart 的command定义
Name 会成为有点新进程的 进程名
一个service被解析完后会返回对应的 service对象
Action的格式:
On trigger
Command
…
关于ACTION
注意,action只能有command,不能有option的定义
Start command的解析:通过查表keyword_info 找到对应的函数指针并挂载
Action实际上是让init进程在一定时机去执行一些命令
Action的执行先后依赖于init中将筛选出的action加入到action_queue队列中的先后顺序决定(动态方式:某个action本身被触发后还会通过trigger命令又一次将符合条件的action筛选出后加入到action_queue队尾。)
当解析完rc文件后,初始化action_queue (通过特定trigger从init.rc中筛选出一批action或单独添加action和回调函数,将他们按操作顺序先后串行的放到action_queue中)
使用action_for_each_trigger(trigger,回调函数) 就可以从action_list中遍历出符合trigger的action,然后在回调函数里做自定义的操作
一个Action有1个trigger并且有0个或多个command组成
如:在init.c的代码中,调用action_for_each_trigger("early-init", action_add_queue_tail); 方法
会找出所有tigger为 early-init 的 action,执行action_add_queue_tail() 方法,这个方法将符合条件的action
加入到action_queue链表中。所以init中进行剩下一部分的初始化工作,实际上就是根据这个按顺序构造好的action_queue队列,以先进先出的顺序(以按特定trigger筛选的顺序),顺序执行从.rc中解析到的action,以及添加自定义action的顺序。
queue_builtin_action(func,name)
分配一个act指针,赋予name=name参数,初始化act->commands,在分配一个cmd指针,func=func参数,加入到act的commands里,然后把这个act加入到action_list,然后把这个act加到action_queue队尾
换句话说在action_queue队尾,加入了一个指定name,指定commands func的action
第一个由init.c添加的action是 queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");就是等待冷启动完成(/dev/.coldboot_done文件的创建,它由ueventd_main()里调用device_init()完成创建,uevent主要完成ueventd.rc文件中定义的文件权限和所有者(设备权限)的初始化及设备初始化等)
接着,property_init_action(初始化属性服务),keychord_init,console_init,set_init_properties(设置一些默认属性,如ro.serialno,ro.hardware等一些ro.属性) 。。。
然后init进入一个死循环,其中相关的有
执行execute_one_command(),这里会从action_queue的头开始一个个取出并删除,先入先出,返回action*,用全局变量cur_action记住,如果没有返回null,就获取这个action的第一个command,用cur_command记住,
然后restart_processes() ,每执行完一个command后就检测是否有需要重启的服务,办法是遍历service_list,找出flag有置SVC_RESTARTING位域的,就执行restart_service_if_needed()方法,这个方法最主要的是在当前时间超出或等于该服务指定的下次启动时间后,调用service_start(svc, NULL)通过fork 启动找出的服务
action的触发方式(trigger):
Action 的Trigger 分2类
property: trigger ,如:on property:sys.usb.config=accessory .这依赖init的属性服务
触发时机:
a) init.main() 进入最后死循环之前,用queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers"); 将从.rc文件中解析出的action中以 property: 开头的action筛选出,并加入到 action_queue队列.(启动完系统时全部执行一遍检测,对符合条件的action,执行其command)。并且打开property_triggers_enabled=1开关,让通过property_set()改变属性值的时候,可以回调到相关属性值的监听action(让下面这种触发时机的情况使能).
b) 当init进程的属性服务,接收到修改属性的消息时,在property_set() 中会调用property_changed(),将从action_list(所有.rc文件中解析到的action)删选出监听该属性名的action,并且修改后的属性值等于action给出的,那么将他们加入到action_queue,最后在init.main()的死循环中被执行
非property: trigger,如:on early-init
触发时机:
a) init.c 中通过手动筛选action_for_each_trigger(”trigger”,func)筛选出(并在最后的死循环中用execute_one_command() 执行action_queue序列)
b) 其他action配置的 trigger 命令触发,从action_list中筛选出指定trigger的action集合,并添加到action_queue队尾(例如:on property:vold.decrypt=trigger_post_fs_data trigger post-fs-data),最终还是在init.main() 最后的死循环中执行
Trigger在init中的顺序(android 4.0.4,新版有所不同)
early-init init bootmode为charger的话(early-fs fs post-fs post-fs-data) bootmode不为charger的话(charger)否则(early-boot boot)
还嵌有一些单独的action用于初始化一些东西
property_init keychord_init console_init set_init_properties property_service_init signal_init check_startup queue_propety_triggers bootchart_init
Init管理service
关于服务的状态有这几种情况
66#define SVC_DISABLED 0x01 /* do not autostart with class */
67#define SVC_ONESHOT 0x02 /* do not restart on exit */
68#define SVC_RUNNING 0x04 /* currently active */
69#define SVC_RESTARTING 0x08 /* waiting to restart */
70#define SVC_CONSOLE 0x10 /* requires console */
71#define SVC_CRITICAL 0x20 /* will reboot into recovery if keeps crashing */
72#define SVC_RESET 0x40 /* Use when stopping a process, but not disabling
73 so it can be restarted with its class */
74#define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */
可以用位域组合
运行状态标记 SVC_RUNNING
这些状态flag 在 .rc 文件中 一个service可以通过对应的OPTION 来设置
服务所指定的program什么时候被调用?
等于是 service_start() 函数什么时候执行,服务什么时候被启动?
从状态常量中,看出有一些启动方式
- 通过class自动启动(在某个action被触发时,如果定义了calss_start [class分组名称] 这条命令,则会对该组内所有符合条件的service进行启动)
- 某些 trigger触发action(如init.c中代码里固定的trigger,或某个.rc中的某个action下定义的start命令也可以触发),然后通过命令 start启动服务
- 在退出时自动(由init)重启(通过init接受到signal,设置service的运行状态, init最后死循环中检测需要重启的服务)
- 通过属性服务的控制命令 ctl.start/stop/restart 来启动服务,最后进入init中的msg_start()(命令可能由zygote进程通过命令socket接受后,并通过/bin/start可执行文件发出)
服务执行的本质是init调用fork后子进程execv服务指定的可执行文件
一个服务启动前的准备工作:
- 清除service对象的 (SVC_DISABLED|SVC_RESTARTING|SVC_RESET) 标志
- time_started = 0,重置运行时长
- 如果需要console则打开/dev/console 并dup 到 0,1,2
- Fork()
子进程:
- 属性初始化 properties_inited(),init_property_area(); 重新读取 PROP_PATH_RAMDISK_DEFAULT 的属性
- 设置服务用 setenv 设置的环境变量
- 创建服务用 socket 设置的域socket (socket <name> <type> <perm> [ <user> [ <group> ] ])
- 如果需要console 则设置 setsid() 设置session 和 group id ,让当前进程成为单独的一个会话,作为这个会话的前台进程,单独接受这个console的io
- Setpgid(0,getpid()) 设置当前进程的 pgid为当前pid,与init进程的进程组独立开来
- 将服务用 gid 设置的参数,用gid(参数) 设置,然后 group,uid。。。
- execve() ,执行指定镜像
Calss 的类别主要定义有core,main。。。
Core 和 main主要是在 on boot时启动
Core有 ueventd,adbd,servicemanager等
Main有netd,zygote,bootanimation,installd,keystore等
Default一般用在recover上
其他的初始化
keychord_init
遍历每个service 调用 add_service_keycodes(),问每个服务是否有使用多点触摸的要求,有的话就把 svc->keychord 加入到 keychords 列表中
然后打开/dev/keychord 一次性写入 keychords 配置信息
Console本质是把 /dev/consle 的fd 作为fork后的service的 STDOUT(0) STDIN(1) STDERR(2)