为了跟老罗的书保持一个比较一致的步伐,这一篇开始我们来看 logd 的实现。当然,这个 logd 不是老罗书里讲的 log 驱动,而是在应用层实现的一个守护进程。
在进入正题之前先说明一下,logd 虽然是用 C++ 写的,但由于比较接近系统,需要读者对系统编程有一定的了解。不熟悉的读者可以通过《Linux系统编程》快速入个门,《UNIX环境高级程序设计》则是关于这一主题最好的书籍。
logd 的启动
通过查看 logd 源码目录,我们可以看到这样一个文件:
// system/core/logd/logd.rc
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram+passcred 0222 logd logd
file /proc/kmsg r
file /dev/kmsg w
user logd
group logd system package_info readproc
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
oneshot
disabled
user logd
group logd
writepid /dev/cpuset/system-background/tasks
on fs
write /dev/event-log-tags "# content owned by logd
"
chown logd logd /dev/event-log-tags
chmod 0644 /dev/event-log-tags
init 进程是在 post-fs 阶段启动 logd 的
// system/core/rootdir/init.rc
on post-fs
# Load properties from
# /system/build.prop,
# /odm/build.prop,
# /vendor/build.prop and
# /factory/factory.prop
load_system_props
# start essential services
start logd
start servicemanager
start hwservicemanager
start vndservicemanage
从这里我们可以得出几个信息:
- logd 是经由 init 进程启动的
- init 进程为 logd 创建了 3 个(UNIX 域)socket,分别是
/dev/socket/logd, /dev/socket/logdr, /dev/socket/logdw
- init 进程为 logd 打开了两个文件
/proc/kmsg, /dev/kmsg
- 把 logd 的 uid 设置为 logd,gid 设置为 logd、system、package_info 和 readproc
- 把 logd 进程的 pid 写到文件 /dev/cpuset/system-background/tasks
关于 socket 的相关知识,读者可以参考《UNIX 网络编程,卷1》。
logd-reinit 用来触发 logd 的重新初始化,同样执行的是 logd 程序,只是多了一个参数 --init
。后面我们讲 logd 的控制命令时再详细说。
至于 init 进程如何解析 init.rc,以后有机会写 init 进程相关文章的时候再讨论。
logd 的初始化
init 进程启动 logd 后,接下来执行的自然是 logd 的 main
函数。这个函数有点长,这里先把代码放上来,后面再一点点慢慢看。
// system/core/logd/main.cpp
// Foreground waits for exit of the main persistent threads
// that are started here. The threads are created to manage
// UNIX domain client sockets for writing, reading and
// controlling the user space logger, and for any additional
// logging plugins like auditd and restart control. Additional
// transitory per-client threads are created for each reader.
int main(int argc, char* argv[]) {
// logd is written under the assumption that the timezone is UTC.
// If TZ is not set, persist.sys.timezone is looked up in some time utility
// libc functions, including mktime. It confuses the logd time handling,
// so here explicitly set TZ to UTC, which overrides the property.
setenv("TZ", "UTC", 1);
// issue reinit command. KISS argument parsing.
if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
return issueReinit();
}
static const char dev_kmsg[] = "/dev/kmsg";
fdDmesg = android_get_control_file(dev_kmsg);
if (fdDmesg < 0) {
fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));
}
int fdPmesg = -1;
bool klogd = __android_logger_property_get_bool(
"ro.logd.kernel",
BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
if (klogd) {