2026/4/17 15:43:34
网站建设
项目流程
梦织网站,开发公司宣传册,百度爱采购怎么推广,wordpress上传后如何访问epoll机制
一句话解释#xff1a;epoll机制可以监听特定的fd#xff0c;当fd收到内容时#xff0c;发送事件回调。相比select和poll机制#xff0c;效率更高。
epoll API epoll_create(int size) 参数#xff1a;
size#xff1a;表示最多可以监听多少个fd#xff0c…epoll机制一句话解释epoll机制可以监听特定的fd当fd收到内容时发送事件回调。相比select和poll机制效率更高。epoll APIepoll_create(int size)参数size表示最多可以监听多少个fd新版本已弃用。返回值epoll实例的fd 0成功 0失败作用初始化epoll机制调用API后操作系统内核会产生一个eventpoll实例并返回一个fd这个fd就是epoll实例的句柄。epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)参数epfd: 方法1中创建的epoll实例的fdop 操作指令EPOLL_CTL_ADD: 注册新的fd到epfd中EPOLL_CTL_MOD修改已注册的fd的监听事件EPOLL_CTL_DEL从epfd中删除一个fdfd要监听的fdevent要监听的eventtypedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; // 表示监听的事件类型(EPOLLIN/EPOLLHUP/EPOLLOUT...) epoll_data_t data; // 用户自定义数据当事件发生时将会原样返回给用户 };返回值 0成功 0失败作用注册、修改或删除监听的fd和事件。epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)参数epfd方法1中创建的epoll实例的fdevents和2中结构一样maxeventsevents数量timeout等待超时时间。如果超过timeout还没有事件则返回返回值到来事件的个数返回的事件存储在events数组中。MessageQueue原理大家都知道Android的主线程的Looper本质是运行了一个死循环不断从MessageQueue中取消息执行如果没有消息则会等待在nativePollOnce方法上这个方法的底层原理就是epoll_wait.下面一起来看源码弄清楚Looper的整个流程是怎样的。首先看Looper.java中的loop方法这是我们启动一个线程looper的入口。// Looper.javapublic static void loop() { final Looper me myLooper(); for (;;) { if (!loopOnce(me, ident, thresholdOverride)) { return; } } }这个方法就是开启一个无限循环调用loopOnce。// Looper.javaprivate static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { // 从messagequeue中获取下一条message Message msg me.mQueue.next(); // 执行message msg.target.dispatchMessage(msg); // Android10开始可以通过添加observer的方式监听messsage的执行情况 if (observer ! null) { observer.messageDispatched(token, msg); } // 回收message msg.recycleUnchecked(); return true; }当loopOnce方法返回true之后又会再次循环调到loopOnce重复执行。接下来我们看MessageQueue的next()方法。// MessageQueue.javaMessage next() { for (;;) { // 通过epoll机制等待消息或超时唤醒 nativePollOnce(ptr, nextPollTimeoutMills); synchronized (this) { Message prevMsg null; Message msg mMessages; if (msg ! null msg.target null) { // msg.target null 表示是同步屏障 // 如果有同步屏障则直接跳到下一个异步的消息同步的消息都过滤掉先不处理 do { prevMsg msg; msg msg.next; } while (msg ! null !msg.isAsynchronous()); } if (msg ! null) { if (now msg.when) { // 如果当前还没到达message的执行时间, 则获取当前的时间差作为timeout nextPollTimeoutMillis (int) Math.min(msg.when - now, Integer.MAX_VALUE); }else{ // 省略一些链表的操作 prevMsg.next msg.next; msg.next null; // 直接返回已经到达执行时间的第一条message return msg; } } } } }这个方法首先会调用nativePollOnce这个native方法等nativePollOnce返回后会去MessageQueue的链表中取下一条待执行的message。取message的方法取链表头的第一个messageMessageQueue中的message是按照时间顺序排列的所以第一个就是最近的待执行的message如果这个消息是同步屏障则跳过所有同步消息直接取下一个异步消息返回否则判断当前message是否到执行时间如果到执行时间则直接返回否则继续调nativePollOnce等待。接下来看nativePollOnce的实现。// android_os_MessageQueue_nativePollOnce()static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { //将Java层传递下来的mPtr转换为nativeMessageQueue NativeMessageQueue* nativeMessageQueue reinterpret_castNativeMessageQueue*(ptr); nativeMessageQueue-pollOnce(env, obj, timeoutMillis); 【3】 }经过一系列调用最后调到了Looper中的pollInner方法Looper.cppint Looper::pollInner(int timeoutMillis) { ... struct epoll_event eventItems[EPOLL_MAX_EVENTS]; //fd最大个数为16 //等待事件发生或者超时在nativeWake()方法向管道写端写入字符则该方法会返回 int eventCount epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); //循环遍历处理所有的事件 for (int i 0; i eventCount; i) { int fd eventItems[i].data.fd; uint32_t epollEvents eventItems[i].events; if (fd mWakeEventFd) { if (epollEvents EPOLLIN) { // 如果是唤醒事件则读取并清空管道数据 awoken(); } } else { ssize_t requestIndex mRequests.indexOfKey(fd); if (requestIndex 0) { // 如果是之前在mRequests中注册过的fd //处理request生成对应的reponse对象push到响应数组 pushResponse(events, mRequests.valueAt(requestIndex)); } } } Done: ; //处理Native的Message调用相应回调方法 while (mMessageEnvelopes.size() ! 0) { const MessageEnvelope messageEnvelope mMessageEnvelopes.itemAt(0); if (messageEnvelope.uptime now) { { handler-handleMessage(message); // 处理消息事件 } } else { mNextMessageUptime messageEnvelope.uptime; break; } } //处理带有Callback()方法的Response事件执行Reponse相应的回调方法 for (size_t i 0; i mResponses.size(); i) { if (response.request.ident POLL_CALLBACK) { // 处理请求的回调方法 int callbackResult response.request.callback-handleEvent(fd, events, data); if (callbackResult 0) { removeFd(fd, response.request.seq); //移除fd } } } return result; }Looper.pollInner主要做如下事情调用epoll_wait等待在一些特定的fd上当epoll_wait返回后fd发生写入或超时时间到执行唤醒的事件。如果唤醒的是mWakeEventFd则直接调用awoken方法。如果唤醒的是之前注册在mRequests中的fd则将Request生成一个对应的Response加入mResponses集合处理native message执行相应的回调方法处理mResponses集合中的所有Response事件调用他们callback的handleEvent回调方法。点击事件就是在这里被执行的我们来看看awoken方法。它的逻辑很简单就是循环读取fd中的全部内容。// Looper.cppvoid Looper::awoken() { char buffer[16]; ssize_t nRead; do { nRead read(mWakeReadPipeFd, buffer, sizeof(buffer)); } while ((nRead -1 errno EINTR) || nRead sizeof(buffer)); }关于mRequests和mResponses的逻辑先挖个坑后面的文章再讲。epoll使用示例下面我们写一个epoll机制使用的示例代码。在这个例子中我们监听了一个sockfd的管道端口启动一个线程等待在epoll_wait上。一旦sockfd中写入数据就可以唤醒我们的线程进行读取。#include sys/socket.h void MonitorInit::createEpoll(int sockfd) { if(mSockFd sockfd) return; mEpollFd epoll_create(EPOLL_MAX_EVENTS); int epollEvents 0; epollEvents | EPOLLIN; struct epoll_event eventItem; memset(eventItem, 0, sizeof(epoll_event)); eventItem.events epollEvents; eventItem.data.fd sockfd; int epollResult epoll_ctl(mEpollFd, EPOLL_CTL_ADD, sockfd, eventItem); LLOG_ERROR(TAG_LOOPER, Adding epoll event resutl %d, epollResult); if(epollResult 0){ LLOG_ERROR(TAG_LOOPER, Error adding epoll event, fd %d, errno %d, sockfd, epollResult); } pthread_t thd; // 开启一个线程这个线程用来监听epoll_wait pthread_create(thd, nullptr, epollCallback, nullptr); pthread_detach(thd); mSockFd sockfd; } void epollCallback(void *arg){ // 死循环等待fd消息 while(loop){ int timeoutMillis 100000; struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); LLOG_ERROR(TAG_LOOPER, receive event count %d, eventCount); if(eventCount 0){ LLOG_ERROR(TAG_LOOPER, Poll failed with an unexpected error, errno%d, errno); } if(eventCount 0){ // 超时时间到 LLOG_ERROR(TAG_LOOPER, pollOnce - timeout); } for(int i 0; i eventCount; i){ int fd eventItems[i].data.fd; uint32_t epollEvents eventItems[i].events; if(fd mSockFd){ // 将fd的内容读出来 } } } }总结MessageQueue核心原理主线程通过Looper中的死循环不断从MessageQueue中获取待指定的message。如果有到执行时间的消息时直接执行。如果还没有到执行时间的消息会通过epoll_wait等待在mWakeReadPipeFd端口等待内容写入超时时间是下一个message执行时间到现在的时间差。如果在等待的过程中有新的消息插入队列会往mWakeReadPipeFd端口写入数据这样就能唤醒等待在这个上面的pollInner方法从而继续执行之后的message。如果等待的过程中没有新的消息插入则会在timeout时间到达的时候唤醒处理后面的message。AI大模型学习福利作为一名热心肠的互联网老兵我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。一、全套AGI大模型学习路线AI大模型时代的学习之旅从基础到前沿掌握人工智能的核心技能因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获取二、640套AI大模型报告合集这套包含640份报告的合集涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师还是对AI大模型感兴趣的爱好者这套报告合集都将为您提供宝贵的信息和启示。因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获三、AI大模型经典PDF籍随着人工智能技术的飞速发展AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型如GPT-3、BERT、XLNet等以其强大的语言理解和生成能力正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获四、AI大模型商业化落地方案因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获作为普通人入局大模型时代需要持续学习和实践不断提高自己的技能和认知水平同时也需要有责任感和伦理意识为人工智能的健康发展贡献力量