epoll I/O复用分析

145 阅读6分钟

epoll I/O复用分析

typedef union epoll_data {
        void *ptr;
        int fd;
        uint32_t u32;
        uint64_t u64;
} epoll_data_t;

struct epoll_event {
        uint32_t     events;      /* Epoll events */
        epoll_data_t data;        /* User data variable */
};

int epoll_create(int size)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

  • events则是分配好的 epoll_event结构体数组,epoll将会把发生的事件复制到 events数组中(events不可以是空指针,内核只负责把数据复制到这个 events数组中,不会去帮助我们在用户态中分配内存。内核这种做法效率很高)

    • 数组名也是一个指针。数组名是常量指针,存放的是首元素地址,在方法入参中,数组名指针会被强转为一个普通指针
        struct epoll_event eventItems[EPOLL_MAX_EVENTS];
        int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    
    

Message

looper对外提供的,应用可自定义的消息。looper负责message的接收和转发,但消息的处理,由应用指定的Handler处理

struct Message {
    Message() : what(0) { }
    Message(int w) : what(w) { }

    /* The message type. (interpretation is left up to the handler) */
    int what;
};

MessageHandler

MessageHandler是一个接口,需要应用继承并实现

class MessageHandler : public virtual RefBase {
protected:
    virtual ~MessageHandler();

public:
    /**
     * Handles a message.
     */
    virtual void handleMessage(const Message& message) = 0;
};

LooperCallback

LooperCallback和Looper_callbackFunc目的:事件发生时,应用指定的callback方法。一个是类,一个是函数指针

class LooperCallback : public virtual RefBase {
protected:
    virtual ~LooperCallback();

public:
    /**
     * Handles a poll event for the given file descriptor.
     * It is given the file descriptor it is associated with,
     * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
     * and the data pointer that was originally supplied.
     *
     * Implementations should return 1 to continue receiving callbacks, or 0
     * to have this file descriptor and callback unregistered from the looper.
     */
    virtual int handleEvent(int fd, int events, void* data) = 0;
};

typedef int (*Looper_callbackFunc)(int fd, int events, void* data);

Looper

总结

  • one looper one thread,通过TLS实现
  • looper可接收和转发应用自定义消息
class Looper : public RefBase {
  • POLL_WAKE 被唤醒,没有callback被执行,没有文件fd准备

  • POLL_CALLBACK 执行callback,包含message或文件fd

  • POLL_TIMEOUT 超时返回,没有callback被执行,没有文件fd准备

  • EVENT_INPUT fd监听输入事件,即read

  • EVENT_OUTPUT fd监听输出事件,即write

  • pollOnce 在指定time监听事件并返回

    int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
    inline int pollOnce(int timeoutMillis) {
        return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
    }

addFd

  • ident和callback,if callback else ident
    int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);
    int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);

sendMessage

入队消息,该消息是一次性消费的

    void sendMessage(const sp<MessageHandler>& handler, const Message& message);

setForThread

一个looper可以不与特定线程bind,但:

  • 调用pollOnce的线程,与创建looper的线程,可能并不是同一个,looper便不是线程安全的,变量访问需要锁机制

将looper与特定线程bind(一般是创建looper的线程,但looper可在线程间转移),即 one loop one thread

  • 多个线程可拥有同一个looper,即多个thread可共享同一个looper
  • 一个looper不可有多个线程
  • looper的事件监听及其处理是在同一个线程的。但其实也可以looper1对事件进行预处理,入队looper2,wake looper2对事件进行处理,looper1返回继续监听事件
    static sp<Looper> prepare(int opts);
	static void setForThread(const sp<Looper>& looper);
    static sp<Looper> getForThread();

looper对处理对象的封装

  • Request 对文件fd及其callback封装
  • Response epoll对相应request的返回事件封装
  • MessageEnvelope 应用自定义消息封装
    struct Request {
        int fd;
        int ident;
        int events;
        int seq;
        sp<LooperCallback> callback;
        void* data;

        void initEventItem(struct epoll_event* eventItem) const;
    };

    struct Response {
        int events;
        Request request;
    };

    struct MessageEnvelope {
        MessageEnvelope() : uptime(0) { }

        MessageEnvelope(nsecs_t u, const sp<MessageHandler> h,
                const Message& m) : uptime(u), handler(h), message(m) {
        }

        nsecs_t uptime;
        sp<MessageHandler> handler;
        Message message;
    };
  • mMessageEnvelopes mRequests mResponses
    Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
    // Locked list of file descriptor monitoring requests.
    KeyedVector<int, Request> mRequests;  // guarded by mLock
    // This state is only used privately by pollOnce and does not require a lock since
    // it runs on a single thread.
    Vector<Response> mResponses;
  • mEpollFd epoll_create返回的epoll文件描述符
  • mWakeEventFd 唤醒epoll_wait的文件描述符
    android::base::unique_fd mEpollFd;  // guarded by mLock but only modified on the looper thread
    android::base::unique_fd mWakeEventFd;  // immutable

具体分析

addFd

  • 检查,如果没有callback,需要检查mAllowNonCallbacks,若无则报错;或者其ident小于0
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
    
  • 将此次addFd,翻译为一个request,并根据fd,检查是否已在mRequests队列中

    • 不在队列中,说明是一个新的请求:

      • int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
        mRequests.add(fd, request);
        
      • 在队列中,说明这次请求是修改感兴趣的事件event

        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
        mRequests.replaceValueAt(requestIndex, request);
        

sendMessageAtTime

  • while,根据uptime(消息处理时间)在队列中查找index,即消息按时间大小排序
  • 将handler及其对应的message装入信封MessageEnvelope
        size_t messageCount = mMessageEnvelopes.size();
        while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {//mMessageEnvelopes按时间长短排序
            i += 1;
        }
        MessageEnvelope messageEnvelope(uptime, handler, message);
        mMessageEnvelopes.insertAt(messageEnvelope, i, 1);

pollInner

  • mResponses仅looper所在线程访问,故不用加锁
    // Poll.
    int result = POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;
  • epoll_wait阻塞,等待事件返回
    • mPolling 表示当前looper空闲,可添加消息 wake 并处理
    // We are about to idle.
    mPolling = true;

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    // No longer idling.
    mPolling = false;

  • 无文件fd事件就绪,超时返回,可能接下来需要处理应用自定义消息
    // Check for poll timeout.
    if (eventCount == 0) {
        result = POLL_TIMEOUT;
        goto Done;
    }
  • 有文件fd事件就绪
    • mWakeEventFd如果是wake事件
    • mRequests.indexOfKey(fd) 已注册fd的事件回调,初步加工为response
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd.get()) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
  • 处理到时的message

    handler->handleMessage(message)

        // Invoke pending message callbacks.
        mNextMessageUptime = LLONG_MAX;
        while (mMessageEnvelopes.size() != 0) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
            if (messageEnvelope.uptime <= now) {
                // Remove the envelope from the list.
                // We keep a strong reference to the handler until the call to handleMessage
                // finishes.  Then we drop it so that the handler can be deleted *before*
                // we reacquire our lock.
                { // obtain handler
                    sp<MessageHandler> handler = messageEnvelope.handler;
                    Message message = messageEnvelope.message;
                    mMessageEnvelopes.removeAt(0);
                    mSendingMessage = true;
                    mLock.unlock();
    
    #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
                    ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
                            this, handler.get(), message.what);
    #endif
                    handler->handleMessage(message);
                } // release handler
    
                mLock.lock();
                mSendingMessage = false;
                result = POLL_CALLBACK;
            } else {
                // The last message left at the head of the queue determines the next wakeup time.
                mNextMessageUptime = messageEnvelope.uptime;
                break;
            }
        }
    
  • 最后处理response·

    response.request.callback->handleEvent(fd, events, data)

    // Invoke all response callbacks.先把response放入列表,延时处理
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
            ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
                    this, response.request.callback.get(), fd, events, data);
#endif
            // Invoke the callback.  Note that the file descriptor may be closed by
            // the callback (and potentially even reused) before the function returns so
            // we need to be a little careful when removing the file descriptor afterwards.
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd, response.request.seq);
            }

            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
    }
  • pthread_once_t 仅调用一次
  • pthread_key_t 线程局部对象,在不同的线程有不同的含义
static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
static pthread_key_t gTLSKey = 0;
  • 在获取线程对象时才通过pthread_once创建initTLSKey
static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;

sp<Looper> Looper::getForThread() {
    int result = pthread_once(& gTLSOnce, initTLSKey);
    return (Looper*)pthread_getspecific(gTLSKey);
}

void Looper::initTLSKey() {
    int error = pthread_key_create(&gTLSKey, threadDestructor);
}

messageQueue

上述讲解Looper时,应用自定义消息为message。那runnable或callable等task此类消息如何利用message封装呢?

  • class Task : public MessageHandler

    • 继承MessageHandler,说明这是一个可被Looper接收的消息

    • packaged_task<T()> 将入参的可调用对象封装为一个promise

template <typename F>
class Task : public MessageHandler {
    template <typename G>
    friend auto makeTask(G&&);

    explicit Task(F&& f) : mTask(std::move(f)) {}

    void handleMessage(const Message&) override { mTask(); }

    using T = std::invoke_result_t<F>;
    std::packaged_task<T()> mTask;
};

makeTask

makeTask将一个普通的可调用对象封装为一个promise

template <typename F>
inline auto makeTask(F&& f) {
    sp<Task<F>> task = new Task<F>(std::move(f));
    return std::make_pair(task, task->mTask.get_future());
}
  • SurfaceFlinger::schedule

    语义:将一个可调用对象封装为一个可被looper接收的消息及其一个feature,即在子线程调用schedule,该消息会在looper线程执行,子线程阻塞并等待结果返回

template <typename F, typename T>
inline std::future<T> SurfaceFlinger::schedule(F&& f) {
    auto [task, future] = makeTask(std::move(f));
    mEventQueue->postMessage(std::move(task));
    return std::move(future);
}
  • 将一个可调用对象封装为一个looper可接收的消息及其feature(任务完成可能有返回值),即pair[Task,feature],再将task传给looper处理,将feature传给调用者