AndroidT(13) init 进程 -- second stage init 中的 Epoll (三)

1. 概览

  在进入 second stage init 讲解之前,先来看看它事件监听及处理的机制 – Epoll 类,它实际上是对 epoll 的封装,使他变得更加适合再 init 中来跟踪事件以及分发触发方法等。整个类只有 4 个方法,在如此小巧的条件下实现了:事件监听的注册、卸载、跟踪以及收集事件的处理方法。这也意味着用户在注册后,在等待到事件后可以直接调用返回的处理方法列表。

2.整体使用骨架

  second init 中就是使用 Epoll 来跟踪事件并处理的,下面看下 Epoll 的使用流程。

//system\core\init\init.cpp
SecondStageMain
    //code 1
    Epoll epoll;
    epoll.Open();
    //code 2
    InstallInitNotifier(&epoll); 
        auto clear_eventfd = [] {
            uint64_t counter;
            TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter)));
        };
        epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd)
    //code 3
    while (true) {
        auto pending_functions = epoll.Wait(epoll_timeout);
        for (const auto& function : *pending_functions) {
            (*function)();
        }
    }

2.1 Epoll 的实例化 – code1

  Epoll 构造方法使用的是空的并没做什么,再 Open 中也是很简单的申请了一个 epoll 的句柄,并记录在 epoll_fd_类变量中。

//system\core\init\epoll.cpp
Result<void> Epoll::Open() {
    if (epoll_fd_ >= 0) return {};
    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));

    if (epoll_fd_ == -1) {
        return ErrnoError() << "epoll_create1 failed";
    }
    return {};
}

2.2 注册监听对象及处理方法 – code2

  Epoll 的监听对象只能是 fd 也就是文件句柄,它的回调方法原型如下,

//system\core\init\epoll.h
typedef std::function<void()> Handler;

  准备好监听对象和它的处理方法后就可以调用 RegisterHandler 注册到 Epoll 中了,来看看它的定义

Result<void> Epoll::RegisterHandler(int fd, Handler handler, uint32_t events)
    //a
    Info info;
    info.events = events;
    info.handler = std::make_shared<decltype(handler)>(std::move(handler));
    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(info));
    //b
    epoll_event ev;
    ev.events = events;
    ev.data.ptr = reinterpret_cast<void*>(&it->second);
    //c
    epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev)

  可见注册分为如下几个步骤
    a) 构建 Info 信息,其中包括它对 fd 中关心的事件类型,比如默认值 EPOLLIN,如果来这个事件就意味着 fd 句柄有数据可以读取了。还有用来处理 fd 数据的回调方法 handler。最后将 Info 和 fd 传入到 map epoll_handlers_中去,供后续使用。
    b) 设置 epoll_event 事件,该数据结构则是使用 epoll 的的标准构造方法,其中记录的 events 则是供 kernel 使用的和上面提到的 info.events值一致。并将 Info 实例作为它的私有数据。
    c) 最后调用 epoll_ctl 将 fd 添加到 epoll_fd_中去,如此系统才会对该 fd 实现监听。

2.3 等待事件并返回处理方法 – code3

  可见 Wait 方法会被循环调用不做退出的,这部分的概念和 Looper 还是很相似的,一个线程中只存在一个 Epoll中,并且 Epoll 的回调方法也是该线程执行的。下面就来看看它的实现

Result<std::vector<std::shared_ptr<Epoll::Handler>>> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout)
    //a
    const auto max_events = epoll_handlers_.size();
    epoll_event ev[max_events];
    auto num_events = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, ev, max_events, timeout_ms));
    //b
    std::vector<std::shared_ptr<Handler>> pending_functions;
    for (int i = 0; i < num_events; ++i) {
        auto& info = *reinterpret_cast<Info*>(ev[i].data.ptr);
        if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&
            (ev[i].events & EPOLLIN) != ev[i].events) {
            // This handler wants to know about exception events, and just got one.
            // Log something informational.
            LOG(ERROR) << "Received unexpected epoll event set: " << ev[i].events;
        }
        pending_functions.emplace_back(info.handler);
    }
    //c
    return pending_functions;

    a) 监听 epoll_fd_ 也就是 epoll句柄,只要挂在它上面的任何一个 fd 来事件了,epoll_wait 就会返回。当然超时的话也会返回。
    b) 循环遍历,看看是哪一个 epoll_event 来数据了,可以查看它的 epoll_event.events 成员变量。对于没有注册的事件则会报 Error 级别的log,但不会退出线程。最后再收集下需要处理的 fd 的回调方法,存入 pending_functions 向量中。
    c) 返回 pending_functions 向量,线程只要循环调用它内部的回调方法即可。

for (const auto& function : *pending_functions) {
    (*function)();
}

3. 总结

  可见使用了 Epoll 后,只要简单的几个步骤即可,作为框架使用还是相当方便的,其中隐藏了 epoll 的数据构造,用户只要提供 监听对象和对于的触发方法即可。不过值得注意的是,这是单线程的,如果注册太多的监听对象,或者某一个处理方法中耗时过长,还是相当影响其他监听事件的处理的。

你可能感兴趣的:(Android,System,&,Framework,android,android,init,system,framework)