android 启动流程 相关2 init进程 属性服务

登山则情满于山,观海则意溢于海。这篇文章主要讲述android 启动流程 相关2 init进程 属性服务相关的知识,希望能为你提供帮助。
 
Init属性服务
系统属性服务
属性梳理
来源和读取时机

  1. 来源:内核参数
ro.kernel.*     代表有qemu内核参数才会设置(在虚拟机中)
ro.boot.*         1.内核设备树相关的设备属性,从 /proc/cmdline 的androidboot.* 中来
2.内部变量export_kernel_boot_props()中有个默认值表,当内核所给出的属性如ro.boot.serialno没有值时,那么ro.serialno的默认值将是表中给出的
 
  1. 来源:属性文件
ctl.* 一般用于IPC,如ctl.bootanim 开机动画,这种不会在读取/property_contexts  后,加入到 contexts集合中(控制消息不持久化记录,通过ctl.action=servername 的servername用ServiceManager::GetInstance().FindServiceByName(servername)找到对应的Service* 对象,然后根据action是 start stop restart 分别调用Service* 对应的函数)
 
PROP_PATH_* 的5个地方的 多个.prop 文件,这5个地方的多个属性文件主要分为3类。
1.  PROP_PATH_RAMDISK_DEFAULT  在属性服务初始化的时候加载,
2.  property_service_init_action() 打开属性服务,读取 PROP_PATH_SYSTEM_BUILD,PROP_PATH_SYSTEM_DEFAULT   
3.  还有用户的 持久化属性PROP_PATH_LOCAL_OVERRIDE  (可以通过 init.rc 中定义的 on property:vold.decrypt=trigger_load_persist_props  load_persist_props      这个action来触发该类属性文件的读取)
Persist.* 需要持久化记录的,记录文件在PROP_PATH_LOCAL_OVERRIDE  =   /data/property/属性名,   例如:    persist.sys.usb.config    ,  persist.sys.timezone  时区设置
 
  1. 来源:.rc文件中的action调用setprop命令设置
在init.rc 中的 on boot 里定义了setprop sys.usb.state $sys.usb.config  等
 
  1. 来源: 其他程序调用setprop
Init进程的set_init_properties_action() 中设置了一些 ro.* 的只读属性
 
 
 
system—properties属性服务的规则
只有1个写入者,多个读取者
Prop_area.count 永远不会减小值
一旦分配完成,prop_info 的name不会改变
一旦分配完成,prop_info 的offset不会改变
 
关于读取值相关的步骤:
  1. Serial = pi-> serial
  2. if SERIAL_DIRTY(serial),wait*,然后执行第1步
  3. Memcpy(local, pi-> value, SERIAL_VALUE_LEN(serial) + 1)
  4. If pi-> serial != serial , 执行第2步
 
关于写入值的步骤:
  1. Pi-> serial = pi-> serial | 1
  2. Memcpy(pi-> value, local_value, value_len)
  3. Pi-> serial  = (value_len < < 24) | ((pi-> serial + 1) & 0xffffff)
 
 
Property_set_impl()的实现
Selinux.reload_policy 等特殊的值不允许重复设置为1
__system_property_find(name) 找到 prop_info *的指针地址
get_prop_area_for_name(name)
list_find(prefixes,…)遍历匹配给定name的prefix_node 的指针,没有就返回null,然后访问context属性,然后访问pa方法获得 prop_area*,也就是在/dev/__propertes__/xxxx 中在tmpfs文件系统里映射到RAM的那块文件的内存地址。然后调用prop_area-> find(name),内部实际上调用了find_property()
find_property() 从整个root_node() 的一个 trie(字典/二叉树混合结构)中查找属性,数据类型是prop_area,这个树的内存地址在data_,返回时被强制转换为 struct prop_bt*
如果已存在,那么用 __system_property_update() 更新值(本质是memcpy value 到 pi-> value),否则用 __system_property_add() 添加新的
 
 
 
查找某个属性值的过程:key char *name  -> __system_property_find(name) ->                                                                                           prop_info*
                                                            get_prop_area_for_name(name) ->                                                                                               prop_area.find(name)
                                                                    list_find(prefixes,…) -> prefix_node  -> prefix_node.context   ->   context_node.pa()
经历的数据结构变迁:
key == char -> prefix_node  -> context_node -> prop_area  -> prop_info -> prop_info.value =https://www.songbingjia.com/android/= char == value
 
 
 
__system_property_add()
prop_area* get_prop_area_for_name()      * ?
context_node::open()
map_prop_area()
如何新增的?
 
 
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() 函数什么时候执行,服务什么时候被启动?
从状态常量中,看出有一些启动方式
  1. 通过class自动启动(在某个action被触发时,如果定义了calss_start [class分组名称]  这条命令,则会对该组内所有符合条件的service进行启动)
  2. 某些 trigger触发action(如init.c中代码里固定的trigger,或某个.rc中的某个action下定义的start命令也可以触发),然后通过命令 start启动服务
  3. 在退出时自动(由init)重启(通过init接受到signal,设置service的运行状态, init最后死循环中检测需要重启的服务)
  4. 通过属性服务的控制命令  ctl.start/stop/restart 来启动服务,最后进入init中的msg_start()(命令可能由zygote进程通过命令socket接受后,并通过/bin/start可执行文件发出)
 
服务执行的本质是init调用fork后子进程execv服务指定的可执行文件
 
一个服务启动前的准备工作:
  1. 清除service对象的 (SVC_DISABLED|SVC_RESTARTING|SVC_RESET)  标志
  2. time_started  = 0,重置运行时长
  3. 如果需要console则打开/dev/console 并dup 到 0,1,2
  4. Fork()
子进程:
  1. 属性初始化 properties_inited(),init_property_area();   重新读取 PROP_PATH_RAMDISK_DEFAULT  的属性
  2. 设置服务用 setenv 设置的环境变量
  3. 创建服务用 socket 设置的域socket (socket < name> < type> < perm> [ < user> [ < group> ] ])
  4. 如果需要console 则设置 setsid()  设置session 和 group id ,让当前进程成为单独的一个会话,作为这个会话的前台进程,单独接受这个console的io
  5. Setpgid(0,getpid())  设置当前进程的 pgid为当前pid,与init进程的进程组独立开来
  6. 将服务用 gid 设置的参数,用gid(参数) 设置,然后 group,uid。。。
  7. 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)
 
接下来分析几个主要的服务
Servicemanager
Zygote
Zygote  –  System_server(和Servicemanager有什么关系)
 
 
 
system_process的进程名称是system_server,就是我们通过ps可以看到的
其实zygote 可以算半个c进程,半个java进程(打开新app的时候,它会在一个虚拟机上下文中 forkAndSpecialize() )(jdwp默认不打开,默认没法用java层的调试器调试)
 
 
几个主要进程的启动顺序和角色 (还有主要函数)
看图:
【android 启动流程 相关2 init进程 属性服务】
android 启动流程 相关2 init进程 属性服务

文章图片

 

    推荐阅读