【Muduo源码剖析笔记】 网络库之EventLoop

【Muduo源码剖析笔记】 网络库之EventLoop、Channel

EventLoop

声明了三个类,Channel、Poller和TimerQueue。

定义了Eventloop类。每个子线程拥有一个线程特有数据t_loopInThisThread,这是个Evenloop类型指针,子线程会在堆栈中创建new出自己对应的Eventloop对象,通过t_loopInThisThread这个指针访问和操作。

数据成员如下:

typedef std::function<void()> Functor;
typedef std::vector<Channel*> ChannelList;

bool looping_; /* atomic */
std::atomic<bool> quit_;
bool eventHandling_; /* atomic */
bool callingPendingFunctors_; /* atomic */
int64_t iteration_;
const pid_t threadId_;
Timestamp pollReturnTime_;
std::unique_ptr<Poller> poller_;
std::unique_ptr<TimerQueue> timerQueue_;
int wakeupFd_;
// unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
std::unique_ptr<Channel> wakeupChannel_;
boost::any context_;

// scratch variables
ChannelList activeChannels_;
Channel* currentActiveChannel_;

mutable MutexLock mutex_;
std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);

定义了一个全局变量 __thread EventLoop* t_loopInThisThread = 0。每个线程都有这么一个Evenloop类型指针。

int createEventfd()

全局函数,创建一个非阻塞的文件,返回该文件描述符。

Class IgnoreSigPipe

对一个对端已经关闭的socket调用两次write, 第二次将会生成SIGPIPE信号, 该信号默认结束进程

定义了一个类,在namespace的命名空间,初始化函数会让遇到SIGPIPE信号的时候,执行SIG_IGN函数。

EventLoop::EventLoop()

将looping_置为false,quit_z置为false,eventHandling_置为false,iteration_初始化为0。

threadId_(CurrentThread::tid()),线程id会用CurrentThread中的tid函数返回。也就是这个构造函数会被子线程调用,应该是使用new。

poller_(Poller::newDefaultPoller(this)):

timerQueue_(new TimerQueue(this)):new一个新的指针初始化一个unique_ptr指针。

wakeupFd_(createEventfd()):创建一个新的文件描述符。

wakeupChannel_(new Channel(this, wakeupFd_)):会用刚刚创建新的文件描述符,初始化一个指向Channel类型的unique_ptr。

currentActiveChannel_(NULL):Channel类型的指针,置为NULL。

函数中会将EventLoop创建的信息写进LOG中(DEBUG级别)。

如果创建EventLoop这个线程是已经有t_loopInThisThread这个指针指向的数据的话,那么就会报LOG_FATAL级别的错误。

不然就将t_loopInThisThread指针指向现在正在创建的Eventloop。

给wakeupchannel设定回调函数,这个回调函数读wakeupFd_,获取一个文件描述符。

然后通过wakeupChannel_开启Reading模式。

~EventLoop()

会输出哪个线程的Eventloop在哪个线程被销毁掉。

调用wakeupChannel的disableAll()函数,然后remove()。

之后把wakeupFd_文件描述符关闭。

把自己线程的loop置为NULL。(??如果关闭的是别人的loop呢)

EventLoop::loop()

首先确保Eventloop不是True(也就是这个loop没有启动),然后需要确认运行这个loop的线程是创建这个Eventloop的线程。然后把quit_置为false。

对LOG写入这个Eventloop要开始启动了的信息。

进入while(!quit_)循环。

首先将activeChannels_清空,activeChannels_是一个装有Channel指针的vector。

然后调用poller_->poll(kPollTimeMs, &activeChannels_),然后对iteration自加。

把eventHadnling设置为true;

然后使用for each的语法把channel从activeChannels中拿出来,置给currentActiveChannel_,然后调用Channel的

currentActiveChannel_->handleEvent(pollReturnTime_)。对这个对象的发生的事件进行处理。

作完后就把当前活跃管道设置为NULL,eventhandling也会设置为false。调用doPendingFunctors。

EventLoop::doPendingFunctors()

会调用pendingFunctors_中的函数。

void assertInLoopThread()

确保运行这个loop的线程是创建这个loop的线程。

EventLoop::updateChannel(Channel* channel)

首先确保这个Channel的主人是这个loop的,然后确认现在运行的loop是本人,之后调用poller_的updateChannel(channel)。

EventLoop::removeChannel(Channel* channel)

Channel会调用这个函数,把自己作为this指针传入,如果eventHandling_为真,那么会确认当前活跃的Channel为channel或者在列表。

调用poller_ 除掉removeChannle。

void EventLoop::quit()

把quit_标识设为true,如果调用这个loop的人不是创建这个loop的人

会调用wakeup()。

void EventLoop::wakeup()

wakeup会往wakeupfd中写入一个1。

Channel

定义了Channel类

封装了一个fd_文件描述符对应的事件,相应要发生什么事件需要处理时的操作函数等等。具体的调用由Eventloop调用。

typedef std::function<void()> EventCallback;
//事件回调函数为void()的函数对象
typedef std::function<void(Timestamp)> ReadEventCallback;
//写时间的回调函数?

数据成员:

  static const int kNoneEvent;
  static const int kReadEvent;
  static const int kWriteEvent;

  EventLoop* loop_;  //channel所属的loop
  const int  fd_;   //channel负责的文件描述符
  int        events_;   //注册的事件
  int        revents_; //poller设置的就绪的事件
  int        index_; // //被poller使用的下标
  bool       logHup_;

  std::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_;
  bool addedToLoop_;
  ReadEventCallback readCallback_;
  EventCallback writeCallback_;
  EventCallback closeCallback_;
  EventCallback errorCallback_;

loop_表示这个Channel隶属于哪个EventLoop,fd_表示这个Channel的对象是哪个文件描述符。然后保存了发生写读等事件的回调函数。

Channel(EventLoop* loop, int fd__)

接受一个loop指针和文件描述符,用loop初始化loop_和fd_初始化fd_。事件数量为0。

Channel::~Channel()
void Channel::tie(const std::shared_ptr& obj)

接受一个指向void的指针引用obj,把引用给tie_。并且设置tied_标志位为true。表示这个弱指针弱绑定了某个对象。

void Channel::update()

addedToLoop_会被置为真,然后调用loop_的updateChannel函数。updateChannel函数就会把调用poller_的updateChannel。也就是会把这个Channel加入到Eventloop中。

void Channel::remove()

确认events_ == kNoneEvent,然后将addedToloop的标识置为否,调用loop_的removeChannel把自己取出。

void Channel::handleEvent(Timestamp receiveTime)

创建一个局部的的shared_ptr 指针,如果tied_设置为true,就会调用tie_绑定的对象返回一个shared_ptr对象指针,然后调用handleEventWithGuard。

这样是为了调用的Channel不被析构吧?和线程安全有关。

如果tied_标志为flase,直接调用handleEventWithGuard。

void Channel::handleEventWithGuard(Timestamp receiveTime)

首先把eventHandling_置为真,然后

LOG_TRACE << reventsToString()

然后会根据revents_的状况调用不同的回调函数或者作不同的处理,最后将eventHandling_。

Poller

typedef std::map<int, Channel*> ChannelMap;

ChannelMap channels_的对象,一个EventLoop* ownerLoop_指向自己的loop。Poller的实现支持epoll和poll两种模式。使用类的继承实现。多态的方式。声明了很多接口提供loop使用,具体实现的时候借用多态。

Poller(EventLoop* loop)

就使用loop初始化自己的指针。

static Poller* newDefaultPoller(EventLoop* loop)

接受一个loop作为初始化参数,根据使用那种poll分别new出继承对象。比如使用Poll的话,就会new出pollpoller(loop)对象,返回赋给Poller*。

Poller::hasChannel(Channel* channel)
  ChannelMap::const_iterator it = channels_.find(channel->fd());
  return it != channels_.end() && it->second == channel;

判断poller里面的注册事件是否有参数channel中指定的事件,使用channel中的文件描述符作为参数寻找。

EPollPoller

typedef std::vector EventList;

派生类,派生于Poller。包含了一个epollfd_和Eventlist数据类型。

epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。文件描述符由epoll_create创建。使用初始化事件列表大小值初始化Eventlist,名为events。

EPollPoller::EPollPoller(EventLoop* loop)

用Loop初始化Poller基类部分,然后调用epoll_create返回标识epoll事件表的文件描述符初始化epollfd_。

void EPollPoller::update(int operation, Channel* channel)

每个事件有一个指针,会指向这个事件所属的管道!!

events的data是由我们自己填进去的,你填一个fd也行

我们直接把这个fd所属的管道指针填进去了。

  struct epoll_event event; //准备一个event变量
  memZero(&event, sizeof event);
  event.events = channel->events(); //把这个管道的注册事件类型写入这个event变量中。
  event.data.ptr = channel;//把evetns.data的指针指向channel
  int fd = channel->fd(); //把这个管道的fd取出来。

然后调用

epoll_ctl(epollfd_, operation, fd, &event)

将这个事件注册进去。这个event是我们注册进去的事件,后续如果这个事件被触发了,会原封不动得输出出来。在epoll_wait的epoll_event*指针中,这个列表就指向了这些被触发的事件。

void EPollPoller::updateChannel(Channel* channel)

参数为要插入epoll事件符中的channel。

首先取出channel的索引,取名为局部变量index。写入LOG信息,表示文件表数字为fd的events事件。

如果索引是-1,那么首先确保channels找不到这个文件描述符的channel,然后把这个channel写入channels_中。以fd为map的键值。

channels_[fd] = channel

如果索引是2,判断是否能找到这个管道中fd对应的管道,然后判断channels_ map结构中的fd键值对应的管道等不等于这个管道。

然后将这个管道得index设置为1(kAdded)。

调用update函数,操作数是把这个管道加入到epoll事件表中。

如果索引不是-1,也不是2,那么就对管道进行更新。

首先要确保这个管道是在poller的注册列表中的,如果这个channel是isNoneEvent,就把这个管道删除。否则调用update(EPOLL_CTL_MOD, channel);

Timestamp poll(int timeoutMs, ChannelList* activeChannels) override

是重写的虚函数。接受一个超时参数,和一个ChannelList参数。是包含Channel的Vector。

调用epollwait

int numEvents = ::epoll_wait(epollfd_,
                               &*events_.begin(),
                               static_cast<int>(events_.size()),
                               timeoutMs);

epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

events Vector的头指针被解引用然后取址赋予给epoll_event*。

如果发生的事件数量大于0,那么就会把发生事件信息写入LOG中。

然后调用fillActiveChannels(numEvents, activeChannels)

功能是把这些事件对应的管道都给加入到activeChannels中。

void EPollPoller::fillActiveChannels(int numEvents,ChannelList* activeChannels) const

首先确保发生的事件数量少于等于事件列表的长度。

遍历发生的事件,首先创建局部变量channel

Channel* channel = static_cast<Channel*>(events_[i].data.ptr)

把这个事件对应的管道给找出来

然后把这个事件的类型设置到这个管道的就绪事件中

最后把这个管道push到activeChannels中。

Eventloopthread

主线程的类,包含了一个Eventloop *loop_ 数据成员,布尔值exiting,Thread类型的值thread_(表示这个loop在哪个线程运行)。一个锁和一个条件变量锁,和一个线程初始回调函数callback_。其需要以Evenloop*为参数。

typedef std::function<void(EventLoop*)> ThreadInitCallback;
EventLoopThread::threadFunc()

是用于初始化Thread类的函数,其首先定义了一个局部变量

Eventloop loop, 在这里子线程创建了eventloop。

如果有创建线程的时候就要调用的函数的话,就会调用这个函数callback_。后面首先会锁住,把这个局部变量的loop地址给loop_(共享的),然后用条件变量通知。之后调用

loop.loop()。

EventLoop* EventLoopThread::startLoop()

会调用thread_.start(),子线程就会运行threadfunc。创建loop并开始loop。

创建一个空的局部变量 Eventloop* loop,并且开始等待loop_不为空。因为当其不为空的时候,就会被通知。然后把loop_的值给局部变量loop,并且返回。

主线程和子线程的共同数据成员是loop_。子线程会把自己的loop给loop_,主线程需要返回这个Loop_。

EventLoopThreadPool

前置声明有EventLoop和EnvetLoopThread

  EventLoop* baseLoop_; //??
  string name_; //
  bool started_;
  int numThreads_; //线程池里面有多少个线程
  int next_; //下一个被 选用的线程池的索引     
  std::vector<std::unique_ptr<EventLoopThread>> threads_;   //指向每个EventLoopThread的指针向量
  std::vector<EventLoop*> loops_;//包含各个子线程loop的指针
EventLoop* EventLoopThreadPool::getNextLoop()

首先确认调用者是baseloop的创建者,把baseloop指针给loop。

然后把loops_的vector中取出下一个eventloop,使用next_作为索引。

把这个loop指针返回。

void EventLoopThreadPool::start(const ThreadInitCallback& cb)

根据numThreads的设定值,初始化出numThreads条线程。

EventLoopThread* t = new EventLoopThread(cb, buf);

是在堆上new出EventloopThread对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值