epoll监听文件_介绍一下 Android Handler 中的 epoll 机制?

介绍一下 Android Handler 中的 epoll 机制?

目录:

  1. IO 多路复用
  2. select、poll、epoll 对比
  3. epoll API
  4. epoll 使用示例
  5. Handler 中的 epoll 源码分析

IO 多路复用

IO 多路复用是一种同步 IO 模型,实现一个线程可以监视多个文件句柄。一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作,没有文件句柄就绪时会阻塞应用程序,交出 cpu。

与多进程和多线程技术相比,IO 多路复用技术的最大优势是系统开销小,系统不必为每个 IO 操作都创建进程或线程,也不必维护这些进程或线程,从而大大减小了系统的开销。

select、poll、epoll 就是 IO 多路复用三种实现方式。

select、poll、epoll 对比

select 最大连接数为进程文件描述符上限,一般为 1024;每次调用 select 拷贝 fd;轮询方式工作时间复杂度为 O(n)

poll 最大连接数无上限;每次调用 poll 拷贝 fd;轮询方式工作时间复杂度为 O(n)

epoll 最大连接数无上限;首次调用 epoll_ctl 拷贝 fd,调用 epoll_wait 时不拷贝;回调方式工作时间复杂度为 O(1)

epoll API

int epoll_create(int size);

创建 eventpoll 对象,并将 eventpoll 对象放到 epfd 对应的 file->private_data 上,返回一个 epfd,即 eventpoll 句柄。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) //返回值:成功 0;失败 -1

对一个 epfd 进行操作。op 表示要执行的操作,包括 EPOLL_CTL_ADD (添加)、EPOLL_CTL_DEL (删除)、EPOLL_CTL_MOD (修改);fd 表示被监听的文件描述符;event 表示要被监听的事件,包括:

  • EPOLLIN(表示被监听的fd有可以读的数据)
  • EPOLLOUT(表示被监听的fd有可以写的数据)
  • EPOLLPRI(表示有可读的紧急数据)
  • EPOLLERR(对应的fd发生异常)
  • EPOLLHUP(对应的fd被挂断)
  • EPOLLET(设置EPOLL为边缘触发)
  • EPOLLONESHOT(只监听一次)
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) //返回值:监听到的产生的事件数

等待 epfd 监听的 fd 所产生对应的事件。epfd 表示 epoll句柄;events 表示回传处理事件的数组;maxevents 表示每次能处理的最大事件数;timeout:等待 IO 的超时时间,-1 表示一直阻塞直到来 IO 被唤醒,大于 0 表示阻塞指定的时间后被唤醒

epoll 使用示例

创建一个管道,使用 epoll 监听管道读端,然后进入阻塞:

 int pipFd[2];
 pipe(pipFd); //打开管道

 struct epoll_event event;
 event.data.fd = pipFd[0]; //设置为监听管道读端
 event.events = EPOLLIN | EPOLLET; //设置参数,接收可以 read() 的通知

 int epfd = epoll_create(256); //创建 epoll 对象
 int res = epoll_ctl(epfd, EPOLL_CTL_ADD, pipFd[0], &event); //添加管道读端为要监听的文件描述符

 struct epoll_event allEvs[256];
 int count = epoll_wait(epfd, allEvs, 256, 5000); //当前线程进入阻塞,等待被唤醒
 for(int i = 0; i //被唤醒,处理触发唤醒文件描述符
     if(allEvs[i].data.fd == pipFd[0] && (allEvs[i].events & EPOLLIN)){
         char buffer[256];
         read(pipeFd, buffer, 100); //接收到管道可以进行读的信号,开始读取
     }
 }

在其他线程写入管道,通知唤醒:

 write(pipFd[1], str,strlen("hello"));

eventfd

eventfd 是 Linux 系统中一个用来通知事件的文件描述符,基于内核向用户空间应用发送通知的机制,可以有效地被用来实现用户空间事件驱动的应用程序。

简而言之:eventfd 就是用来触发事件通知,它只有一个系统调用接口:

int eventfd(unsigned int initval, int flags);

表示打开一个 eventfd 文件并返回文件描述符,支持 epoll/poll/select 操作。

之所以要在介绍 Handler native 源码前先介绍 eventfd,是因为在 Android 6.0 后,Handler 底层替换为 eventfd/epoll 实现。而 6.0 之前是由 pipe/epoll 实现的,就像上面的 epoll 使用示例那样。

Handler 中的 epoll 源码分析

主要分析 MessageQueue.java 中的三个 native 函数:

private native static long nativeInit(); //返回 ptr
private native void nativePollOnce(long ptr, int timeoutMillis); //阻塞
private native static void nativeWake(long ptr); //唤醒
nativeInit

首先来看 nativeInit 方法,nativeInit 在 MessageQueue 构造函数中被调用,其返回了一个底层对象的指针:

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

对应实现在 android_os_MessageQueue.cpp 中:

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    ...
    return reinterpret_cast(nativeMessageQueue);
}

可见 MessageQueue 对应的底层对象就是 NativeMessageQueue,而 NativeMessageQueue 初始化时会创建一个底层的 Looper 对象:

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

如上代码,可以知道 Looper 对象是 ThreadLocal 类型。Looper 的构造函数如下:

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), ...{
    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    ...
    rebuildEpollLocked();
}

首先通过 eventfd 系统调用返回一个文件描述符,专门用于事件通知。接着来看 rebuildEpollLocked 方法:

void Looper::rebuildEpollLocked() {
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event));
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem); 
    ...
}

可以看到我们已经熟悉的 epoll 操作了:通过 epoll_create 创建 epoll 对象,然后调用 epoll_ctl 添加 mWakeEventFd 为要监听的文件描述符。

nativePollOnce

之前学习 Handler 机制时多次看到过 nativePollOnce 方法,也知道它会进入休眠,下面就来彻底搞懂它的原理。对应的底层调用同样是在 android_os_MessageQueue.cpp 中:

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mLooper->pollOnce(timeoutMillis);
    ...
}

可以看到实现同样是在 Looper.cpp 中,接着来看 Looper 的 pollOnce 方法:

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    for (;;) {
        ...
        result = pollInner(timeoutMillis);
    }
}

int Looper::pollInner(int timeoutMillis) {
    ...
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ...

至此通过调用 epoll_wait 方法,当前线程进入休眠,等待被唤醒。

nativeWake

最后来看如何通过 nativeWake 唤醒线程,首先是 android_os_MessageQueue.cpp 中:

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
    nativeMessageQueue->wake();
}void NativeMessageQueue::wake() {
    mLooper->wake();
}

与 nativeInit、nativePollOnce 一样,最终实现都是在 Looper.cpp 中,Looper 的 wake 方法如下:

void Looper::wake() {
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
                    mWakeEventFd, strerror(errno));
        }
    }
}

其中关键逻辑是对 mWakeEventFd 发起写入操作,从而唤醒 nativePollOnce 中通过 epoll_wait 进入休眠的线程。

推荐阅读:

开源一组视频时间轴控件

Activity Window 创建及添加过程

抽象工厂模式

Android UI 绘制请求与绘制时机工厂模式Android 消息屏障与异步消息Java 并发编程知识点梳理总结1638a8c94cb8f3ab6485ce19f5c312e8.png

关注我

助你升职加薪

Android 面试官

7c2fe87486f6f45d523db409a1fa27c3.png464f1a7947a055925b4fabc089f94f6a.png点赞在看, 年薪百万

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275975.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端工程师的一大神器——puppeteer

大家好,我是若川。欢迎加我微信 ruochuan12,长期交流学习。今天推荐神器puppeteer,我猜有挺多人不知道。文章不长,看完有空也可以试玩。我18年也写过一篇puppeteer爬取生成pdf的文章,时间真快。前端使用puppeteer 爬虫…

selenium界面元素定位

一、 Selenium界面元素定位 本文元素定位以das2为例 #导入包 from selenium import webdriver #打开火狐驱动 driverwebdriver.Firefox() #访问网址 driver.get("http://192.168.3.217:8080/das/seatlogin.jsp ") 进行web页面自动化测试,对页面上…

vue.js ui_UI / UX开发:考虑Vue.js

vue.js uiBecause sometimes we have to add logic to our concepts, and Vue makes it a whole lot easier.因为有时我们必须在概念中添加逻辑,而Vue使其变得更加容易。 FULL DISCLOSURE: THIS IS NOT A COMPLETE JAVASCRIPT OR VUE COURSE. There’s no way I co…

Silverlight学习笔记十七BingMap(三)之地图的地区标识

如果我们需要在Bing Maps中加入一个小图钉标记&#xff0c;该如何实现了&#xff1f; Bing Maps控件已经为我们提供了这个功能&#xff0c;在Microsoft.Maps.MapControl名称空间下提供了实现图钉应用的图钉层Pushpin类用该类来实现普通标识 在Xaml中添加<map:Pushpin Locati…

win10查看pcie设备_壹拓网科技解密WIN10系统使用向日葵开机棒远程开机需要设置几个地方...

向日葵开机棒&#xff0c;是一款非常好用的远程智能远程开机硬件&#xff0c;它一头接网线&#xff0c;另外一头和被开电脑接在同一个路由器下&#xff0c;不需要和被开电脑或者设备直接连接&#xff0c;当然&#xff0c;被开电脑需要有线联网&#xff0c;暂时不支持使用无线方…

如何成为公司独当一面的工程师

大家好&#xff0c;我是若川。欢迎加我微信 ruochuan12&#xff0c;长期交流学习。今天推荐黄老师的这篇文章&#xff0c;你可能看到过了&#xff0c;但值得再看一遍。之前常有小伙伴问&#xff0c;大多情况下我都会分享这篇文章。点击下方卡片关注我、加个星标&#xff0c;或者…

flex如何做响应式设计_响应式设计-您做错了!

flex如何做响应式设计Responsive design is not just about the web that automatically adjusts to different screen resolutions and resizeable images, but designs that are crucial for web performance.自适应设计不仅涉及可自动适应不同屏幕分辨率和可调整大小图像的网…

30万手表推荐_今年六十岁生日,儿子说要送只30万的手表,请问有哪些推荐?...

关注腕表部落&#xff0c;尽享腕表生活一位读者向笔者提出这样一个问题&#xff1a;今年六十岁生日&#xff0c;儿子说要送只30万的手表&#xff0c;请问有哪些推荐&#xff1f;首先要恭喜这位老爷子&#xff0c;一来是生日马上就要到了&#xff0c;二来是还有这么孝顺而且慷慨…

写 Node.js 代码,从学会调试开始

大家好&#xff0c;我是若川&#xff08;点这里加我微信 ruochuan12&#xff0c;长期交流学习&#xff09;。今天推荐这篇调试文章&#xff0c;熟悉我的读者都知道我写的源码文章都多次强调要调试&#xff0c;而且写了调试方法。点击下方卡片关注我、加个星标&#xff0c;或者查…

创建用户友好的表单

Forms are a common way to engage with users and could be a user’s first impression of your product. Since forms aren’t always the user’s favourite thing, it is essential to make filling out forms as easy as possible. Let’s go over a few tips for creati…

细节决定成败—关于.net的.dll.refresh文件

一直在做.net的项目&#xff0c;c/s的、b/s的&#xff0c;一直没有注意这个东西。众所周知&#xff0c;.net的程序生成后会在bin目录下生成.dll文件&#xff0c;而.dll.refresh这个文件从何而来呢&#xff1f;那天无聊地google了下才知&#xff0c;这个东东是在你的项目中引用第…

环境在c盘_如何给女朋友解释为什么 Windows 上面的软件都把自己安装在 C 盘

本文经授权转载自漫画编程(ID&#xff1a;mhcoding)周末&#xff0c;我在家里面看电视&#xff0c;女朋友正在旁边鼓捣她的电脑&#xff0c;但是好像并不是很顺利&#xff0c;于是就有了以下对话。计算机存储我们使用的计算机中&#xff0c;保存信息的介质有两类&#xff1a;一…

能让你纵享丝滑的SSR技术,转转这样实践

大家好&#xff0c;我是若川&#xff08;点这里加我微信 ruochuan12&#xff0c;长期交流学习&#xff09;。今天推荐这篇图文并茂的SSR技术文章。这是江西前端群里一个小伙伴的文章。群里小伙伴很多都在知名大厂&#xff0c;但他们都很低调。点击下方卡片关注我、加个星标&…

魅族魅蓝mirror简单打开usb调试模式的步骤

经常我们使用安卓手机链接电脑的时候&#xff0c;或者使用的有些应用比如我们企业营销团队经常使用的应用引号精灵&#xff0c;以前使用的老版本就需要开启USB调试模式下使用&#xff0c;现经常新版本不需要了&#xff0c;如果手机没有开启USB调试模式&#xff0c;电脑则无办法…

hp-ux 单用户 启动_UX备忘单:搜索与浏览

hp-ux 单用户 启动重点 (Top highlight)When designing search results and interest sites, you have to keep in mind what ‘mode’ your user is in. Are they in ‘searching mode’ or ‘browsing mode’? This will help you determine how to design your platform to…

细数开源历史上的九个重大事件

开放源码&#xff08;开源&#xff09;的精神在于使用者可以使用、复制、散布、研究和改进软件。这可以追溯到20世纪60年代&#xff0c;至今已有半个世纪了。伯乐在线-职场博客的这篇文章将列举开源历史上的九大重要事件。虽然本文不是专门对开源产品&#xff0c;但还是说到了一…

有赞大数据平台安全建设实践

一、概述 在大数据平台建设初期&#xff0c;安全也许并不是被重点关注的一环。大数据平台的定位主要是服务数据开发人员&#xff0c;提高数据开发效率&#xff0c;提供便捷的开发流程&#xff0c;有效支持数仓建设。大数据平台的用户都是公司内部人员。数据本身的安全性已经由公…

请先设置tkk_理光MP2014扫描至文件夹的设置方法

理光旗下的2014系列入门级A3黑白复印机市场保有量较大&#xff0c;该系列机型加装M16网卡后可以方便的实现扫描至文件夹功能&#xff0c;经常有客户咨询该机型的扫描设置方法&#xff0c;下面我就以MP2014D为例来演示一下该机型的SMB扫描设置方法&#xff1a;首先是在电脑上建立…

听说现在都考这些React面试题

大家好&#xff0c;我是若川。最近刷脉脉看见圈里都在聊面试&#xff0c;吐槽最多的还是万年考点 React 和 Vue。不过关于两者的比较似乎有点针尖对麦芒的赶脚。确实&#xff0c;面试的偏重点往往映射公司对该框架的重视程度&#xff0c;但也不能一概而论&#xff0c;去学习或放…

荒岛余生为什么没有打开包裹_您会带到荒岛什么办公桌设置?

荒岛余生为什么没有打开包裹Throughout life, you experience a lot of desks and a lot of desk setups. Real or virtual, at the office or at home, temporal or permanent — just a way to call it, nothing is permanent— a big one with a great office view or a sma…