定时器函数选择
1.定时函数,用于让程序等待一段时间或安排计划任务:
sleep
alarm
usleep
nanosleep
clock_nanosleep
getitimer / setitimer
timer_create / timer_settime / timer_gettime / timer_delete
timerfd_create / timerfd_gettime / timerfd_settime
2.timerfd_* 入选的原因:
(1)sleep / alarm / usleep在实现时有可能用了信号SIGALRM,在多线程程序中处理信号是个相当麻烦的事情,应当尽量避免
(2)nanosleep 和 clock_nanosleep是线程安全的,但是在非阻塞网络编程中,绝对不能用让线程挂起的方式来等待一段时间,程序会失去响应。正确的做法是注册一个时间回调函数。
(3)getitimer 和 timer_create也是用信号来 deliver 超时,在多线程程序中也会有麻烦。
(4)timer_create可以指定信号的接收方是进程还是线程,算是一个进步,不过在信号处理函数(signal handler)能做的事情实在很受限。
(5)timerfd_create把时间变成了一个文件描述符,该“文件”在定时器超时的那一刻变得可读(相当于触发了可读事件),这样就能很方便地融入到 select/poll 框架中,用统一的方式来处理 IO 事件和超时事件,这也正是 Reactor 模式的长处。
3.函数
#include <sys/ timerfd.h >
int timerfd_create ( int clockid , int flags);
int timerfd_settime ( int fd , int flags, const struct itimerspec * new_value , struct itimerspec * old_value );
int timerfd_gettime ( int fd , struct itimerspec * curr_value )
测试(muduo_net库源码分析(二)的测试程序)
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <boost/bind.hpp>
#include <stdio.h>
#include <sys/timerfd.h>
using namespace muduo;
using namespace muduo::net;
EventLoop* g_loop;
int timerfd;
void timeout(Timestamp receiveTime)
{
printf("Timeout!\n");
uint64_t howmany;
::read(timerfd, &howmany, sizeof howmany);//一定要读完,因为是LT触发,否则会一直处于触发的状态
//g_loop->quit();//结束EventLoop::loop中的while循环
//注意:EventLoop::quit可以跨线程调用!
}
int main(void)
{
EventLoop loop;//创建一个reactor
g_loop = &loop;
timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
Channel channel(&loop, timerfd);//一个Channel对象只属于一个EventLoop对象,一个EventLoop对象拥有多个Channel对象
channel.setReadCallback(boost::bind(timeout, _1));//设置可读事件到来时触发的回调函数
channel.enableReading();//关注它的可读事件(这里指定时器的超时事件)
struct itimerspec howlong;
bzero(&howlong, sizeof howlong);
howlong.it_value.tv_sec = 1;
::timerfd_settime(timerfd, 0, &howlong, NULL);
loop.loop();
::close(timerfd);
}
打开下面的代码后:
g_loop->quit();//结束EventLoop::loop中的while循环
EPollPoller详解(个人的深入理解)
EPollPoller.cc中有一个匿名命名空间,表示事件(Channel对象)的状态:
这里补充一个重点,那就是Channel类中的数据成员index_。
对于PollPoller类,index_表示 Channel* 在::poll的事件数组中的下标;对于EPollPoller类,index_表示事件的状态(即KNew、KAdded、KDeleted)。
为什么会这样呢,其实很好理解,对于::poll函数,std::vector<struct pollfd>pollfds_;
::poll函数的第一个参数是struct pollfd数组的首地址,是传入传出参数,::poll要监控的所有事件就是通过pollfds_设置后再传入函数中。::poll返回pollfds_后,还要对其进行遍历,找出其中触发的numEvents个事件。
而对于::epoll_wait函数,事件数组为std::vector<struct epoll_event>events_;
::epoll_wait函数的第二个参数是struct epoll_event数组的首地址,是传出参数,::epoll_wait要监控的所有事件不是通过events_设置的,与events_无关,而是通过::epoll_ctl对epoll的红黑树进行设置。::epoll_wait返回events_后,events_中的前numEvents个事件全部是触发的事件(所以这也就是为什么在EPollPoller类的构造函数中要给events_预先分配容量的原因),不需要像::poll一样进行寻找。
EPollPoller源码
EPollPoller.h
#ifndef MUDUO_NET_POLLER_EPOLLPOLLER_H
#define MUDUO_NET_POLLER_EPOLLPOLLER_H
#include <muduo/net/Poller.h>
#include <map>
#include <vector>
struct epoll_event;
namespace muduo
{
namespace net
{
///
/// IO Multiplexing with epoll(4).
///
class EPollPoller : public Poller
{
public:
EPollPoller(EventLoop* loop);
virtual ~EPollPoller();
virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);
virtual void updateChannel(Channel* channel);
virtual void removeChannel(Channel* channel);
private:
static const int kInitEventListSize = 16;
void fillActiveChannels(int numEvents,
ChannelList* activeChannels) const;
void update(int operation, Channel* channel);
typedef std::vector<struct epoll_event> EventList;
typedef std::map<int, Channel*> ChannelMap;
int epollfd_;//epoll_create创建的句柄
EventList events_;//::epoll_wait函数的第二个参数是struct epoll_event数组的首地址,第二个参数是传出参数!
ChannelMap channels_;
};
}
}
#endif // MUDUO_NET_POLLER_EPOLLPOLLER_H
EPollPoller.cc
#include <muduo/net/poller/EPollPoller.h>
#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>
#include <boost/static_assert.hpp>
#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <sys/epoll.h>
using namespace muduo;
using namespace muduo::net;
// On Linux, the constants of poll(2) and epoll(4)
// are expected to be the same.
BOOST_STATIC_ASSERT(EPOLLIN == POLLIN);
BOOST_STATIC_ASSERT(EPOLLPRI == POLLPRI);
BOOST_STATIC_ASSERT(EPOLLOUT == POLLOUT);
BOOST_STATIC_ASSERT(EPOLLRDHUP == POLLRDHUP);
BOOST_STATIC_ASSERT(EPOLLERR == POLLERR);
BOOST_STATIC_ASSERT(EPOLLHUP == POLLHUP);
namespace
{
const int kNew = -1;//当前事件是新事件,既不在红黑树上,也不在map中
const int kAdded = 1;//当前事件在红黑树上,也在map中
const int kDeleted = 2;//当前事件不在红黑树上,但是当前事件仍在map中
}
EPollPoller::EPollPoller(EventLoop* loop)
: Poller(loop),
epollfd_(::epoll_create1(EPOLL_CLOEXEC)), //创建一个epoll句柄
events_(kInitEventListSize)//创建一个vector,预先分配容量大小为16
//EPollPoller构造函数中对vector预先分配容量的原因如下:
//::epoll_wait的第二个参数是传出参数,::epoll_wait要监控的事件不是通过该参数设置的,而是通过::epoll_ctl
//::poll的第一个参数是传入传出参数,要监控的事件通过该参数设置
{
if (epollfd_ < 0)
{
LOG_SYSFATAL << "EPollPoller::EPollPoller";
}
}
EPollPoller::~EPollPoller()
{
::close(epollfd_);
}
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
int numEvents = ::epoll_wait(epollfd_,
&*events_.begin(),
static_cast<int>(events_.size()),
timeoutMs);
//int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
/*
struct epoll_event {
uint32_t events; //Epoll events
epoll_data_t data; // User data variable
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
*/
Timestamp now(Timestamp::now());
if (numEvents > 0)
{
LOG_TRACE << numEvents << " events happended";
fillActiveChannels(numEvents, activeChannels);//将::epoll_wait返回的numEvents个事件放入活动通道中
if (implicit_cast<size_t>(numEvents) == events_.size())
{
events_.resize(events_.size()*2);//vector扩容
}
}
else if (numEvents == 0)
{
LOG_TRACE << " nothing happended";
}
else
{
LOG_SYSERR << "EPollPoller::poll()";
}
return now;
}
void EPollPoller::fillActiveChannels(int numEvents,
ChannelList* activeChannels) const
{
assert(implicit_cast<size_t>(numEvents) <= events_.size());
for (int i = 0; i < numEvents; ++i)
{
Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
#ifndef NDEBUG
int fd = channel->fd();
ChannelMap::const_iterator it = channels_.find(fd);
assert(it != channels_.end());
assert(it->second == channel);
#endif
channel->set_revents(events_[i].events);
activeChannels->push_back(channel);
}
}
void EPollPoller::updateChannel(Channel* channel)
{
Poller::assertInLoopThread();
LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
const int index = channel->index();
if (index == kNew || index == kDeleted)// index为-1或2说明要把当前事件挂到红黑树上去
{
// a new one, add with EPOLL_CTL_ADD
int fd = channel->fd();
if (index == kNew)
{
assert(channels_.find(fd) == channels_.end());
channels_[fd] = channel;//将新组成的key-value加入channels_,即加到map中//std::map<int, Channel*>channels_;
}
else // index == kDeleted
{
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
}
channel->set_index(kAdded);//修改当前通道状态为已添加
update(EPOLL_CTL_ADD, channel);//当前事件挂到epoll的红黑树上去
}
else
{
//说明需要进行更新或移除(这里的移除只是将事件从红黑树上移除,并没有从map中移除)
// update existing one with EPOLL_CTL_MOD/DEL
int fd = channel->fd();
(void)fd;
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
assert(index == kAdded);
if (channel->isNoneEvent())//移除(这里的移除只是将事件从红黑树上移除,并没有从map中移除)
{
update(EPOLL_CTL_DEL, channel);
channel->set_index(kDeleted);
}
else//更新
{
update(EPOLL_CTL_MOD, channel);
}
}
}
void EPollPoller::removeChannel(Channel* channel)//将事件移除(这里的移除是既将事件从红黑树上移除,也将事件从map中移除)
{
Poller::assertInLoopThread();
int fd = channel->fd();
LOG_TRACE << "fd = " << fd;
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
assert(channel->isNoneEvent());
int index = channel->index();
assert(index == kAdded || index == kDeleted);
size_t n = channels_.erase(fd);
(void)n;
assert(n == 1);
if (index == kAdded)//如果事件还在红黑树上,那么除了刚才将事件从map中移除外,还要将事件从红黑树上移除
{
update(EPOLL_CTL_DEL, channel);
}
channel->set_index(kNew);
}
//完成对epoll的红黑树的操作
//3种operation:
//EPOLL_CTL_ADD (注册新的fd到epfd)
//EPOLL_CTL_MOD (修改已经注册的fd的监听事件)
//EPOLL_CTL_DEL (从epfd删除一个fd)
void EPollPoller::update(int operation, Channel* channel)
{
//挂到红黑树的每一个结点的类型都是struct epoll_event类型
struct epoll_event event;
bzero(&event, sizeof event);
event.events = channel->events();
event.data.ptr = channel;//重点
int fd = channel->fd();
if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
{
if (operation == EPOLL_CTL_DEL)
{
LOG_SYSERR << "epoll_ctl op=" << operation << " fd=" << fd;
}
else
{
LOG_SYSFATAL << "epoll_ctl op=" << operation << " fd=" << fd;
}
}
}