【从源码看Android】02MessageQueue的epoll原型
来源:互联网 发布:淘宝xboxone美版二手 编辑:程序博客网 时间:2024/05/06 21:30
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];
- #endif
- void 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 源代码
- 【从源码看Android】02MessageQueue的epoll原型
- 【从源码看Android】02MessageQueue的epoll原型
- 【从源码看Android】03Android MessageQueue消息循环处理机制(epoll实现)
- 【从源码看Android】03Android MessageQueue消息循环处理机制(epoll实现)
- 从Android源码角度对Handler,MessageQueue,Looper之间消息传递工作原理的理解
- 也来解释一下android的MessageQueue源码
- android源码解析--MessageQueue
- Android MessageQueue 源码笔记
- Android MessageQueue源码分析
- android MessageQueue 源码解析
- 从源码的角度解析Handler、Looper、Message和MessageQueue
- 从epoll源码分析它的使用
- Android源码分析之MessageQueue
- Android 消息机制 - Handler, Looper, Message, MessageQueue 的源码分析
- Android 从源码看Handler消息机制
- 从应用角度看Android源码
- 从应用角度看Android源码
- 从应用角度看Android源码
- OC UI学习中的笔记
- Web前端开发学习3:SEO代码优化
- Maven详解之------maven版本管理
- 【从源码看Android】03Android MessageQueue消息循环处理机制(epoll实现)
- 杭电1176—免费馅饼题解
- 【从源码看Android】02MessageQueue的epoll原型
- 为什么linux内核函数出现错误,返回值是一个负数
- Qt之统一的UI界面格式基调,漂亮的UI界面
- 网络解析之xml
- The app icon set named "AppIcon" did not have any applicable content.
- PySqlite 学习笔记
- Maya mental ray 焦散
- 【LEETCODE】100-Same Tree
- HDU 1698 线段树区间更新以及lazy思想