【從源碼看Android】02MessageQueue的epoll原型

來源:互聯網
上載者:User

標籤:核心   epoll   android   源碼   thread   

1 開頭

上一講講到Looper,大家對Looper有了大概的了結(好幾個月過去了…)

大家都知道一個Handler對應有一個MessageQueue,

在哪個線程上new Handler(如果不指定looper對象),那麼這個handler就預設對應於這個線程上的prepare過的Looper

如Handler.java代碼所示,mLooper由Looper.myLooper()指定,

public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> klass = getClass();            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                    (klass.getModifiers() & Modifier.STATIC) == 0) {                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                    klass.getCanonicalName());            }        }        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

而Looper.myLooper()來自此線程裡儲存的looper對象(在looper.prepare時存入)

public static Looper myLooper() {        return sThreadLocal.get();    }

so,一個handler,對應了一套MessageQueue、Thread、Looper

這些都是 【從源碼看Android】01從Looper說起 講過的東西,那麼下面來些硬貨



2 一個問題引入

從一個問題引入,如果在子線程12上建立了一個handler,

現在在主線程上調用handler.sendEmptyMessage,

handler如何在主線程上處理這個msg,

然後從子線程12讓handler的handleMessage函數處理呢?


那麼這個時候就要引入一個跨線程的事件模型--epoll,

這一講先把cpp epoll模型講清楚,

下一講再講android裡如何利用這個模型的



3 epoll模型

epolldemo.cpp

#include <iostream>#include <vector>#include <queue>#include <pthread.h>#include <unistd.h>#include <sys/epoll.h>#include <assert.h>#include <fcntl.h>#define NUM_THREAD 4#define NUM_LENGTH 200#define MAX_EVENTS 20#define USES_EPOLL#ifdef USES_EPOLL/****(1).建立一個epoll描述符,調用epoll_create()來完成,epoll_create()有一個整型的參數size,用來告訴核心,要建立一個有size個描述符的事件列表(集合)int epoll_create(int size)(2).給描述符設定所關注的事件,並把它添加到核心的事件列表中去,這裡需要調用epoll_ctl()來完成。int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)這裡op參數有三種,分別代表三種操作:a. EPOLL_CTL_ADD, 把要關注的描述符和對其關注的事件的結構,添加到核心的事件列表中去b. EPOLL_CTL_DEL,把先前添加的描述符和對其關注的事件的結構,從核心的事件列表中去除c. EPOLL_CTL_MOD,修改先前添加到核心的事件列表中的描述符的關注的事件(3). 等待核心通知事件發生,得到發生事件的描述符的結構列表,該過程由epoll_wait()完成。得到事件列表後,就可以進行事件處理了。int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)– EPOLLIN,讀事件– EPOLLOUT,寫事件– EPOLLPRI,帶外資料,與select的例外狀況事件集合對應– EPOLLRDHUP,TCP串連對端至少寫寫半關閉– EPOLLERR,錯誤事件– EPOLLET,設定事件為邊沿觸發– EPOLLONESHOT,只觸發一次,事件自動被刪除*/int g_epollfd;int g_wakeFds[2];#endifvoid awake(){    ssize_t nWrite;    do     {        nWrite = write(g_wakeFds[1], "W", 1);    }     while (nWrite == -1);}void awoken() {    char buffer[16];    ssize_t nRead;    do {        nRead = read(g_wakeFds[0], buffer, sizeof(buffer));    } while ((nRead == -1 ) || nRead == sizeof(buffer));}using namespace std;void* threadRead(void* userdata){    queue<int>* q = (queue<int>*)userdata;    struct epoll_event events[MAX_EVENTS];    while( true )    {        int fds = epoll_wait(g_epollfd, events, MAX_EVENTS, 1000);        if(fds < 0){            printf("epoll_wait error, exit\n");            break;        }        for(int i = 0; i < fds; i++){            if( events[i].events & EPOLLIN ) // read event            {                printf("%s,%d/%d\n", "EPOLLIN",i,fds);                while( !q->empty() )                {                    q->pop();                    printf("removed! \n" );                }            }        }        awoken();    }    return userdata;}void* threadRun(void* userdata){queue<int>* q = (queue<int>*)userdata;while( true )    {#ifdef USES_EPOLL        q->push( 1 );        printf("%ld:%s\n",(long)pthread_self() ,"added!");        awake();#else#endif    usleep(1000*500);    }    printf("exit thread:%ld\n",(long)pthread_self() );return userdata;}int main(int argc, char const *argv[]){/**pipe(建立管道):1) 標頭檔 #include<unistd.h>2) 定義函數: int pipe(int filedes[2]);3) 函數說明: pipe()會建立管道,並將檔案描述詞由參數filedes數組返回。              filedes[0]為管道裡的讀取端              filedes[1]則為管道的寫入端。*/    int result = pipe(g_wakeFds);    assert( result!=0 );    result = fcntl(g_wakeFds[0], F_SETFL, O_NONBLOCK);    assert(result!=0);    result = fcntl(g_wakeFds[1], F_SETFL, O_NONBLOCK);    assert(result!=0);    g_epollfd = epoll_create( MAX_EVENTS );    assert( g_epollfd > 0 );    struct epoll_event epv = {0, {0}};    //epv.data.ptr = userdata;    epv.data.fd = g_wakeFds[0];    epv.events = EPOLLIN;    if(epoll_ctl(g_epollfd, EPOLL_CTL_ADD, g_wakeFds[0], &epv) < 0)        printf("Event Add failed[fd=%d], evnets[%d]\n", epv.data.fd, epv.events);    else        printf("Event Add OK[fd=%d], op=%d, evnets[%0X]\n", epv.data.fd, EPOLL_CTL_ADD, epv.events);    queue<int> q;    vector<pthread_t> v;for (int i = 0; i < NUM_THREAD; ++i){pthread_t tid;pthread_create(&tid,NULL,threadRun,&q);v.push_back(tid);}    pthread_t tid;    pthread_create(&tid,NULL,threadRead,&q);    v.push_back(tid);      for(vector<pthread_t>::const_iterator it = v.begin(); it < v.end(); ++it)        pthread_join(*it,NULL);return 0;}


大致思路是這樣的:

a.127行開始建立管道g_wakeFds,g_wakeFds[0]是讀取連接埠,g_wakeFds[1]是寫入連接埠

b.136行建立全域的g_epollfd,即epoll檔案描述符,參數為這個檔案描述符所支援的最大事件數目

c.144行epoll_ctl建立一個事件關聯,即將g_epollfd與g_wakeFds[0]進行關聯,如果g_wakeFds[0]發生變化,就會觸發事件,並且事件為139建立的epoll_event執行個體

d.151-156行建立多個線程作為生產者,生產int放入queue中,放入完後調用awake()函數,向g_wakeFds[1]寫入一位元組,觸發事件

f.158-160行建立一個消費者來消費生產的int

g.其中76行int fds = epoll_wait(g_epollfd, events, MAX_EVENTS, 1000);來等待生產者生產的int,當g_wakeFds[1]有資料寫入時,g_wakeFds[0]就會觸發剛剛註冊的事件,擷取到註冊的事件後對事件進行處理(消費int),隨後調用awoken()清空g_wakeFds[0],進入下一輪epoll_wait


注意:生產enqueue和消費dequeue是需要同步鎖的,這裡省略了這個過程,android在java中對Message實現的同步鎖


4 運行結果



5 源碼下載

http://pan.baidu.com/s/1i3BTWpv


6 總結

當一個線程的訊息佇列沒有訊息需要處理時,它就會在這個管道的讀端檔案描述符上進行睡眠等待,直到其他線程通過這個管道的寫端檔案描述符來喚醒它。這樣就節省了線程上對於cpu資源的消耗。


7 reference

《Android系統源碼情景分析》- 羅昇陽

Android NDK 原始碼

Android SDK 原始碼



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.