Android LOG系统源码解析(一)
在学习老罗的《Android系统源码情景分析》中发现其中的代码版本实在是有点老,和现在版本差别较大所以基于android 11重新梳理了一下。
旧版本差异
删除了logger驱动新增了logd来实现日志系统。源码路径为system/core/logd
log系统启动and初始化
看到路径下面有logd.rc文件可以想到logd应该是在系统启动的时候init进程通过解析init.rc文件启动的,通过查看init.rc文件也确实如此。
# system/core/rootdir/init.rc
on init
# Start logd before any other services run to ensure we capture all of their logs.
start logd
# Start lmkd before any other services run so that it can register them
start lmkd
# Start essential services.
start servicemanager
start hwservicemanager
start vndservicemanager
接下来就看下logd.rc文件中启动了什么
# 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
capabilities SYSLOG AUDIT_CONTROL
priority 10
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
# Limit SELinux denial generation to 5/second
service logd-auditctl /system/bin/auditctl -r 5
oneshot
disabled
user logd
group logd
capabilities AUDIT_CONTROL
从service可以看到他干了下面几件事
- 启动放在system/bin下面的logd
- 启动三个socket,logd,logw,logr
- 生成两个文件 /proc/kmsg 和 /dev/kmsg
- 启动logd-reinit,设置了oneshot所以只会调用一次
- 启动logd-auditctl
启动logd
看main方法,其实代码中注释写的也很详细。在main方法中干了几件事
- 打开/dev/kmsg 文件拿到fd
- 根据系统属性ro.logd.kernel 判断,如果为true就打开/proc/kmsg文件拿到fd
- 启动reinit线程,当logd-reinit传入参数reinit时,进行调用
- 创建LogBuffer,这个对象保存了所有的日志,但是初始化并不是这里
- 启动每个socket的监听器
- LogReader监听/dev/socket/logdr,当有客户端连接的时候,发送LogBuffer中的log
- LogListener监听/dev/socket/logdw,有日志写入的时候,讲数据写如LogBuff
- CommandListener监听/dev/socket/logd,负责监听给logd的指令并处理
- 如果配置了ro.logd.auditd 为ture会启动LogAudit,主要负责selinux相关的日志
- 如果配置了ro.logd.kernel 为true会启动LogKlog,主要负责收集内核相关的日志
下面是源码:
int main(int argc, char* argv[]) {
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) {
static const char proc_kmsg[] = "/proc/kmsg";
fdPmesg = android_get_control_file(proc_kmsg);
if (fdPmesg < 0) {
fdPmesg = TEMP_FAILURE_RETRY(
open(proc_kmsg, O_RDONLY | O_NDELAY | O_CLOEXEC));
}
if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
}
bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
if (DropPrivs(klogd, auditd) != 0) {
return EXIT_FAILURE;
}
// Reinit Thread
sem_init(&reinit, 0, 0);
pthread_attr_t attr;
if (!pthread_attr_init(&attr)) {
struct sched_param param;
memset(¶m, 0, sizeof(param));
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
pthread_t thread;
reinit_running = true;
if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
reinit_running = false;
}
}
pthread_attr_destroy(&attr);
}
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
// entries.
LastLogTimes* times = new LastLogTimes();
// LogBuffer is the object which is responsible for holding all
// log entries.
logBuf = new LogBuffer(times);
signal(SIGHUP, reinit_signal_handler);
if (__android_logger_property_get_bool(
"logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
BOOL_DEFAULT_FLAG_ENG |
BOOL_DEFAULT_FLAG_SVELTE)) {
logBuf->enableStatistics();
}
// LogReader listens on /dev/socket/logdr. When a client
// connects, log entries in the LogBuffer are written to the client.
LogReader* reader = new LogReader(logBuf);
if (reader->startListener()) {
return EXIT_FAILURE;
}
// LogListener listens on /dev/socket/logdw for client
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
LogListener* swl = new LogListener(logBuf, reader);
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
if (swl->startListener(600)) {
return EXIT_FAILURE;
}
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
CommandListener* cl = new CommandListener(logBuf, reader, swl);
if (cl->startListener()) {
return EXIT_FAILURE;
}
// LogAudit listens on NETLINK_AUDIT socket for selinux
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
LogAudit* al = nullptr;
if (auditd) {
al = new LogAudit(logBuf, reader,
__android_logger_property_get_bool(
"ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
? fdDmesg
: -1);
}
LogKlog* kl = nullptr;
if (klogd) {
kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
}
readDmesg(al, kl);
if (kl && kl->startListener()) {
delete kl;
}
if (al && al->startListener()) {
delete al;
}
TEMP_FAILURE_RETRY(pause());
return EXIT_SUCCESS;
}
初始化LogBuff
在启动logd的时候启动了reinit线程pthread_create(&thread, &attr, reinit_thread_start, nullptr)
,等待reinit的信号执行,在logd.rc中可以知道logd启动结束之后会启动 logd-reinit,同样是logd的main方法,但是这时候会传入–reinit参数调用到issueReinit
,发送reinit指令使得reinit_thread_start
往下执行,在这里会执行LogBuff的初始化。
static void* reinit_thread_start(void* /*obj*/) {
prctl(PR_SET_NAME, "logd.daemon");
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
if (fdDmesg >= 0) {
static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
'l',
'o',
'g',
'd',
'.',
'd',
'a',
'e',
'm',
'o',
'n',
':',
' ',
'r',
'e',
'i',
'n',
'i',
't',
'\n' };
write(fdDmesg, reinit_message, sizeof(reinit_message));
}
// Anything that reads persist.<property>
if (logBuf) {
logBuf->init();
logBuf->initPrune(nullptr);
}
android::ReReadEventLogTags();
}
return nullptr;
}
启动logd-auditctl
logd-auditctl的作用主要是限制selinux日志写入频率更新为5秒,这个日常开发的时候用到不多。到这里相关服务就都启动了。
监听启动
这里额外说一下监听启动和接受的方法。在代码中监听启动都是调用的startListener()
,这个方法来自于 SocketListener ,LogReader 和LogListener 都继承了SocketListener 。
方法的调用链为
startListener()
->threadStart(void *obj)
->runListener()
# system/core/libsysutils/src/SocketListener.cpp
int SocketListener::startListener(int backlog) {
if (mListen && listen(mSock, backlog) < 0) {
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
if (pipe2(mCtrlPipe, O_CLOEXEC)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}
if (pthread_create(&mThread, nullptr, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
return 0;
}
void SocketListener::runListener() {
while (true) {
for (SocketClient* c : pending) {
// Process it, if false is returned, remove from the map
SLOGV("processing fd %d", c->getSocket());
if (!onDataAvailable(c)) {
release(c, false);
}
c->decRef();
}
}
}
代码比较长只展示了核心的代码,主要功能就是两个
- 通过名字获取socket,创建socketclient
- 开启线程,在线程中用while(true)循环获取数据,通过onDataAvailable() 来处理数据。子类通过重写onDataAvailable来实现各自的功能,所以看LogReader 和LogListener 的处理时就可以直接看onDataAvailable中的实现。