ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

664-Linux高效的wakeupfd进程间通信

2021-10-03 20:32:47  阅读:219  来源: 互联网

标签:EventLoop int eventfd 664 间通信 Linux EFD pool


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 对象资源

我们看看muduo库是怎么使用的

eventloop类中有这么两个变量:

int wakeupFd_;                           //eventfd对应的文件描述符
std::unique_ptr<Channel> wakeupChannel_; //eventfd对应的通道

eventloop类本身就维护了一个eventfd事件,这个事件是为了唤醒pool()的。此话怎讲?pool()毕竟是一个阻塞的函数,如果pool()所监听的事件在一段时间没有一个被激活,那么pool()就需要阻塞一段时间,如果此时我们不希望pool()阻塞在那里,要怎么办呢?我们就需要人工激活一个事件,打破pool的阻塞,这个事件就是eventfd事件。由于eventfd的跨线程的特性,我们就可在其他线程来打破evenloop对象所在线程的阻塞状态。muduo库中提供的接口函数是:

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";
  }
}
//创建wakeupfd,用来notify唤醒subReactor处理新来的channel
int createEventfd()
{
    int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (evtfd < 0)
    {
        LOG_FATAL("eventfd error:%d \n", errno);
    }
    return evtfd;
}

EventLoop::EventLoop()//构造函数 
    : looping_(false)
    , quit_(false)
    , callingPendingFunctors_(false)
    , threadId_(CurrentThread::tid())
    , poller_(Poller::newDefaultPoller(this))
    , wakeupFd_(createEventfd())
    , wakeupChannel_(new Channel(this, wakeupFd_))
{
    LOG_DEBUG("EventLoop created %p in thread %d \n", this, threadId_);
    if (t_loopInThisThread)//这个线程已经有loop了,就不创建了 
    {
        LOG_FATAL("Another EventLoop %p exists in this thread %d \n", t_loopInThisThread, threadId_);
    }
    else//这个线程还没有loop,创建 
    {
        t_loopInThisThread = this;
    }

    //设置wakeupfd的事件类型以及发生事件后的回调操作
    wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));
    //每一个eventloop都将监听wakeupchannel的EPOLLIN读事件了
    wakeupChannel_->enableReading();
}

标签:EventLoop,int,eventfd,664,间通信,Linux,EFD,pool
来源: https://blog.csdn.net/LINZEYU666/article/details/120598023

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有