106°

[日更-2019.5.19] Android 系统内的守护进程(二)--core类中的服务 : healthd

声明

  • 其实很好奇Android系统中的一些关键守护进程服务的作用;
  • 暂且大概分析下它们的作用,这样有助于理解整个系统的工作过程;

0 写在前面的

  • 只要是操作系统,不用说的就是其中肯定会运行着一些很多守护进程(daemon)来完成很多杂乱的工作。通过系统中的init.rc文件也可以看出来,其中每个service中就包含着系统后台服务进程。
  • 而这些服务被分为:core类服务(adbd/servicemanager/healthd/lmkd/logd/vold)和main类服务;
  • main类服务又分为:网络类服务(netd/mdnsd/mtpd/rild)、图形及媒体类服务(surfaceflinger/bootanimation/mediaserver/dnnserver)、其他类服务(installd/keystore/debuggerd/sdcard/Zygote)

1 healthd

    “健康度守护进程”(health daemon)的意思是:该服务会周期性地执行检测综合性的“设备健康值” 的任务(当前只有一个与电池电量有关的任务)。该守护进程也会把自己注册为BatteryPropertiesRegistrar服务(在L版中,则是batterypropreg 或者batteryproperties)。完成了这个注册之后,healthd提供了几个框架服务(例如BatteryStatsService),用它从sysfs那里得到的数据,更新电池电量信息。

    healthd会先去完成初始化配置,然后进入执行代码的循环,如下图所示:

             

    healthd的主循环中使用epoll(2) APT对三个文件描述符进行轮询操作(读),并在每次读取到数据时,执行己注册的相关操作, 具体操作如下表所示。

描述符 类型 作用
wakealarm_fd TimerFD 该定时器(Timer)被设为每隔periodic_chores_interval 秒就唤醒进程一次,被这个定时器唤醒时,healthd将会去执行periodic_chores 的代码
event_fd NETLink 读取内核通知事件。healthd只会关心与其自身相关的power子系统(SUBSYSTEM=POWER)的事件。这些事件包括与电池电量及充电相关的通知。如果读取到了这些事件,healthd进程将会去执行battery_update()函数
binder_fd /dev/binder 这种情况下,healthd将以batterypropregj服务的身份出现,让各个框架客户瑞中的Listener更新相关信息
  • 第一个要读取的文件描述符是 wakealarm_ fd: healthd用它执行周期性的日常工作。这里一共使用两种内部类型: fast (1分钟,用在使用电源充电时)和slow (10分钟,用在使用电池供电时)。当前定义的唯一一个日常工作是battery_update(),该函数会让healthd以Battery PropertiesRegistrar服务的身份,更新电池电量的当前状态。
  • 当从event_fd这个NETLink接收到来自POWER子系统的事件时,这个battery_update()函数也会被调用: healthd 并不会去解析这些事件中的具体信息,只是去刷新一下电池电量的当前状态。之所以需要此模式,是因为healthd需要能够对插上/拔下充电器之类的事件, 或是其他电视~管理警告做出响应。
  • binder_fd是供框架中的listener(首先是BattervStatsService)使用的接口。

2 利用strace追踪healthd的后台行为

    使用功能强大的strace,你可以观察到healthd在后台的行为:通过附加healthd进程的ID (需有root 权限)并调用ptrace(2) APl, strace可以在healthd调用各个系统调用的同时得到相关通知(如下图所示)。因为任何一个进程只要想做一点真正有意义的事,都必须要使用系统调用,这就向我们提供了一种能够详细地跟踪healthd行为的方法,并揭示出healthd是通过sysfs伪文件系统中的哪些伪文件来获取电池电量使用情况的。

hammerhead:/ # ps | grep "healthd"
root      188   1     20424  17368 sys_epoll_ 00059334 S /sbin/healthd
hammerhead:/ # str
strace   strings
hammerhead:/ # strace -p 188
strace: Process 188 attached
epoll_pwait(3, [{EPOLLIN, {u32=36381, u64=4776546117586292253}}], 2, -1, NULL, 8) = 1
recvmsg(7, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"online@/devices/system/cpu/cpu1\0"..., 2048}], msg_controllen=24, [{cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS, {pid=0, uid=0, gid=0}}], msg_flags=0}, 0) = 105
epoll_pwait(3, [{EPOLLIN, {u32=36381, u64=4776546117586292253}}], 2, -1, NULL, 8) = 1
recvmsg(7, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"online@/devices/system/cpu/cpu2\0"..., 2048}], msg_controllen=24, [{cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS, {pid=0, uid=0, gid=0}}], msg_flags=0}, 0) = 105
epoll_pwait(3, [{EPOLLIN, {u32=36381, u64=4776546117586292253}}], 2, -1, NULL, 8) = 1
recvmsg(7, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"online@/devices/system/cpu/cpu3\0"..., 2048}], msg_controllen=24, [{cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS, {pid=0, uid=0, gid=0}}], msg_flags=0}, 0) = 105
epoll_pwait(3, [{EPOLLIN, {u32=36381, u64=4776546117586292253}}], 2, -1, NULL, 8) = 1
recvmsg(7, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"offline@/devices/system/cpu/cpu3"..., 2048}], msg_controllen=24, [{cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS, {pid=0, uid=0, gid=0}}], msg_flags=0}, 0) = 107
epoll_pwait(3, [{EPOLLIN, {u32=36381, u64=4776546117586292253}}], 2, -1, NULL, 8) = 1
recvmsg(7, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"offline@/devices/system/cpu/cpu2"..., 2048}], msg_controllen=24, [{cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS, {pid=0, uid=0, gid=0}}], msg_flags=0}, 0) = 107
epoll_pwait(3, [{EPOLLIN, {u32=36381, u64=4776546117586292253}}], 2, -1, NULL, 8) = 1
recvmsg(7, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"offline@/devices/system/cpu/cpu1"..., 2048}], msg_controllen=24, [{cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS, {pid=0, uid=0, gid=0}}], msg_flags=0}, 0) = 107
epoll_pwait(3, [{EPOLLIN, {u32=36381, u64=4776546117586292253}}], 2, -1, NULL, 8) = 1
recvmsg(7, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"online@/devices/system/cpu/cpu1\0"..., 2048}], msg_controllen=24, [{cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS, {pid=0, uid=0, gid=0}}], msg_flags=0}, 0) = 105
epoll_pwait(3, [{EPOLLIN, {u32=36381, u64=4776546117586292253}}], 2, -1, NULL, 8) = 1

    sysfs中的伪文件(/sys/class/power_supply/*)的名称都是标准的一一事实上,它们都是指向特定的平台设备节点的符号文件,而这些设备节,点的名称,在不同的设备上可能是各不相同的。其中,保存有系统当前关于power_supply的各种状态。

hammerhead:/sys/class/power_supply # ls -al
total 0
drwxr-xr-x  2 root root 0 1970-04-22 10:55 .
drwxr-xr-x 60 root root 0 1970-04-22 10:55 ..
lrwxrwxrwx  1 root root 0 1970-04-22 10:55 ac -> ../../devices/f9923000.i2c/i2c-84/84-006b/power_supply/ac
lrwxrwxrwx  1 root root 0 1970-04-22 10:55 batt_therm -> ../../devices/battery_tm_ctrl.78/power_supply/batt_therm
lrwxrwxrwx  1 root root 0 1970-04-22 10:55 battery -> ../../devices/f9923000.i2c/i2c-84/84-0036/power_supply/battery
lrwxrwxrwx  1 root root 0 1970-04-22 10:55 touch -> ../../devices/virtual/power_supply/touch
lrwxrwxrwx  1 root root 0 1970-04-22 10:55 usb -> ../../devices/msm_dwc3/power_supply/usb
lrwxrwxrwx  1 root root 0 1970-04-22 10:55 wireless -> ../../devices/bq51013b_wlc.77/power_supply/wireless

3 被用作charger守护进程的healthd

    在Android 5之前的系统中,有一个特殊的守护进程charger,如果系统检测出自己正在充电模式下启动,那么init进程就会启动这个守护进程(通过class_start charger 指令,仅包含一个单独的服务)。

    在Android 5之后的系统中,charger己经被合并到healthd里去了,因为healthd的主要任务就是监视电池电量的相关状态,在以charger模式运行时,healthd会以一种类似init的“分身” (watchdog和ueventd)的形式运行。 在源码目录:~/LineageOS/system/core/rootdir/init.rc中:

# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
    class_stop charger
    trigger late-init

on charger class_start charger

    在源码目录:~/LineageOS/device/lge/hammerhead/init.hammerhead.rc中:

service charger /sbin/healthd -c
    class charger
    critical
    seclabel u:r:healthd:s0

    换而言之,/charger现在只不过是/sbin/healthd的一个符号链接,只不过是用一个-c参数启动。 可以查看系统根目录:

hammerhead:/ $ ls charger -al
lrwxrwxrwx 1 root root 13 1969-12-31 13:00 charger -> /sbin/healthd

    charger守护进程负责在设备充电时,把电池电量信息以图形的形式显示给用户看,如下图所示:

                                    

    healthd在Android中扮演一个更为重要的角色。它的重要性可以体现在它是为数不多的几个被放在root文件系统中的守护进程之一(healthd位于/sbin目录中,而不像其他大多数守护进程那样位于/system/bin目录中)。

4 后续的

    后续如果工作中涉及到它时再分析吧,就写这些... Enjoy it!

本文由【小馬佩德罗】发布于开源中国,原文链接:https://my.oschina.net/XiaoMaPedro/blog/3061490

全部评论: 0

    我有话说: