ANDROID L日志系统——JAVAAPI与LIBLOG

来源:互联网 发布:游戏制作编程 编辑:程序博客网 时间:2024/06/03 11:14
在 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 stream0666 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.cpp release(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