在 Android L(包含Android L)之后,Andoird使用了全新的日志系统,也非之前结合Kernel Ring Buffer的方式来存储,读写Log。替而代之是使用新的日志机制Logd。所以说,在/dev/log/下面创建的几个设备,根本就没有使用!没有使用!
其实,init在创建它们的时候,就有说明,只是没有注意到了。
INFO(“kernel logger is deprecatedn”);
就来分析Android L的日志系统。
从上一篇文章《Kernel的环形Buffer(Ring Buffer)——以Logger Buffer为例》分析可知,Android系统的Log都是用一个环形buffer来存储管理的,换成Logd之后,应该也是通过Ring Buffer来管理,只是由Kernel空间,改成用户空间。那么现在就来看看用户层是如何,往这个buffer中写Log,以及从这个buffer中读出来Log。
在Java层写APP时,一般都会调用android.util.Log这个包的一些静态方式来打印Log;
  1. java.lang.Object
  2. ↳ android.util.Log
  3. API for sending log output.
  4. Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e() methods.
分析Log.java,Log.v() Log.d() Log.i等等最终都调用到

  1. public static int v(String tag, String msg) {
  2. return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
  3. }
=》通过JNI调用android_util_Log.cpp

  1. { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
  2. ...>
  3. static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
  4. jint bufID, jint priority, jstring tagObj, jstring msgObj)
  5. {
  6.     //先判断ID是否是一个合法LOG_ID_MAX,这个变量定义在system/下面的Log.h里面  
  7. if (bufID < 0 || bufID >= LOG_ID_MAX) {
  8. jniThrowNullPointerException(env, "bad bufID");
  9. return -1;
  10. }
  11. //取出来TAG
  12. if (tagObj != NULL)
  13. tag = env->GetStringUTFChars(tagObj, NULL);
  14. //取出要写入的Message
  15.      msg = env->GetStringUTFChars(msgObj, NULL);
  16. //调用__android_log_buf_write来写入到buffer
  17. int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
  18. if (tag != NULL)
  19. env->ReleaseStringUTFChars(tagObj, tag);
  20. env->ReleaseStringUTFChars(msgObj, msg);
  21. return res;
  22. }
__android_log_buf_write是在liblog中实现的。

在liblog中,会通过sokect通讯把要写入log交给logd去处理,大致流程如下:

下一节就来讨论logd的实现。

1,在系统启动到init处理的时候,会解析init.rc启动logd service如下:

  1. service logd /system/bin/logd
  2. class core
  3. socket logd stream 0666 logd logd
  4. socket logdr seqpacket 0666 logd logd
  5. socket logdw dgram 0222 logd logd
  6. group root system
同时会创建和初始化3个socket::logd, logdr, logdw。分别是用来监听命令,处理读log,和处理写log。
socket logd stream 0666 logd logd
在init中解析socket的处理如下:
service_start(struct service *svc, const char *dynamic_args)@init.cpp
  1. for (si = svc->sockets; si; si = si->next) {  
  2. //读取socket类型,stream或者dgram
  3. int socket_type = (
  4. !strcmp(si->type, "stream") ? SOCK_STREAM :
  5. (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
  6. //创建socket
  7. int s = create_socket(si->name, socket_type,
  8. si->perm, si->uid, si->gid, si->socketcon ?: scon);
  9. if (s >= 0) {
  10. //发布socket,把创建的socketFd写到环境变量,让其它Sokect的Server端通过android_get_control_socket(mSocketName)来获得socketFd.
  11. publish_socket(si->name, s);
  12. }
  13. }
核心是create_socket,来看这里的实现,代码位于init/util.cpp
  1. int create_socket(const char *name, int type, mode_t perm, uid_t uid,
  2. gid_t gid, const char *socketcon)
  3. {
  4. struct sockaddr_un addr;
  5. int fd, ret;
  6. char *filecon;
  7. //调用系统调用socket来创建一个PF_UNIX的socket
  8. fd = socket(PF_UNIX, type, 0);
  9. addr.sun_family = AF_UNIX;
  10. snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
  11. name);
  12. //把这个socket绑定到addr上,这个addr就与/dev/socket/*有关了
  13. ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
这个init基本上就把Socket的Server端的初始化工作准备好了。
2,logd启动之后,会获得相应的socket,并监听socket。
以logdw为例,main()#logd/main.cpp
  1. // LogListener listens on /dev/socket/logdw for client
  2. // initiated log messages. New log entries are added to LogBuffer
  3. // and LogReader is notified to send updates to connected clients.
  4. LogListener *swl = new LogListener(logBuf, reader);
  5. // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
  6. if (swl->startListener(300)) {
  7. exit(1);
  8. }
LogListener继承成SocketListener,而startListener正是其父类SocketListener的方法。
先看New LogListener(LogBuf, reader)
  1. LogListener::LogListener(LogBuffer *buf, LogReader *reader) : 
  2. //同时会构造一个父类SocketListener,getLogSocket()是通过logdw这个名字返回一个SocketFd
  3. SocketListener(getLogSocket(), false),
  4. //把两个结构体传过来
  5. logbuf(buf),
  6. reader(reader) {
  7. }
接下来看SocketListener的构造函数,也就是把相关参数传过来进行赋值传递。
SocketListener.cpp
  1. SocketListener::SocketListener(int socketFd, bool listen) {
  2. init(NULL, socketFd, listen, false);
  3. }
  4. =》
  5. void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
  6. mListen = listen;
  7. mSocketName = socketName;
  8. mSock = socketFd;
  9. mUseCmdNum = useCmdNum;
  10. pthread_mutex_init(&mClientsLock, NULL);
  11. mClients = new SocketClientCollection();
  12. }
再回到上面,logd/main.cpp中main()。创建完LogListener,紧接着就swl->startListener(300);这个startListener直接由SocketListener实现,我们直接来看SocketListener.cpp
  1. int SocketListener::startListener(int backlog) {
  2. if (!mSocketName && mSock == -1) {
  3. ...
  4. //在构造中mSocketName已经传过来了
  5. } else if (mSocketName) {
  6. //获得SocketFd
  7. if ((mSock = android_get_control_socket(mSocketName)) < 0) {
  8. ...
  9. }
  10. SLOGV("got mSock = %d for %s", mSock, mSocketName);
  11. fcntl(mSock, F_SETFD, FD_CLOEXEC);
  12. }
  13. //调用listen的系统调用,监听SocketFd。此时mListen为NULL,应该不会调用listen??TODO,有编译器有关??
  14. if (mListen && listen(mSock, backlog) < 0) {
  15. ...
  16. } else if (!mListen)
  17. //创建SocketClient,并放到mClients的,mClients是存储所有SocketClient的List容器。
  18. mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
  19. ...
  20. //创建PID为mThread的线程,线程执行函数是thradStart,并启动 。 
  21. if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
  22. SLOGE("pthread_create (%s)", strerror(errno));
  23. return -1;
  24. }
  25. return 0;
  26. }
来看thread执行函数threadStart
  1. void *SocketListener::threadStart(void *obj) {
  2. SocketListener *me = reinterpret_cast<SocketListener *>(obj);
  3. me->runListener();
  4. pthread_exit(NULL);
  5. return NULL;
  6. }
runListener有点长,主要做了以下几个事情。
  1. void SocketListener::runListener() {
  2. ...
  3. rc = select(max + 1, &read_fds, NULL, NULL, NULL);
  4. ...
  5. c = accept(mSock, &addr, &alen);
  6. ...
  7. /* Process the pending list, since it is owned by the thread,* there is no need to lock it */while (!pendingList.empty()) {/* Pop the first item from the list */it = pendingList.begin();SocketClient* c = *it;pendingList.erase(it);/* Process it, if false is returned, remove from list */if (!onDataAvailable(c)) {//这个数据处理函数,由继承SocketListener的类来实现,在这里就是指LogListener.cpprelease(c, false);}c->decRef();}
这些都是UNIX线程通信的系统调用。这样Socket的Server就准备好了。
总结一下,在unix Socket通信中Server端一般有以下几个步骤
The steps involved in establishing a socket on the server side are as follows:
1,Create a socket with the socket() system call
2,Bind the socket to an address using the bind() system call. For a server socket on the Internet, an address consists of a port number on the host machine.
3,Listen for connections with the listen() system call
4,Accept a connection with the accept() system call. This call typically blocks until a client connects with the server.
Send and receive data
对于logdw,1,2步骤在init里面完成,3,4步是LogListener的父类SocketListener里面完成。
3,Logdw是如何处理来自liblog的请求处理的。
在第2小节中,具体的数据处理是由onDataAvailable()完成,这个函数是LogListener.cpp来实现,
第1步,读取数据,并存在Socket定义的MSG相关结构体内
  1. char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + LOGGER_ENTRY_MAX_PAYLOAD];
  2. //定义iov用于接收Client的writerv的内容。即一条LOG会在在buffer中
  3. struct iovec iov = { buffer, sizeof(buffer) };
  4. memset(buffer, 0, sizeof(buffer));
  5. //存放Client的进程信息
  6. char control[CMSG_SPACE(sizeof(struct ucred))];
  7. struct msghdr hdr = {
  8. NULL,
  9. 0,
  10. &iov,//真正存放LOG message
  11. 1,
  12. control,
  13. sizeof(control),
  14. 0,
  15. };
  16. int socket = cli->getSocket();
  17. //通过系统调用 把Client传过来的socket数据存放在hdr这个结构体中。
  18. ssize_t n = recvmsg(socket, &hdr, 0);
这里有必要说一下msghdr这个结构体:
msghdr是用于Socket在两个进程之间通讯定义的消息头
  1. struct msghdr {
  2. void *msg_name; /* optional address */
  3. socklen_t msg_namelen; /* size of address */
  4. struct iovec *msg_iov; /* scatter/gather array */
  5. size_t msg_iovlen; /* # elements in msg_iov */
  6. void *msg_control; /* ancillary data, see below */
  7. size_t msg_controllen; /* ancillary data buffer len */
  8. int msg_flags; /* flags on received message */
  9. };
msg_control:是一个指向cmsghdr 结构体的指针,
  1. struct cmsghdr {
  2. socklen_t cmsg_len; /* data byte count, including header */
  3. int cmsg_level; /* originating protocol */
  4. int cmsg_type; /* protocol-specific type */
  5. /* followed by unsigned char cmsg_data[]; */
  6. };
msg_controllen :参见下图,即cmsghdr 结构体可能不止一个;
对于CMSG在LogListener.cpp里面是control变量,char control[CMSG_SPACE(sizeof(struct ucred))];也就是说CMSG是存放Client的PID,UID,GID信息的。
  1. struct ucred {
  2. pid_t pid; /* process ID of the sending process */
  3. uid_t uid; /* user ID of the sending process */
  4. gid_t gid; /* group ID of the sending process */
  5. };
第2步,解析CMSG里面进程相关信息,并检查权限
  1. struct ucred *cred = NULL;
  2. struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
  3. while (cmsg != NULL) {
  4. if (cmsg->cmsg_level == SOL_SOCKET
  5. && cmsg->cmsg_type == SCM_CREDENTIALS) {
  6. cred = (struct ucred *)CMSG_DATA(cmsg);
  7. break;
  8. }
  9. cmsg = CMSG_NXTHDR(&hdr, cmsg);
  10. }
  11. if (cred == NULL) {
  12. return false;
  13. }
  14. //检查进程的权限
  15. if (cred->uid == AID_LOGD) {
  16. // ignore log messages we send to ourself.
  17. // Such log messages are often generated by libraries we depend on
  18. // which use standard Android logging.
  19. return false;
  20. }
第3步,处理真正的Log信息,从第1步可以知道,Log信息是存放在iov指向的buffer里面,即对buffer处理就是处理Log信息
  1. android_log_header_t *header = reinterpret_cast<android_log_header_t *>(buffer);
  2. if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX || header->id == LOG_ID_KERNEL) {
  3. return false;
  4. }
  5. char *msg = ((char *)buffer) + sizeof(android_log_header_t);
  6. n -= sizeof(android_log_header_t);
  7. // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
  8. // truncated message to the logs.
  9. if (logbuf->log((log_id_t)header->id, header->realtime,
  10. cred->uid, cred->pid, header->tid, msg,
  11. ((size_t) n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX) >= 0) {
  12. reader->notifyNewLog();
  13. }
  14. return true;
首先调用 logbuf->log()创建一条Log,然后调用reader->nofifyNewLog()把Log存储到buffer中。
至logd的实现,基本上分析完成。关于LogBuffer和LogReader,读者可以自己深入分析。
msghdr部分参考了:http://blog.csdn.net/jnu_simba/article/details/9079627

ANDROID L日志系统——JAVAAPI与LIBLOG相关推荐

  1. Android L日志系统1——logd

    在介绍完Android M之前的日志系统的实现之后,我们现在来看看现在最新的Android L的日志机制.Android L与之前版本最大的变化,就是日志保存的位置由Kernel的Ringer Buf ...

  2. Android动态日志系统Holmes

    背景 美团点评公司是全球领先的一站式生活服务平台,为6亿多消费者和超过450万优质商户提供连接线上线下的电子商务网络.美团点评的业务覆盖了超过200个丰富品类和2800个城区县网络,在餐饮.外卖.酒店 ...

  3. 以Android L读取系统所有logcat并写入文件为例分析Android 以添加系统进程的方式申请selinux的权限执行shell脚本,以及avc:dined应该怎么申请权限

    添加selinux较好的文章,可以通过adb shell dmesg > kenel.log 获取kmesg 可以看到 avc:dined 相关内容 首先说说环境: 基于Android L的ao ...

  4. Android Log日志系统

    目录 0. 前言 1. Native的Log写过程解析 2. Socket的另一端Logd 0. 前言 Android中 logd 详解_私房菜的博客-CSDN博客_logd 里面讲了Java层Log ...

  5. Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化-[Android取经之路]

    摘要:本节主要来讲解Android10.0 日志系统的架构分析,以及logd.logcat的初始化操作 阅读本文大约需要花费15分钟. 文章首发微信公众号:IngresGe 专注于Android系统级 ...

  6. Android日志系统Logcat源代码简要分析

    在前面两篇文章Android日志系统驱动程序Logger源代码分析和Android应用程序框架层和系统运行库层日志系统源代码中,介绍了Android内核空间层.系统运行库层和应用程序框架层日志系统相关 ...

  7. Android源码解析之日志系统Logcat

    转载自:http://blog.csdn.net/Luoshengyang/article/details/6606957 在前面两篇文章Android日志系统驱动程序Logger源代码分析和Andr ...

  8. android界面赏析,Android L的UI赏析 部分比苹果iOS 8更赏心悦目

    [摘要]Material Design拥有各种交互及视觉上的变化,比苹果的iOS 8更加赏心悦目. 腾讯数码讯(编译:Lotus) 如果你是一个狂热的Android粉丝,那么在未来几个月中一定会发现很 ...

  9. android core log,Android 日志系统(Logcat)的实现分析

    这篇说一下Android 日志系统的实现: 1. Android中的打印分为4个缓冲区和6个打印等级,在frameworks\base\core\java\android\util\Log.java中 ...

最新文章

  1. 修改及查看mysql数据库的字符集
  2. SVA Function Coverage
  3. List类集接口-ArrayList
  4. Python 的and 运算
  5. ssh 与 locale
  6. DirectX_11_游戏编程入门_1
  7. NB-IoT的DRX、eDRX、PSM三个模式怎么用?
  8. android返回键返回指定目录,Android 返回键返回到指定的Activity
  9. plt,cv2图片像素值的立体显示
  10. centos 雷凌凌ralink无线网卡驱动 安装
  11. 习题8.16 (简单方法)输入一个字符串,内有数字和非数字字符
  12. Neon Love(霓虹爱)
  13. 大厂团队协作工具推荐
  14. 基于Java和Netty实现的联机版坦克大战游戏
  15. C++中的FILL和MEMSET(zzl)
  16. VS code 设置多行注释快捷键
  17. DeFi:过去、现在和未来
  18. Arduino UNO驱动MCP9808高精度数字温度传感器
  19. Qt串口等接口数据协议传输时的字节拼接处理
  20. 英语语法最终珍藏版笔记-14独立主格结构

热门文章

  1. Jsoup之提交url
  2. typedefnbsp;struct与struct的区别
  3. 面试中回答关于oracle数据库优化的方法
  4. Android Service与Runnable整合并用
  5. 重写StyleSheetTheme
  6. 安卓手机Charles抓包显示unknown原因及解决办法
  7. 感受野receptive field个人理解
  8. Linux 下从命令行打开pdf文件和html文件的命令
  9. 二十万字C/C++、嵌入式软开面试题全集宝典七
  10. 科大星云诗社动态20210310