muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制
muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制
上篇文章为EventLoop添加了一个定时器Fd,为EventLoop增加了3个接口:runAfter()、runAt()、runEvery()、这三个接口用于处理定时任务和周期任务. 底层通过封装TimerFd实现。
TimerId runAt(const TimeStamp& time, const NetCallBacks::TimerCallBack& cb);
TimerId runAfter(double delay, const NetCallBacks::TimerCallBack& cb);
TimerId runEvery(double interval, const NetCallBacks::TimerCallBack& cb);
今天为EventLoop添加另一个Fd:EventFd, 用于实现线程间的事件通知机制.本文会先介绍eventfd的使用,然后给出muduo中EventLoop对eventfd的封装.
eventfd的使用
eventfd系统函数
eventfd - 事件通知文件描述符
#include <sys/eventfd.h>
int eventfd(unsigned int initval ,int flags );
创建一个能被用户应用程序用于时间等待唤醒机制的eventfd对象.
initval
:
eventfd()创建一个可用作事件的“eventfd对象”用户空间应用程序和内核等待/通知机制通知用户空间应用程序的事件。该对象包含一个由内核维护的无符号64位整型(uint64_t)计数器。此计数器的初始值通过initval指定。一般设0.
flags
:
以下标志中按位OR运算以更改eventfd()的行为,(文件中常用的这两个flags肯定都懂意思吧,就不翻译了,第三个信号量的不管它.):
EFD_CLOEXEC (since Linux 2.6.27)
Set the close-on-exec (FD_CLOEXEC) flag on the new file
descriptor. See the description of the O_CLOEXEC flag in
open(2) for reasons why this may be useful.
EFD_NONBLOCK (since Linux 2.6.27)
Set the O_NONBLOCK file status flag on the new open file
description. Using this flag saves extra calls to fcntl(2) to
achieve the same result.
EFD_SEMAPHORE (since Linux 2.6.30)
Provide semaphore-like semantics for reads from the new file
descriptor. See below.
read(2)
成功读取返回一个8byte的整数。read(2)如果提供的缓冲区的大小小于8个字节返回错误EINVAL
write (2)
将缓冲区写入的8字节整形值加到内核计数器上。可以写入的最大值
是计数器中是最大的无符号64位值减1(即0xfffffffffffffffe)。
返回值:
On success, eventfd() returns a new eventfd file descriptor. On error, -1 is returned and errno is set to indicate the error.
使用示例
#include <iostream>
#include <assert.h>
#include <poll.h>
#include <signal.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include <string.h>
#include <thread>
static int s_efd = 0;
int createEventfd()
{
int evtfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
std::cout << "createEventfd() fd : " << evtfd << std::endl;
if (evtfd < 0)
{
std::cout << "Failed in eventfd\n";
abort();
}
return evtfd;
}
void testThread()
{
int timeout = 0;
while(timeout < 3) {
sleep(1);
timeout++;
}
uint64_t one = 1;
ssize_t n = write(s_efd, &one, sizeof one);
if(n != sizeof one)
{
std::cout << " writes " << n << " bytes instead of 8\n";
}
}
int main()
{
s_efd = createEventfd();
fd_set rdset;
FD_ZERO(&rdset);
FD_SET(s_efd, &rdset);
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
std::thread t(testThread);
while(1)
{
if(select(s_efd + 1, &rdset, NULL, NULL, &timeout) == 0)
{
std::cout << "timeout\n";
timeout.tv_sec = 1;
timeout.tv_usec = 0;
FD_SET(s_efd, &rdset);
continue;
}
uint64_t one = 0;
ssize_t n = read(s_efd, &one, sizeof one);
if(n != sizeof one)
{
std::cout << " read " << n << " bytes instead of 8\n";
}
std::cout << " wakeup !\n";
break;
}
t.join();
close(s_efd);
return 0;
}
./test.out
createEventfd() fd : 3
timeout
timeout
timeout
wakeup !
eventfd 单纯的使用文件描述符实现的线程间的通知机制,可以很好的融入select、poll、epoll的I/O复用机制中.
EventLoop对eventfd的封装
所增加的接口及成员:
typedef std::function<void()> Functor;
void runInLoop(const Functor& cb);
void wakeup(); //是写m_wakeupFd 通知poll 处理读事件.
void queueInLoop(const Functor& cb);
private:
//used to waked up
void handleRead();
void doPendingFunctors();
int m_wakeupFd;
std::unique_ptr<Channel> p_wakeupChannel;
mutable MutexLock m_mutex;
bool m_callingPendingFunctors; /* atomic */
std::vector<Functor> m_pendingFunctors; // @GuardedBy mutex_
工作时序
(runInLoop() -> quueInLoop())/queueInLoop() -> wakeup() -> poll() -> handleRead() -> doPendingFunctors()
runInLoop()
如果用户在当前IO线程调用这个函数, 回调会同步进行; 如果用户在其他线程调用runInLoop(),cb会被加入队列, IO线程会被唤醒来调用这个Functor.
void EventLoop::runInLoop(const Functor& cb)
{
if(isInloopThread())
cb();
else
queueInLoop(cb);
}
queueInLoop()
会将回调添加到容器,同时通过wakeup()唤醒poll()调用容器内的回调.
void EventLoop::queueInLoop(const Functor& cb)
{
LOG_TRACE << "EventLoop::queueInLoop()";
{
MutexLockGuard lock(m_mutex);
m_pendingFunctors.push_back(std::move(cb));
}
if(!isInloopThread())
{
wakeup();
}
}
内部实现,
wakeup()
写已注册到poll的eventfd 通知poll 处理读事件.
// m_wakeupFd(createEventfd()),
// p_wakeupChannel(new Channel(this, m_wakeupFd)),
void EventLoop::wakeup()
{
uint64_t one = 1;
ssize_t n = sockets::write(m_wakeupFd, &one, sizeof one);
if(n != sizeof one)
{
LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8";
}
}
handleRead()
poll回调读事件,处理eventfd.
void EventLoop::handleRead() //handle wakeup Fd
{
LOG_TRACE << "EventLoop::handleRead() handle wakeup Fd";
uint64_t one = 1;
ssize_t n = sockets::read(m_wakeupFd, &one, sizeof one);
if(n != sizeof one)
{
LOG_ERROR << "EventLoop::handleRead() reads " << n << "bytes instead of 8";
}
doPendingFunctors();
}
doPendingFunctors()
处理挂起的事件.
void EventLoop::doPendingFunctors()
{
LOG_TRACE << "EventLoop::doPendingFunctors()";
std::vector<Functor> functors;
m_callingPendingFunctors = true;
{
MutexLockGuard lock(m_mutex);
functors.swap(m_pendingFunctors);
}
for(size_t i = 0; i < functors.size(); ++i)
{
functors[i]();
}
m_callingPendingFunctors = false;
}
总结
本文主要介绍了muduo中EventLoop通过 通过封装一层eventfd实现的runInLoop()函数,使得其他线程想往EventLoop所在的I/O线程注册任务成为可能.
下篇文章会写Connector和Acceptor,链接器和监听器 实现第一条链接。
muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制的更多相关文章
- muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor
目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...
- muduo网络库学习笔记(三)TimerQueue定时器队列
目录 muduo网络库学习笔记(三)TimerQueue定时器队列 Linux中的时间函数 timerfd简单使用介绍 timerfd示例 muduo中对timerfd的封装 TimerQueue的结 ...
- muduo网络库学习笔记(10):定时器的实现
传统的Reactor通过控制select和poll的等待时间来实现定时,而现在在Linux中有了timerfd,我们可以用和处理IO事件相同的方式来处理定时,代码的一致性更好. 一.为什么选择time ...
- muduo 网络库学习之路(一)
前提介绍: 本人是一名大三学生,主要使用C++开发,兴趣是高性能的服务器方面. 网络开发离不开网络库,所以今天开始学一个新的网络库,陈老师的muduo库 我参考的书籍就是陈老师自己关于muduo而编著 ...
- muduo网络库学习之MutexLock类、MutexLockGuard类、Condition类、CountDownLatch类封装中的知识点
一.MutexLock 类 class MutexLock : boost::noncopyable 二.MutexLockGuard类 class MutexLockGuard : bo ...
- Linux多线程服务端编程 使用muduo C++网络库 学习笔记 日志log
代码来自陈硕开源代码库 muduo中 地址是https://github.com/chenshuo/muduo #pragma once #include <string> #define ...
- 网络协议学习笔记(四)传输层的UDP和TCP
概述 传输层里比较重要的两个协议,一个是 TCP,一个是 UDP.对于不从事底层开发的人员来讲,或者对于开发应用的人来讲,最常用的就是这两个协议.由于面试的时候,这两个协议经常会被放在一起问,因而我在 ...
- muduo网络库架构总结
目录 muduo网络库简介 muduo网络库模块组成 Recator反应器 EventLoop的两个组件 TimerQueue定时器 Eventfd Connector和Acceptor连接器和监听器 ...
- 陈硕 - Linux 多线程服务端编程 - muduo 网络库作者
http://chenshuo.com/book/ Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)http://blog.csdn.net/nk_test/ ...
随机推荐
- 【转】Nginx学习---深入浅出Nginx的介绍
[原文]https://www.toutiao.com/i6595428119933354500/ Nginx是一款轻量级的Web服务器.反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在 ...
- SDN期末
一.项目描述 负载均衡程序 二.小组分工 组名:我们真的很弱 组员:李佳铭.吴森杰.张岚鑫.薛宇涛.杨凌澜 三.代码演示及过程描述 四.课程总结
- ansible(一)
Ansible简介 Ansible:Ansible的核心程序Host Lnventory:记录了每一个由Ansible管理的主机信息,信息包括ssh端口,root帐号密码,ip地址等等.可以通过fil ...
- Spring-AOP SpringBoot自动配置和启动Spring AOP
SpringBoot 会使用 @Conditional* 注解来进行判断是否需要自动启动 AOP,如果 classpath 下有 spring-aop 的 jar 和有 EnableAspectJAu ...
- 乐视4.14硬件免费日de用户体验
此贴用于记录2016年4月14日乐视硬件免费日购买X65超级电视的用户体验.后续将动态更新 我是乐视电视的第一批用户,从乐视上市第一批超级电视,我先后帮助家人.同事.朋友买了6台乐视超级电视,也算是乐 ...
- HTTPS_SSL apache认证、配置的、步骤以及原理说明
一 .1.单向认证,就是传输的数据加密过了,但是不会校验客户端的来源 2.双向认证,如果客户端浏览器没有导入客户端证书,是访问不了web系统的,找不到地址,想要用系统的人没有证书就访问不了系统HTTP ...
- 使用docker-compose运行Django
1.新建空目录 2.进入该目录新建Dockerfile文件,并在该Dockerfile文件添加如下内容 FROM python:3 ENV PYTHONUNBUFFERED 1 RUN mkdir / ...
- 笔记一:CSS选择器
0.前言:无论学什么,前端都是绕不开的一门技术,对于不同的人需求不同,作为一个python开发者不仅需要能读懂基本的html/css以及js代码,还要会使用它的常用的标签,以及了解比较有用的标签,把逻 ...
- Android分享到微信时点击分享无反应的问题解决(注意事项)
问题描述:调用分享到微信的sdk点击程序的分享按钮程序无反应 解决办法: 问题原因:微信分享对客户端的要求相当严格,首先你必须在给应用注册账号时,把注册信息相对的填写完整,其中“应用包名”,“应用的签 ...
- MVC的BundleConfig应用
1.MVC可以通过BundleConfig类来配置css和js的统一引用,分别通过StyleBundle和ScriptBundle来创建. 2.可以在母版页中统一加载设置在BundleConfig.c ...