<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                nativePollOnce()的實現函數是android\_os\_MessageQueue\_nativePollOnce,代碼如下: **android_os_MessageQueue.cpp-->android_os_MessageQueue_nativePollOnce()** ``` static voidandroid_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jintptr, jint timeoutMillis) NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); // 取出NativeMessageQueue對象,并調用它的pollOnce nativeMessageQueue->pollOnce(timeoutMillis); } ``` 分析pollOnce函數: **android_os_MessageQueue.cpp-->NativeMessageQueue::pollOnece()** ``` void NativeMessageQueue::pollOnce(inttimeoutMillis) { mLooper->pollOnce(timeoutMillis); // 重任傳遞到Looper的pollOnce函數 } ``` Looper的pollOnce函數如下: **Looper.cpp-->Looper::pollOnce()** ``` inline int pollOnce(int timeoutMillis) { returnpollOnce(timeoutMillis, NULL, NULL, NULL); } ``` 上面的函數將調用另外一個有4個參數的pollOnce函數,這個函數的原型如下: ``` int pollOnce(int timeoutMillis, int* outFd, int*outEvents, void** outData) ``` 其中: - timeOutMillis參數為超時等待時間。如果為-1,則表示無限等待,直到有事件發生為止。如果值為0,則無需等待立即返回。 - outFd用來存儲發生事件的那個文件描述符 。 - outEvents用來存儲在該文件描述符1上發生了哪些事件,目前支持可讀、可寫、錯誤和中斷4個事件。這4個事件其實是從epoll事件轉化而來。后面我們會介紹大名鼎鼎的epoll。 - outData用于存儲上下文數據,這個上下文數據是由用戶在添加監聽句柄時傳遞的,它的作用和pthread\_create函數最后一個參數param一樣,用來傳遞用戶自定義的數據。 另外,pollOnce函數的返回值也具有特殊的意義,具體如下: - 當返回值為ALOOPER\_POLL\_WAKE時,表示這次返回是由wake函數觸發的,也就是管道寫端的那次寫事件觸發的。 - 返回值為ALOOPER\_POLL\_TIMEOUT表示等待超時。 - 返回值為ALOOPER\_POLL\_ERROR,表示等待過程中發生錯誤。 - 返回值為ALOOPER\_POLL\_CALLBACK,表示某個被監聽的句柄因某種原因被觸發。這時,outFd參數用于存儲發生事件的文件句柄,outEvents用于存儲所發生的事件。 上面這些知識是和epoll息息相關的。 * * * * * **提示** 查看Looper的代碼會發現,Looper采用了編譯選項(即#if和#else)來控制是否使用epoll作為I/O復用的控制中樞。鑒于現在大多數系統都支持epoll,這里僅討論使用epoll的情況。 * * * * * #### 1. epoll基礎知識介紹 epoll機制提供了Linux平臺上最高效的I/O復用機制,因此有必要介紹一下它的基礎知識。 從調用方法上看,epoll的用法和select/poll非常類似,其主要作用就是I/O復用,即在一個地方等待多個文件句柄的I/O事件。 下面通過一個簡單例子來分析epoll的工作流程。 ``` /* **① 使用epoll前,需要先通過epoll_create函數創建一個epoll句柄。** 下面一行代碼中的10表示該epoll句柄初次創建時候分配能容納10個fd相關信息的緩存。 對于2.6.8版本以后的內核,該值沒有實際作用,這里可以忽略。其實這個值的主要目的是 確定分配一塊多大的緩存。現在的內核都支持動態拓展這塊緩存,所以該值就沒有意義了 */ int epollHandle = epoll_create(10); /* **② 得到epoll句柄后,下一步就是通過epoll_ctl把需要監聽的文件句柄加入到epoll句柄中。** 除了指定文件句柄本身的fd值外,同時還需要指定在該fd上等待什么事件。epoll支持四類事件, 分別是EPOLLIN(句柄可讀)、EPOLLOUT(句柄可寫),EPOLLERR(句柄錯誤)、EPOLLHUP(句柄斷)。 epoll定義了一個結構體struct epoll_event來表達監聽句柄的訴求。 假設現在有一個監聽端的socket句柄listener,要把它加入到epoll句柄中 */ struct epoll_event listenEvent; //先定義一個event /* EPOLLIN表示可讀事件,EPOLLOUT表示可寫事件,另外還有EPOLLERR,EPOLLHUP表示 系統默認會將EPOLLERR加入到事件集合中 */ listenEvent.events = EPOLLIN;// 指定該句柄的可讀事件 // epoll_event中有一個聯合體叫data,用來存儲上下文數據,本例的上下文數據就是句柄自己 listenEvent.data.fd = listenEvent; /* **③** EPOLL_CTL_ADD將監聽fd和監聽事件加入到epoll句柄的等待隊列中; EPOLL_CTL_DEL將監聽fd從epoll句柄中移除; EPOLL_CTL_MOD修改監聽fd的監聽事件,例如本來只等待可讀事件,現在需要同時等待 可寫事件,那么修改listenEvent.events 為EPOLLIN|EPOLLOUT后,再傳給epoll句柄*/ epoll_ctl(epollHandle,EPOLL_CTL_ADD,listener,&listenEvent); /* 當把所有感興趣的fd都加入到epoll句柄后,就可以開始坐等感興趣的事情發生了。 為了接收所發生的事情,先定義一個epoll_event數組 */ struct epoll_event resultEvents[10]; int timeout = -1; while(1) { /* **④ 調用epoll_wait用于等待事件。**其中timeout可以指定一個超時時間, resultEvents用于接收發生的事件,10為該數組的大小。 epoll_wait函數的返回值有如下含義: nfds大于0表示所監聽的句柄上有事件發生; nfds等于0表示等待超時; nfds小于0表示等待過程中發生了錯誤*/ int nfds = epoll_wait(epollHandle,resultEvents, 10, timeout); if(nfds == -1) { // epoll_wait發生了錯誤 } else if(nfds == 0) { //發生超時,期間沒有發生任何事件 } else{ // ⑤resultEvents用于返回那些發生了事件的信息 for(int i = 0; i < nfds; i++) { struct epoll_event & event =resultEvents[i]; if(event & EPOLLIN) { /* **⑥ 收到可讀事件。**到底是哪個文件句柄發生該事件呢?可通過event.data這個聯合 體取得 前傳遞給epoll的上下文數據,該上下文信息可用于判斷到底是誰發生了事件 */ ...... } .......//其他處理 } } } ``` epoll整體使用流程如上面代碼所示,基本和select/poll類似,不過作為Linux平臺最高效的I/O復用機制,這里有些內容供讀者參考, epoll的效率為什么會比select高?其中一個原因是調用方法。每次調用select時,都需要把感興趣的事件復制到內核中,而epoll只在epll\_ctl進行加入的時候復制一次。另外,epoll內部用于保存事件的數據結構使用的是紅黑樹,查找速度很快。而select采用數組保存信息,不但一次能等待的句柄個數有限,并且查找起來速度很慢。當然,在只等待少量文件句柄時,select和epoll效率相差不是很多,但還是推薦使用epoll。 epoll等待的事件有兩種觸發條件,一個是水平觸發(EPOLLLEVEL),另外一個是邊緣觸發(EPOLLET,ET為Edge Trigger之意),這兩種觸發條件的區別非常重要。讀者可通過man epoll查閱系統提供的更為詳細的epoll機制。 最后,關于pipe,還想提出一個小問題供讀者思考討論: 為什么Android中使用pipe作為線程間通訊的方式?對于pipe的寫端寫入的數據,讀端都不感興趣,只是為了簡單的喚醒。POSIX不是也有線程間同步函數嗎?為什么要用pipe呢? 關于這個問題的答案,可參見鄧凡平的一篇博文“隨筆之如何實現一個線程池”。 - <http://www.cnblogs.com/innost/archive/2011/11/24/2261454.html> #### 2. pollOnce()函數分析 下面分析帶4個參數的pollOnce()函數,代碼如下: **Looper.cpp-->Looper::pollOnce()** ``` int Looper::pollOnce(int timeoutMillis, int*outFd, int* outEvents, void** outData) { intresult = 0; for(;;) { // 一個無限循環 // mResponses是一個Vector,這里首先需要處理response while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); ALooper_callbackFunc callback = response.request.callback; if (!callback) {// 首先處理那些沒有callback的Response int ident = response.request.ident; // ident是這個Response的id int fd = response.request.fd; int events = response.events; void* data = response.request.data; ...... if (outFd != NULL) *outFd = fd; if (outEvents != NULL) *outEvents = events; if (outData != NULL) *outData = data; /* 實際上,對于沒有callback的Response,pollOnce只是返回它的 ident,并沒有實際做什么處理。因為沒有callback,所以系統也不知道如何處理 */ return ident; } } if(result != 0) { if(outFd != NULL) *outFd = 0; if (outEvents != NULL) *outEvents = NULL; if (outData != NULL) *outData = NULL; return result; } // 調用pollInner函數。注意,它在for循環內部 result = pollInner(timeoutMillis); } } ``` 初看上面的代碼,可能會讓人有些丈二和尚摸不著頭腦。但是把pollInner()函數分析完畢,大家就會明白很多。pollInner()函數非常長,把用于調試和統計的代碼去掉,結果如下: **Looper.cpp-->Looper::pollInner()** ``` int Looper::pollInner(int timeoutMillis) { if(timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); ......//根據Native Message的信息計算此次需要等待的時間 timeoutMillis = messageTimeoutMillis; } intresult = ALOOPER_POLL_WAKE; mResponses.clear(); mResponseIndex = 0; #ifdef LOOPER_USES_EPOLL // 只討論使用epoll進行I/O復用的方式 structepoll_event eventItems[EPOLL_MAX_EVENTS]; // 調用epoll_wait,等待感興趣的事件或超時發生 inteventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); #else ......//使用別的方式進行I/O復用 #endif //從epoll_wait返回,這時候一定發生了什么事情 mLock.lock(); if(eventCount < 0) { //返回值小于零,表示發生錯誤 if(errno == EINTR) { goto Done; } //設置result為ALLOPER_POLL_ERROR,并跳轉到Done result = ALOOPER_POLL_ERROR; gotoDone; } //eventCount為零,表示發生超時,因此直接跳轉到Done if(eventCount == 0) { result = ALOOPER_POLL_TIMEOUT; gotoDone; } #ifdef LOOPER_USES_EPOLL // 根據epoll的用法,此時的eventCount表示發生事件的個數 for (inti = 0; i < eventCount; i++) { intfd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; /* 之前通過pipe函數創建過兩個fd,這里根據fd知道是管道讀端有可讀事件。 讀者還記得對nativeWake函數的分析嗎?在那里我們向管道寫端寫了一個”W”字符,這樣 就能觸發管道讀端從epoll_wait函數返回了 */ if(fd == mWakeReadPipeFd) { if (epollEvents & EPOLLIN) { // awoken函數直接讀取并清空管道數據,讀者可自行研究該函數 awoken(); } ...... }else { /* mRequests和前面的mResponse相對應,它也是一個KeyedVector,其中存儲了 fd和對應的Request結構體,該結構體封裝了和監控文件句柄相關的一些上下文信息, 例如回調函數等。我們在后面的小節會再次介紹該結構體 */ ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; // 將epoll返回的事件轉換成上層LOOPER使用的事件 if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; // 每處理一個Request,就相應構造一個Response pushResponse(events, mRequests.valueAt(requestIndex)); } ...... } } Done: ; #else ...... #endif // 除了處理Request外,還處理Native的Message mNextMessageUptime = LLONG_MAX; while(mMessageEnvelopes.size() != 0) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); const MessageEnvelope& messageEnvelope =mMessageEnvelopes.itemAt(0); if(messageEnvelope.uptime <= now) { { sp<MessageHandler> handler = messageEnvelope.handler; Message message = messageEnvelope.message; mMessageEnvelopes.removeAt(0); mSendingMessage = true; mLock.unlock(); /* 調用Native的handler處理Native的Message 從這里也可看出Native Message和Java層的Message沒有什么關系 */ handler->handleMessage(message); } mLock.lock(); mSendingMessage = false; result = ALOOPER_POLL_CALLBACK; }else { mNextMessageUptime = messageEnvelope.uptime; break; } } mLock.unlock(); // 處理那些帶回調函數的Response for (size_t i = 0; i < mResponses.size();i++) { const Response& response = mResponses.itemAt(i); ALooper_callbackFunc callback = response.request.callback; if(callback) {// 有了回調函數,就能知道如何處理所發生的事情了 int fd = response.request.fd; int events = response.events; void* data = response.request.data; // 調用回調函數處理所發生的事件 int callbackResult = callback(fd, events, data); if (callbackResult == 0) { // callback函數的返回值很重要,如果為0,表明不需要再次監視該文件句柄 removeFd(fd); } result = ALOOPER_POLL_CALLBACK; } } returnresult; } ``` 看完代碼了,是否還有點模糊?那么,回顧一下pollInner函數的幾個關鍵點: - 首先需要計算一下真正需要等待的時間。 - 調用epoll\_wait函數等待。 - epoll\_wait函數返回,這時候可能有三種情況: a) 發生錯誤,則跳轉到Done處。 b) 超時,這時候也跳轉到Done處。 c) epoll\_wait監測到某些文件句柄上有事件發生。 - 假設epoll\_wait因為文件句柄有事件而返回,此時需要根據文件句柄來分別處理: a) 如果是管道讀這一端有事情,則認為是控制命令,可以直接讀取管道中的數據。 b) 如果是其他FD發生事件,則根據Request構造Response,并push到Response數組中。 - 真正開始處理事件是在有Done標志的位置。 a) 首先處理Native的Message。調用Native Handler的handleMessage處理該Message。 b) 處理Response數組中那些帶有callback的事件。 上面的處理流程還是比較清晰的,但還是有個一個攔路虎,那就是mRequests,下面就來清剿這個攔路虎。 #### 3. 添加監控請求 添加監控請求其實就是調用epoll\_ctl增加文件句柄。下面通過從Native的Activity找到的一個例子來分析mRequests。 **android_app_NativeActivity.cpp-->loadNativeCode_native()** ``` static jint loadNativeCode_native(JNIEnv* env, jobject clazz,jstring path, jstringfuncName,jobject messageQueue, jstringinternalDataDir, jstring obbDir, jstringexternalDataDir, int sdkVersion, jobject jAssetMgr,jbyteArray savedState) { ...... /* 調用Looper的addFd函數。第一個參數表示監聽的fd;第二個參數0表示ident; 第三個參數表示需要監聽的事件,這里為只監聽可讀事件;第四個參數為回調函數,當該fd發生 指定事件時,looper將回調該函數;第五個參數code為回調函數的參數 */ code->looper->addFd(code->mainWorkRead,0, ALOOPER_EVENT_INPUT,mainWorkCallback, code); ...... } ``` Looper的addFd()代碼如下所示: **Looper.cpp-->Looper::addFd()** ``` int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunccallback, void* data) { if (!callback) { /* 判斷該Looper是否支持不帶回調函數的文件句柄添加。一般不支持,因為沒有回調函數 Looper也不知道如何處理該文件句柄上發生的事情 */ if(! mAllowNonCallbacks) { return -1; } ...... } #ifdef LOOPER_USES_EPOLL intepollEvents = 0; // 將用戶的事件轉換成epoll使用的值 if(events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; if(events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; { AutoMutex _l(mLock); Request request; // 創建一個Request對象 request.fd = fd; // 保存fd request.ident = ident; // 保存id request.callback = callback; //保存callback request.data = data; // 保存用戶自定義數據 struct epoll_event eventItem; memset(& eventItem, 0, sizeof(epoll_event)); eventItem.events = epollEvents; eventItem.data.fd = fd; // 判斷該Request是否已經存在,mRequests以fd作為key值 ssize_t requestIndex = mRequests.indexOfKey(fd); if(requestIndex < 0) { // 如果是新的文件句柄,則需要為epoll增加該fd int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem); ...... // 保存Request到mRequests鍵值數組 mRequests.add(fd, request); }else { // 如果之前加過,那么就修改該監聽句柄的一些信息 int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, &eventItem); ...... mRequests.replaceValueAt(requestIndex, request); } } #else ...... #endif return1; } ``` #### 4. 處理監控請求 我們發現在pollInner()函數中,當某個監控fd上發生事件后,就會把對應的Request取出來調用。 ``` pushResponse(events, mRequests.itemAt(i)); ``` 此函數如下: **Looper.cpp-->Looper::pushResponse()** ``` void Looper::pushResponse(int events, constRequest& request) { Responseresponse; response.events = events; response.request = request; //其實很簡單,就是保存所發生的事情和對應的Request mResponses.push(response);//然后保存到mResponse數組 } ``` 根據前面的知識可知,并不是單獨處理Request,而是需要先收集Request,等到Native Message消息處理完之后再做處理。這表明,在處理邏輯上,Native Message的優先級高于監控FD的優先級。 下面來了解如何添加Native的Message。 #### 5. Native的sendMessage Android 2.2中只有Java層才可以通過sendMessage()往MessageQueue中添加消息,從4.0開始,Native層也支持sendMessage()了。sendMessage()的代碼如下: **Looper.cpp-->Looper::sendMessage()** ``` void Looper::sendMessage(constsp<MessageHandler>& handler, constMessage& message) { //Native的sendMessage函數必須同時傳遞一個Handler nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC); sendMessageAtTime(now, handler, message); //調用sendMessageAtTime } [Looper.java-->Looper::sendMessageAtTime()] void Looper::sendMessageAtTime(nsecs_t uptime, constsp<MessageHandler>& handler, constMessage& message) { size_t i= 0; { AutoMutex _l(mLock); size_t messageCount = mMessageEnvelopes.size(); // 按時間排序,將消息插入到正確的位置上 while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) { i += 1; } MessageEnvelope messageEnvelope(uptime, handler, message); mMessageEnvelopes.insertAt(messageEnvelope, i, 1); // mSendingMessage和Java層中的那個mBlocked一樣,是一個小小的優化措施 if(mSendingMessage) { return; } } // 喚醒epoll_wait,讓它處理消息 if (i ==0) { wake(); } } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看