马上就是4月1日了, 愚人节。 但2018年已经过了1/4, 好快, 这不是愚人的。
在tars代码中, 目前还没有看到select/poll的痕迹, 光芒被epoll遮挡了。
我们知道, epoll也就三个常用的api, epoll_create, epoll_ctl, epoll_wait, 且默认是LT模式。 可以思考一下, 为什么比起select/poll, epoll会多出epoll_create和epoll_ctl这两个api呢? 其实, 这两个api在select/poll中, 都有不同的表现形式, 从而可以看到, 真正监测fd的函数是select/poll/epoll_wait.
现在tc_epoller要来进行封装操作,且封装成默认ET模式, 如构造函数:
TC_Epoller(bool bEt = true);
随后, 每一次ctl, 都判断了是ET还是LT:
void TC_Epoller::ctrl(int fd, long long data, __uint32_t events, int op)
{
struct epoll_event ev;
ev.data.u64 = data;
if(_et)
{
ev.events = events | EPOLLET;
}
else
{
ev.events = events;
}
epoll_ctl(_iEpollfd, op, fd, &ev);
}
创建epoll管理句柄, 也是我们熟悉的方式:
void TC_Epoller::create(int max_connections)
{
_max_connections = max_connections;
_iEpollfd = epoll_create(_max_connections + 1);
if(_pevs != NULL)
{
delete[] _pevs;
}
_pevs = new epoll_event[_max_connections + 1];
}
其实, 新linux对epoll_create的参数有优化, 如man所说:
epoll_create() creates an epoll(7) instance. Since Linux 2.6.8, the
size argument is ignored, but must be greater than zero; see NOTES
below.
如下也是封装, 方便操作而已:
void TC_Epoller::add(int fd, long long data, __uint32_t event)
{
ctrl(fd, data, event, EPOLL_CTL_ADD);
}
void TC_Epoller::mod(int fd, long long data, __uint32_t event)
{
ctrl(fd, data, event, EPOLL_CTL_MOD);
}
void TC_Epoller::del(int fd, long long data, __uint32_t event)
{
ctrl(fd, data, event, EPOLL_CTL_DEL);
}
再看:
int TC_Epoller::wait(int millsecond)
{
return epoll_wait(_iEpollfd, _pevs, _max_connections + 1, millsecond);
}
这里的+1操作不要和select第一个参数的+1操作混淆(之前说过), epoll_wait这里的_max_connections + 1是因为:
void TC_Epoller::create(int max_connections)
{
_max_connections = max_connections;
_iEpollfd = epoll_create(_max_connections + 1);
if(_pevs != NULL)
{
delete[] _pevs;
}
_pevs = new epoll_event[_max_connections + 1];
}
可以看到, tc_epoller无非就是对裸体epoll的封装, 穿上衣服, 比落体更美。 如上的epoll封装, 可以应用于后面要介绍的cllient socket和server socket.