日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

ANDROID L日志系统——JAVAAPI与LIBLOG

發布時間:2025/3/15 windows 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ANDROID L日志系统——JAVAAPI与LIBLOG 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在 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;
  • java.lang.Object
  • ? android.util.Log
  • API for sending log output.
  • Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e() methods.
  • 分析Log.java,Log.v() Log.d() Log.i等等最終都調用到
  • public static int v(String tag, String msg) {
  • return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
  • }
  • =》通過JNI調用android_util_Log.cpp
  • { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
  • ...>
  • static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
  • jint bufID, jint priority, jstring tagObj, jstring msgObj)
  • {
  • ????//先判斷ID是否是一個合法LOG_ID_MAX,這個變量定義在system/下面的Log.h里面 ?
  • if (bufID < 0 || bufID >= LOG_ID_MAX) {
  • jniThrowNullPointerException(env, "bad bufID");
  • return -1;
  • }
  • //取出來TAG
  • if (tagObj != NULL)
  • tag = env->GetStringUTFChars(tagObj, NULL);
  • //取出要寫入的Message
  • ???? msg = env->GetStringUTFChars(msgObj, NULL);
  • //調用__android_log_buf_write來寫入到buffer
  • int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
  • if (tag != NULL)
  • env->ReleaseStringUTFChars(tagObj, tag);
  • env->ReleaseStringUTFChars(msgObj, msg);
  • return res;
  • }
  • __android_log_buf_write是在liblog中實現的。 在liblog中,會通過sokect通訊把要寫入log交給logd去處理,大致流程如下:

    下一節就來討論logd的實現。


    1,在系統啟動到init處理的時候,會解析init.rc啟動logd service如下:

  • service logd /system/bin/logd
  • class core
  • socket logd stream 0666 logd logd
  • socket logdr seqpacket 0666 logd logd
  • socket logdw dgram 0222 logd logd
  • 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
  • for (si = svc->sockets; si; si = si->next) { ?
  • //讀取socket類型,stream或者dgram
  • int socket_type = (
  • !strcmp(si->type, "stream") ? SOCK_STREAM :
  • (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
  • //創建socket
  • int s = create_socket(si->name, socket_type,
  • si->perm, si->uid, si->gid, si->socketcon ?: scon);
  • if (s >= 0) {
  • //發布socket,把創建的socketFd寫到環境變量,讓其它Sokect的Server端通過android_get_control_socket(mSocketName)來獲得socketFd.
  • publish_socket(si->name, s);
  • }
  • }
  • 核心是create_socket,來看這里的實現,代碼位于init/util.cpp
  • int create_socket(const char *name, int type, mode_t perm, uid_t uid,
  • gid_t gid, const char *socketcon)
  • {
  • struct sockaddr_un addr;
  • int fd, ret;
  • char *filecon;
  • //調用系統調用socket來創建一個PF_UNIX的socket
  • fd = socket(PF_UNIX, type, 0);
  • addr.sun_family = AF_UNIX;
  • snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
  • name);
  • //把這個socket綁定到addr上,這個addr就與/dev/socket/*有關了
  • ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
  • 這個init基本上就把Socket的Server端的初始化工作準備好了。 2,logd啟動之后,會獲得相應的socket,并監聽socket。 以logdw為例,main()#logd/main.cpp
  • // 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(300)) {
  • exit(1);
  • }
  • LogListener繼承成SocketListener,而startListener正是其父類SocketListener的方法。 先看New LogListener(LogBuf, reader)
  • LogListener::LogListener(LogBuffer *buf, LogReader *reader) :?
  • //同時會構造一個父類SocketListener,getLogSocket()是通過logdw這個名字返回一個SocketFd
  • SocketListener(getLogSocket(), false),
  • //把兩個結構體傳過來
  • logbuf(buf),
  • reader(reader) {
  • }
  • 接下來看SocketListener的構造函數,也就是把相關參數傳過來進行賦值傳遞。 SocketListener.cpp
  • SocketListener::SocketListener(int socketFd, bool listen) {
  • init(NULL, socketFd, listen, false);
  • }
  • =》
  • void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
  • mListen = listen;
  • mSocketName = socketName;
  • mSock = socketFd;
  • mUseCmdNum = useCmdNum;
  • pthread_mutex_init(&mClientsLock, NULL);
  • mClients = new SocketClientCollection();
  • }
  • 再回到上面,logd/main.cpp中main()。創建完LogListener,緊接著就swl->startListener(300);這個startListener直接由SocketListener實現,我們直接來看SocketListener.cpp
  • int SocketListener::startListener(int backlog) {
  • if (!mSocketName && mSock == -1) {
  • ...
  • //在構造中mSocketName已經傳過來了
  • } else if (mSocketName) {
  • //獲得SocketFd
  • if ((mSock = android_get_control_socket(mSocketName)) < 0) {
  • ...
  • }
  • SLOGV("got mSock = %d for %s", mSock, mSocketName);
  • fcntl(mSock, F_SETFD, FD_CLOEXEC);
  • }
  • //調用listen的系統調用,監聽SocketFd。此時mListen為NULL,應該不會調用listen??TODO,有編譯器有關??
  • if (mListen && listen(mSock, backlog) < 0) {
  • ...

  • } else if (!mListen)
  • //創建SocketClient,并放到mClients的,mClients是存儲所有SocketClient的List容器。
  • mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
  • ...
  • //創建PID為mThread的線程,線程執行函數是thradStart,并啟動 。?
  • if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
  • SLOGE("pthread_create (%s)", strerror(errno));
  • return -1;
  • }
  • return 0;
  • }
  • 來看thread執行函數threadStart
  • void *SocketListener::threadStart(void *obj) {
  • SocketListener *me = reinterpret_cast<SocketListener *>(obj);
  • me->runListener();
  • pthread_exit(NULL);
  • return NULL;
  • }
  • runListener有點長,主要做了以下幾個事情。
  • void SocketListener::runListener() {
  • ...
  • rc = select(max + 1, &read_fds, NULL, NULL, NULL);
  • ...
  • c = accept(mSock, &addr, &alen);
  • ...
  • /* 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相關結構體內
  • char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + LOGGER_ENTRY_MAX_PAYLOAD];
  • //定義iov用于接收Client的writerv的內容。即一條LOG會在在buffer中
  • struct iovec iov = { buffer, sizeof(buffer) };
  • memset(buffer, 0, sizeof(buffer));
  • //存放Client的進程信息
  • char control[CMSG_SPACE(sizeof(struct ucred))];
  • struct msghdr hdr = {
  • NULL,
  • 0,
  • &iov,//真正存放LOG message
  • 1,
  • control,
  • sizeof(control),
  • 0,
  • };
  • int socket = cli->getSocket();
  • //通過系統調用 把Client傳過來的socket數據存放在hdr這個結構體中。
  • ssize_t n = recvmsg(socket, &hdr, 0);
  • 這里有必要說一下msghdr這個結構體: msghdr是用于Socket在兩個進程之間通訊定義的消息頭
  • struct msghdr {
  • void *msg_name; /* optional address */
  • socklen_t msg_namelen; /* size of address */
  • struct iovec *msg_iov; /* scatter/gather array */
  • size_t msg_iovlen; /* # elements in msg_iov */
  • void *msg_control; /* ancillary data, see below */
  • size_t msg_controllen; /* ancillary data buffer len */
  • int msg_flags; /* flags on received message */
  • };
  • msg_control:是一個指向cmsghdr 結構體的指針,
  • struct cmsghdr {
  • socklen_t cmsg_len; /* data byte count, including header */
  • int cmsg_level; /* originating protocol */
  • int cmsg_type; /* protocol-specific type */
  • /* followed by unsigned char cmsg_data[]; */
  • };
  • msg_controllen :參見下圖,即cmsghdr 結構體可能不止一個; 對于CMSG在LogListener.cpp里面是control變量,char control[CMSG_SPACE(sizeof(struct ucred))];也就是說CMSG是存放Client的PID,UID,GID信息的。
  • struct ucred {
  • pid_t pid; /* process ID of the sending process */
  • uid_t uid; /* user ID of the sending process */
  • gid_t gid; /* group ID of the sending process */
  • };
  • 第2步,解析CMSG里面進程相關信息,并檢查權限
  • struct ucred *cred = NULL;
  • struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
  • while (cmsg != NULL) {
  • if (cmsg->cmsg_level == SOL_SOCKET
  • && cmsg->cmsg_type == SCM_CREDENTIALS) {
  • cred = (struct ucred *)CMSG_DATA(cmsg);
  • break;
  • }
  • cmsg = CMSG_NXTHDR(&hdr, cmsg);
  • }
  • if (cred == NULL) {
  • return false;
  • }
  • //檢查進程的權限

  • if (cred->uid == AID_LOGD) {
  • // ignore log messages we send to ourself.
  • // Such log messages are often generated by libraries we depend on
  • // which use standard Android logging.
  • return false;
  • }
  • 第3步,處理真正的Log信息,從第1步可以知道,Log信息是存放在iov指向的buffer里面,即對buffer處理就是處理Log信息
  • android_log_header_t *header = reinterpret_cast<android_log_header_t *>(buffer);
  • if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX || header->id == LOG_ID_KERNEL) {
  • return false;
  • }
  • char *msg = ((char *)buffer) + sizeof(android_log_header_t);
  • n -= sizeof(android_log_header_t);
  • // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
  • // truncated message to the logs.
  • if (logbuf->log((log_id_t)header->id, header->realtime,
  • cred->uid, cred->pid, header->tid, msg,
  • ((size_t) n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX) >= 0) {
  • reader->notifyNewLog();
  • }
  • 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的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 久久精品aⅴ无码中文字字幕重口 | 国产亚洲综合在线 | 成人一二三 | 久久噜噜色综合一区二区 | 波多野结衣免费观看视频 | 国产麻豆天美果冻无码视频 | 天天躁日日躁狠狠躁喷水 | 国精产品一区一区三区视频 | 精品视频在线观看 | 黄色三级小说 | 熟妇高潮一区二区三区 | 九九精品视频免费 | 少女忠诚电影高清免费 | www.欧美com| 国产伦理久久精品久久久久 | 在线日韩免费 | 秒拍福利视频 | 黄色免费在线网址 | 亚洲午夜福利在线观看 | 国产精品99一区二区三区 | 中文字幕Av日韩精品 | 成人精品亚洲 | 超碰在线网 | 一区二区成人在线观看 | jizz成熟丰满日本少妇 | 韩日午夜在线资源一区二区 | 亚洲乱亚洲乱妇 | 91久久综合精品国产丝袜蜜芽 | 欧美成人女星 | 中文在线www | www.白白色| 色哟哟在线观看视频 | 隔壁人妻偷人bd中字 | 欧美极品videos精品 | 中文字幕网站在线观看 | 欧美天天干 | 欧美国产日韩一区二区 | 一区三区视频在线观看 | 欧美日韩一区二区在线视频 | 国产资源第一页 | 中国黄色a级片 | 一级特黄bbbbb免费观看 | 午夜天堂视频 | 九热精品| 欧美日韩精品中文字幕 | 实拍澡堂美女洗澡av | 嫩草国产 | 很色的网站 | 精品女同一区二区 | 少妇无码一区二区三区免费 | 日韩欧美亚洲天堂 | 亚洲а∨天堂久久精品2021 | 香蕉视频免费网站 | 啪啪在线视频 | 欧美日韩在线免费观看视频 | 久久毛片基地 | 日本一区二区色 | 一区二区三区毛片 | 一本在线免费视频 | 亚洲aa在线 | 国产女人在线 | 99热网站| 国产美女主播在线 | 日本r级电影在线观看 | 两个女人互添下身爱爱 | 在线色网址| 久久一区二区三 | 久久超碰在线 | 白丝美女喷水 | 国产性生活一级片 | 欧美日韩中文字幕在线观看 | 你懂的日韩 | 色狠av | 日产精品久久久一区二区 | 黄色片特级 | 99热这里只有精品首页 | 欧美交换配乱吟粗大25p | 久艹在线视频 | 一本大道久久a久久精二百 琪琪色在线视频 | 日本大乳奶做爰 | 少妇一级淫片免费放2 | 日本wwww视频 | 四虎在线视频 | 五月天精品 | 天堂av免费观看 | 欧美熟妇精品黑人巨大一二三区 | 黑人巨大精品欧美黑白配亚洲 | 成人免费淫片aa视频免费 | 美女视频久久 | 色呦呦一区二区三区 | jzjzjzjzj亚洲成熟少妇 | 国产亚洲欧美一区 | 少妇人妻真实偷人精品视频 | 在线免费观看视频网站 | 日本精品视频在线播放 | 婷婷久久五月 | 色哟哟黄色 | 日本免费色视频 | a级在线免费观看 |