1. 概览
之前几章介绍了native层的LOG输出以及分析其实现,但那都是客户端,如果只有客户端组装发送数据而没有服务端进行处理的话,LOG的输出也是无法实现的。logd也就是这些客户端的服务端了,概览图见《natvie LOG 输出的实现》章节。下面就来看看 logd 的编译脚本以及它在 Android 系统中如何被启动的。
2.logd的编译 – Android.bp
AOSP T 版本中,logd已经使用 Android.bp 来描述编译了,下面是对应的文件
//system\logging\logd\Android.bp
cc_defaults {
name: "logd_defaults",
shared_libs: [
"libbase",
"libz",
],
...
}
cc_binary {
name: "logd",
defaults: ["logd_defaults"],
init_rc: ["logd.rc"],
srcs: [
...
"main.cpp",
...
],
...
}
编译完后则位于 system 分区下,其以 logd 命名。logd.rc 则会被放置到 system/etc/init 目录下,在 Android 启动的时候由 init 进程解析并执行。
值得注意的是 logd_defaults 的内容则是被继承了。源码部分 main.cpp 则包含了 logd 执行时的入口函数 main 。
/system/bin/logd
3. logd 服务定义
在 Android 中系统服务的启动,是 init 根据所给的 rc 配置文件 fork 而来的。logd同样也不例外,其对应的rc文件则是 logd.rc,定义如下
//system\logging\logd\logd.rc
service logd /system/bin/logd
#a
socket logd stream 0666 logd logd
#b
socket logdr seqpacket 0666 logd logd
#c
socket logdw dgram+passcred 0222 logd logd
#d
file /proc/kmsg r
file /dev/kmsg w
#e
onrestart setprop logd.ready false
上面rc主要做了如下几件事
a)创建socket logd,用于接收控制 logd 本身的请求。
b)创建socket logdr,用于client来读取由 logd 管理的log,例如logcat显示的log则是通过该节点获得。
c)创建socket logdw,用于client写入log,例如在java代码中使用接口Slog打印的log,这些log可以通过logcat获取并显示。
d)提前打开 kernel log相关的文件,file 命令被 init 所支持,其用于优化代码执行过程的效率,把原来程序执行所需要执行的指令放到了初始化阶段。下面是 init 中对file命令的说明,很清晰
file <path> <type>
Open a file path and pass its fd to the launched process. _type_ must be
"r", "w" or "rw". For native executables see libcutils android_get_control_file().
所以e阶段的含义是,在init启动logd的时候就打开/proc/kmsg及/dev/kmsg这两个文件,前者以仅读权限打开,后者以仅写权限打开。后续 logd 想获取对应文件的 fd 通过 libcutils 的接口(android_get_control_file)获取。
e)初始化属性,logd.ready 为 false,该属性值代表logd的功能是否初始化完成,例如,如果此属性被logd设置为true后,logcat工具以及代码中系列LOG接口就可以正常使用了。
4. logd 服务的启动
4.1 logd 的启动阶段
logd.rc 中虽然申明了logd 服务但并不会直接启动,下面是 logd 的启动点,
//system/etc/init/hw/init.rc
on init
...
# Start logd before any other services run to ensure we capture all of their logs.
start logd
可见它的启动则是位于 init.rc 中的 init 阶段,即系统初始化到init阶段时则会启动logd服务。
下面则 init 中所支持的各个启动点,用户可以在自己的rc文件中配置其合适的启动点
//file:system/core/init/README.md
Trigger Sequence
----------------
Init uses the following sequence of triggers during early boot. These are the
built-in triggers defined in init.cpp.
1. early-init
- The first in the sequence, triggered after cgroups has been configured
but before ueventd's coldboot is complete.
2. init
- Triggered after coldboot is complete.
3. charger
- Triggered if ro.bootmode == "charger".
4. late-init
- Triggered if ro.bootmode != "charger", or via healthd triggering a boot from charging mode.
5. early-fs
- Start vold.
6. fs
- Vold is up. Mount partitions not marked as first-stage or latemounted.
7. post-fs
- Configure anything dependent on early mounts.
8 late-fs
- Mount partitions marked as latemounted.
9. post-fs-data
- Mount and configure "/data";
set up encryption. "/metadata" is reformatted here if it could not mount in first-stage init.
10. zygote-start
- Start the zygote.
11. early-boot
- After zygote has started.
12. boot
- After `early-boot` actions have completed.
可见 logd 的启动还是非常的早的,因为 log 对于调试来说还是非常的重要及方便的。
4.2 logd 服务的执行入口
实际上 service 后面的内容就是服务的启动命令,和在 linux 命令行窗口启动并无二至
logd /system/bin/logd
从logd服务的启动配置来看,它是无参数启动。所以对于 main 我们是无需更新 argc、argv的。
//system\logging\logd\main.cpp
int main(int argc, char* argv[]) {
}