预备知识:
eventfd
eventfd是Linux 2.6提供的一种系统调用,它可以用来实现事件通知。eventfd包含一个由内核维护的64位无符号整型计数器,创建eventfd时会返回一个文件描述符,进程可以通过对这个文件描述符进行read/write来读取/改变计数器的值,从而实现进程间通信。
#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
flags 可以是以下值的 OR 运算结果,用以改变 eventfd 的行为。
- EFD_CLOEXEC (since Linux 2.6.27)
文件被设置成 O_CLOEXEC,创建子进程 (fork) 时不继承父进程的文件描述符。 - EFD_NONBLOCK (since Linux 2.6.27)
文件被设置成 O_NONBLOCK,执行 read / write 操作时,不会阻塞。 - EFD_SEMAPHORE (since Linux 2.6.30)
提供类似信号量语义的 read 操作,简单说就是计数值 count 递减 1。
在 Linux 2.6.26 版本之前,没有使用参数 flags,必须指定为 0。muduo库中设置为0
操作方法
一切皆为文件是 Linux 内核设计的一种高度抽象,eventfd 的实现也不例外,我们可以使用操作文件的方法操作 eventfd。
- read(): 读取 count 值后置 0。如果设置 EFD_SEMAPHORE,读到的值为 1,同时 count 值递减 1。
- write(): 其实是执行 add 操作,累加 count 值。
- epoll()/poll()/select(): 支持 IO 多路复用操作。
- close(): 关闭文件描述符,eventfd 对象引用计数减 1,若减为 0,则释放 eventfd 对象资源
正题:
eventloop类中有这么两个变量:
int wakeupFd_; //eventfd对应的文件描述符
std::unique_ptr<Channel> wakeupChannel_; //eventfd对应的通道
eventloop类本身就维护了一个eventfd事件,这个事件是为了唤醒pool()的。此话怎讲?pool()毕竟是一个阻塞的函数,如果pool()所监听的事件在一段时间没有一个被激活,那么pool()就需要阻塞一段时间,如果此时我们不希望pool()阻塞在那里,要怎么办呢?我们就需要人工激活一个事件,打破pool的阻塞,这个事件就是eventfd事件。由于eventfd的跨线程的特性,我们就可在其他线程来打破evenloop对象所在线程的阻塞状态。木朵中提供的接口函数是:
void EventLoop::wakeup()
{
uint64_t one = 1;
ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);
if (n != sizeof one)
{
LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8";
}
}
来源:CSDN
作者:请叫我少爷
链接:https://blog.csdn.net/shaochuang1/article/details/103799540