在Android中,如果一个线程有消息循环(如UI线程),那么其他线程可以获取它的Handler对象,使用这个Handler对象发送消息到消息循环所在的线程,这个线程收到这个消息后,可以做一些操作,最典型的就是子线程执行耗时任务,然后发消息到主线程,主线程接到消息后更新UI。
Android中和消息循环相关的几个Java类如下:
1 Looper
用于循环遍历队列中的消息
2 MessageQueue
存储消息的队列,并且获取不到消息时,会阻塞等待
3 Handler
发送消息和处理消息
我们通过aosp的源码看一下,App主线程的消息循环是如何建立的。我们知道,App进程的入口函数可以认为是ActivityThread
的main
函数。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("" );
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
/// M: ANR Debug Mechanism
mAnrAppManager.setMessageLogger(Looper.myLooper());
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到,在该函数中,会调用Looper.prepareMainLooper()
,Looper.loop()
来创建App主线程的消息循环。
但是不只是主线程才可以有消息循环,通过正确的调用Looper.prepare()
和Looper.loop()
,所有线程都可以创建消息循环。下面是在App的某个线程中创建消息循环的事例代码:
public class MyLooperThread extends Thread {
private static final String NAME = "MyLooperThread";
private Handler mHandler;
public MyLooperThread() {
super(NAME);
}
public Handler getHandler() {
return mHandler;
}
@Override
public void run() {
Looper.prepare();
mHandler = new Handler(Looper.myLooper());
Looper.loop();
}
}
从另一个线程中可以获取MyLooperThread
的Handler对象,并且发送消息:
@Override
public void onClick(View view) {
if (mMyHandler == null) {
mMyHandler = mMyLooperThread.getHandler();
}
mMyHandler.post(new Runnable() {
@Override
public void run() {
Log.e(TAG, "Current Thread is " +
Thread.currentThread().getName());
}
});
}
下面我们根据aosp
的源码(这里我们使用aosp 8.1的代码)来分析,这个消息循环是如何实现的。
首先看Looper的prepare方法的实现:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
首先prepare
会创建一个Looper
对象,然后将Looper
对象存放到ThreadLocal
中,将这个Looper
对象和当前线程关联在一起。Looper
的初始化在构造函数中,我们接下去看Looper
的构造函数:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,在Looper
中会创建一个MessageQueue
对象。我们接下去看MessageQueue
的构造函数:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue
的构造函数调用了nativeInit
方法:
private native static long nativeInit();
接下来继续看JNI中的实现。nativeInit
方法的native实现在frameworks/base/core/jni/android_os_MessageQueue.cpp
中,对应的方法是android_os_MessageQueue_nativeInit
:
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
在这个方法中,创建了一个NativeMessageQueue
对象,我们接下去看NativeMessageQueue
的构造函数,也实现在frameworks/base/core/jni/android_os_MessageQueue.cpp
中:
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
在NativeMessageQueue
中创建了一个Looper
,这个Looper
是native层的,实现在system/core/libutils/Looper.cpp
中。接下来我们从Looper
的构造函数开始:
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
strerror(errno));
AutoMutex _l(mLock);
rebuildEpollLocked();
}
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
close(mEpollFd);
}
// Allocate the new epoll instance and register the wake pipe.
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
strerror(errno));
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
我们只解释最关键的地方,其他不相关的代码,先不解释。
在Looper
构造方法中,首先调用mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
创建一个mWakeEventFd
,这个mWakeEventFd
可以认为是一个用于进程/线程间通信的文件,不是真实的磁盘文件,而是linux内核的虚拟文件系统(VFS)抽象出来的,它的底层只是对应某个数据结构,该数据结构里有一些字段来标示它的状态,具体实现细节我们这里不深究。
然后调用rebuildEpollLocked
函数,该函数中,调用mEpollFd = epoll_create(EPOLL_SIZE_HINT);
创建一个mEpollFd
,这个mEpollFd
代表一个epoll
实例,epoll
实例用于支持高效的IO多路复用,这里我们先不管什么是IO多路复用,只需要知道它可以阻塞监听一个或多个文件,当被监听的文件发生变化的时候,会被唤醒。
最后调用epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
将上面创建的mWakeEventFd
交给epoll
来管理。可以认为使用epoll
来监听mWakeEventFd
,当有人向mWakeEventFd
中写入数据的时候,epoll
会被唤醒。
接下来我们分析Looper.loop
这条线(这里指的是Java层的Looper)。Looper
中loop
的实现如下:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
......
该方法调用MessageQueue
的next
方法去获取消息,我们知道next
方法是会睡眠的,如果MessageQueue
里面没有消息,那么会next
方法会一直等待,直到有一个新的消息发到了MessageQueue
中,当前线程会被唤醒,然后next
方法继续执行,并返回这个新的消息(这里我们不考虑延迟消息这种情况)。我们接下去继续分析next
方法,看它是如何实现睡眠等待的。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
......
......
关键的地方是调用nativePollOnce
:
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
对应的native实现是frameworks/base/core/jni/android_os_MessageQueue.cpp
中的android_os_MessageQueue_nativePollOnce
函数:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
调到nativeMessageQueue
的pollOnce
,该函数同样实现在frameworks/base/core/jni/android_os_MessageQueue.cpp
中:
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
这里调用了native层的Looper
的pollOnce
函数,该函数实现在system/core/libutils/Looper.cpp
中:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
我们略过一些不重要的逻辑,直接看pollInner
函数:
int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d",
this, mNextMessageUptime - now, timeoutMillis);
#endif
}
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mPolling = false;
......
......
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 {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
......
......
在这里,关键的地方是epoll_wait
的调用和awoken
的调用。
在pollInner
里,调用epoll_wait
阻塞监听上一步使用eventfd()
创建的mWakeEventFd
,只要有人向mWakeEventFd
里写入一个64位的无符号整数(uint64_t)
,epoll_wait
就会被唤醒,并且将唤醒事件填充到eventItems
数组中,然后通过for循环检查epoll_wait
返回的事件,如果是mWakeEventFd
的EPOLLIN
事件,则说明有人向mWakeEventFd
中写入了数据。然后调用awoken
读出mWakeEventFd
中的数据。我们看一下awoken
的实现:
void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ awoken", this);
#endif
uint64_t counter;
TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
可以看到,awoken
确实只是读出mWakeEventFd
中的数据而已,并且读出的数据也没什么用。
既然已经唤醒了,为什么还要读出mWakeEventFd
中的数据呢?这个数据并没有什么意义。这是由于epoll
的触发模式决定的。
epoll
的触发模式分为两种:水平触发和边缘触发。水平触发的意思是只要被监听的fd
中存在数据,epoll_wait
就一直返回,只有将fd
中的数据读出,才会再次阻塞,等待下次写入再唤醒。边缘触发是只有向fd
中写入数据,才会触发epoll_wait
返回,epoll_wait
返回后,不必读出fd
中的数据,因为不管fd
中是否有数据,epoll_wait
都会再次等待有人写入。
在Looper.cpp
中,使用的是水平触发,所以需要读出里面的数据,epoll_wait
才会正常等待下一次事件。
是水平触发还是边缘触发,是在通过epoll_ctl
监听文件的时候指定的,通过struct epoll_event
的events
字段指定。默认是水平触发,只需要指定eventItem.events = EPOLLIN
,下面是Looper.cpp
中通过epoll
监听mWakeEventFd
的代码:
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
如果采用边缘触发模式,则需要指定EPOLLET
:
eventItem.events = EPOLLIN | EPOLLET;
当执行完Looper.prepare()
后,就可以获取线程的handler
对象,通过handler
对象向消息队列中发送消息,消息的处理就会在当前线程中处理。下面看一下,为什么发送一个消息,就能唤醒当前线程,并且在当前线程处理消息。
先看Handler
的sendMessage
方法(post
也会调用sendMessage
方法):
Handler.java
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
private native static void nativeWake(long ptr);
Handler
的sendMessage
一直会调到MessageQueue
的enqueueMessage
中,在equeueMessage
中,先将消息放到队列上,然后调用nativeWake
来唤醒队列。
nativeWake
对应的jni方法,实现在frameworks/base/core/jni/android_os_MessageQueue.cpp
中:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
调用了NativeMessageQueue
的wake
方法,该方法也实现在frameworks/base/core/jni/android_os_MessageQueue.cpp
中:
void NativeMessageQueue::wake() {
mLooper->wake();
}
继续调用到Looper
的wake
方法,下面继续看Looper
的wake
方法,该方法实现在system/core/libutils/Looper.cpp
中:
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
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));
}
}
}
Looper
的wake
方法中,向mWakeEventFd
写入一个uint64_t
。前面分析Looper.prepare
,Looper.loop
时讲到,mWakeEventFd
是通过eventfd()
创建的一个专门用于进程(或线程)之间通知的文件,并且该文件通过epoll
监控,所以这里写入一个数据,就可以唤醒epoll_wait
,也就是唤醒消息循环线程,消息循环线程被唤醒后,就会取队列中的消息去执行,如果队列中没有消息,又会继续调到epoll_wait
进行等待。当下一次有人向mWakeEventFd
写数据,也就是在java层中有人使用handler发送消息,则该线程又会被唤醒去执行消息。
经过上面的分析,我们可以知道,Android的消息循环的基础是epoll
监控eventfd
,对应关系如下:
Looper.prepare
对应eventfd()
,epoll_create
,epoll_ctl
Looper.loop
对应epoll_wait
Handler.sendMessage
对应write eventfd
其实即使我们不深入源码分析,我们平时做开发,也可以看到epoll
的存在,只是可能没有深入去思考它到底是什么。下面就列举一下平时看到的epoll
:
在使用ps
打印进程信息时,会有一列叫做WCHAN
,WCHAN
的意义是,当前进程正在哪个内核函数上等待。
可以看到,这个进程的WCHAN
为Sys_epoll_
,其实这里没显示完整,完整的WCHAN
值为Sys_epoll_wait
,可以通过查看/proc/pid/wchan
来查看:
epoll_wait
对应的系统调用就是Sys_epoll_wait
,所以可以确认,当前进程正在等待epoll_wait
返回,因为消息循环是通过epoll
实现的,所以得到的结论就是:这个进程正在消息队列上等待。
在分析anr的时候,经常看到主线程或者带有消息循环的线程的状态是这样的:
这种状态也是在消息队列上等待。调用栈如下,
Looper.loop
MessageQueue.next
MessageQueue.nativePollOnce
android_os_MessageQueue_nativePollOnce
android::NativeMessageQueue.pollOnce
android::Looper.pollOnce
android::Looper.pollInner
epoll_wait
和我们之前分析源码的调用流程是一致的。
epoll
是一种IO多路复用技术,也就是通过一个线程可以监控多个fd
。像一些普通的系统调用,如read
,只能监控一个fd
,如果要监控多个fd
,就要启动多个线程,如果需要监控的fd
比较多,如大型服务器中的socket
很多,如果每个socket
都起一个线程来监控,系统中会出现大量线程,对系统造成很大的压力。
而epoll
使用一个线程,就能监控多个fd
。大大降低了系统中的线程数量。
如何用epoll
监控多个fd
呢?只需要调用epoll_ctl
将fd
加到它的监控集合中即可。如下:
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
c++层的Looper
是支持监控多个fd
的:
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
}
int Looper::removeFd(int fd) {
return removeFd(fd, -1);
}
其实addFd
的实现就是调用epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
,removeFd
的实现就是调用epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL)
。
而Android的消息循环,只监控了一个mWakeEventFd
,所以Android的消息循环只使用了Looper.cpp
的部分功能。
使用c++写一个用epoll
监控eventfd
的示例,代码量很小:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main() {
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
pid_t pid = fork();
if (pid == 0) {
//child process
for (int i=0; i<5; i++) {
uint64_t val = i;
std::cout << "child process write value to event fd " << val << std::endl;
write(efd, &val, sizeof(val));
sleep(2);
}
} else if(pid > 0) {
// parent process
int epoll_fd = epoll_create(1);
struct epoll_event event;
memset(&event, 0, sizeof(epoll_event));
event.events = EPOLLIN | EPOLLET;
event.data.fd = efd;
int result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, efd, &event);
while (true) {
struct epoll_event events[1];
int event_count = epoll_wait(epoll_fd, events, 1, -1);
std::cout << "epoll_wait returned " << std::endl;
for (int j=0; j<event_count; j++) {
if (events[0].data.fd == efd) {
if (events[0].events & EPOLLIN) {
uint64_t val;
read(efd, &val, sizeof(uint64_t));
std::cout << "parent process read value from event fd : " << val << std::endl;
}
}
}
}
}
}
eventfd:
http://man7.org/linux/man-pages/man2/eventfd.2.html
epoll:
http://man7.org/linux/man-pages/man7/epoll.7.html