Android 中的Looper如何实现阻塞与唤醒的
来源:互联网 发布:饥荒mac版mod 编辑:程序博客网 时间:2024/06/03 12:41
说该问题之前我们需要首先介绍一下eventfd的作用:
一、eventfd作用
#include <sys/eventfd.h>
int eventfd(unsigned int initval, intflags);
参数解释:
如果是2.6.26或之前版本的内核,flags 必须设置为0。
Initval:初始化计数器值,该值(暂时取名为A)保存在内核。
Flags支持一下标志位:
EFD_NONBLOCK 类似于使用O_NONBLOCK标志设置文件描述符。
EFD_CLOEXEC 类似open以O_CLOEXEC标志打开,
O_CLOEXEC 应该表示执行exec()时,之前通过open()打开的文件描述符会自动关闭测试时,
在open()之后,调用一下exec(),在新的进程中检测描述符是否已经关闭
返回值:
函数返回一个文件描述符,与打开的其他文件一样,可以进行读写操作。
read/write操作返回的文件描述符
Read:如果计数器A的值不为0时,读取成功,获得到该值。
如果A的值为0,非阻塞模式时,会直接返回失败,并把error置为EINVAL
如果为阻塞模式,一直会阻塞到A为非0为止。
Write:将缓冲区写入的8字节整形值加到内核计数器上,即会增加8字节的整数在计数器A上,
如果A的值达到0xfffffffffffffffe时,就会阻塞(在阻塞模式下),直到A的值被read。阻塞和非阻塞情况同上面read一样。
所以当调用weite函数将1,2,3,4依次写入eventfd()函数返回的文件描述符后,当调用read读取该文件描述符,读取到的值是:(initval+1+2+3+4)的结果
总结:通过man eventfd提供的例子,作用大概和管道差不多
例子:
#include <sys/eventfd.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> /* Definition of uint64_t */
#define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
int main(int argc, char *argv[])
{
uint64_t u;
int efd = eventfd(10, 0);
if (efd == -1)
handle_error("eventfd");
int ret = fork();
if(ret == 0)
{ int j = 0;
for ( j= 1; j < argc; j++) {
printf("Child writing %s to efd\n", argv[j]);
u = atoll(argv[j]);
ssize_t s = write(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
handle_error("write");
}
printf("Child completed write loop\n");
exit(EXIT_SUCCESS);
}
else
{
sleep(2);
ssize_t s = read(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
handle_error("read");
printf("Parent read %llu from efd\n",(unsigned long long)u);
exit(EXIT_SUCCESS);
}
}
运行结果:比较简单,不做解释。子进程写入命令行中传入的参数,父进程读取其中计数器的值。
运行结果:
./eventfd 10 20 30
Child writing 10 to efd
Child writing 20 to efd
Child writing 30 to efd
Child completed write loop
Parent read 70 from efd
命令行传入的是10、20、30其和应为60,为啥读取的是70呢?
因为在eventfd(10, 0)传入的第一个参数是10,这个参数是创建eventfd时初始化计数器的值。所以读取到的最终结果是(60+10)
二、Android 中的Looper如何实现阻塞与唤醒的
system\core\libutils\Looper.cpp
其中Looper的pollOnce()用于睡眠等待;其中Looper的wake() 用于唤醒。
接下来我们分析Looper与eventfd的关系(需要在Android6.0及6.0之后的代码中看)
构造:
Looper::Looper(bool allowNonCallbacks)
===》mWakeEventFd = eventfd(0, EFD_NONBLOCK);//调用eventfd返回文件描述符mWakeEventFd
===》...
===》rebuildEpollLocked();
=======》int mEpollFd = epoll_create(EPOLL_SIZE_HINT);//使用epoll机制======》struct epoll_event eventItem;
======》eventItem.events = EPOLLIN;
======》eventItem.data.fd = mWakeEventFd;
======》int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);//使用epoll监控mEpollFd
阻塞:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)
===》result = pollInner(timeoutMillis);
======》 //使用epoll进行监控eventfd返回的文件描述符,当有数据时则解除阻塞状态
======》 int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
唤醒:
void Looper::wake()
===》TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));//其中mWakeEventFd就是eventfd返回的文件描述符
在此我们就搞懂了Looper的休眠与唤醒机制。
在Android旧版本使用管道与epoll来完成Looper的休眠与唤醒的
在Android6新版本中使用的是eventfd与epoll来完成Looper的休眠与唤醒的
- Android 中的Looper如何实现阻塞与唤醒的
- Looper中的睡眠等待与唤醒机制
- 进程的阻塞与唤醒
- 如何唤醒socket被阻塞的函数
- 如何唤醒socket被阻塞的函数
- 2多线程的阻塞、唤醒与同步
- 线程阻塞与唤醒
- 线程阻塞与唤醒
- Android中的Handler与Looper
- Java线程唤醒与阻塞
- Java线程唤醒与阻塞
- java线程阻塞与唤醒
- Java线程唤醒与阻塞
- Java线程唤醒与阻塞
- Java线程唤醒与阻塞
- 各个操作系统中 阻塞/睡眠 - 唤醒 模型的实现
- Android多线程中的Handler机制、Looper的介绍与整理
- 0027 Java线程的阻塞与唤醒【基础】
- Struts2的运行原理及简要剖析
- 自定义上传组件样式
- ContenType类型大全(包括Office2007文件等问题的解决办法)
- Hive编写UDF函数
- C语言二级指针做函数参数改变该指针的指向
- Android 中的Looper如何实现阻塞与唤醒的
- memcmp的问题
- Spring Boot访问mysql(JPA方式)最简单配置
- python 状态机语句"Python is fun"褒贬义判断的两种实现方式
- Unity NGUI UITexture 图片替换
- Atlantis HDU
- axis2 webservice 接口请求(json对象组装参数请求(用于对象请求)、多个参数请求、阿里巴巴json工具使用)
- SourceTree冲突解决
- 根据城市名获取天气