记录一下最近这学期学习的sylar服务器框架项目,输出整理一下项目的结构,用到的知识和自己的体会
项目仓库地址
https://github.com/sylar-yin/sylar/
整理博客过程中参考的大佬资料链接:
============================================
基础介绍
利用已经实现的异步IO模块,对C函数库中的系统底层socket以及sleep系列等API进行hook,使其展现异步性能
在替换底层实现的同时,hook模块完全模拟API原本的行为,用户在使用API的时候是完全感受不到被hook的事实的,并且该模块在设计上为了保证API一致性以及尊重用户行为,对fcntl,setsockopt等函数也进行了hook
sylar使用的是动态链接中的hook实现,即通过动态库的全局符号介入功能,用自定义的接口来替换掉同名的系统调用接口
在实现hook之前,先使用dlsym()函数将libc库中的原始接口保存
头文件部分
hook开关
提供了两个hook开关,通过设置一个全局bool变量的值来开关hook
namespace sylar{
bool is_hook_enable();
void set_hook_enable(bool flag);
}
// 线程局部变量,控制每个线程是否开启hook
static thread_local bool t_hook_enable = false;
bool is_hook_enable() {
return t_hook_enable;
}
void set_hook_enable(bool flag) {
t_hook_enable = flag;
}
获取hook函数的函数指针类型
由于C++有函数重载机制,底层存储的函数名称和C的机制不同,所以包裹在C的名称空间内
// 按照C的的方式编译链接(避免C++函数名重载)
extern "C" {
// sleep接口
typedef unsigned int (*sleep_fun) (unsigned int seconds);
extern sleep_fun sleep_f;
typedef int (*usleep_fun) (useconds_t usec);
extern usleep_fun usleep_f;
typedef int (*nanosleep_fun) (const struct timespec *req, struct timespec *rem);
extern nanosleep_fun nanosleep_f;
// socket接口
typedef int (*socket_fun) (int domain, int type, int protocol);
extern socket_fun socket_f;
typedef int (*connect_fun)(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
extern connect_fun connect_f;
typedef int (*accept_fun)(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
extern accept_fun accept_f;
// read接口
typedef ssize_t (*read_fun) (int fd, void *buf, size_t count);
extern read_fun read_f;
typedef ssize_t (*readv_fun) (int fd, const struct iovec *iov, int iovcnt);
extern readv_fun readv_f;
typedef ssize_t (*recv_fun) (int sockfd, void *buf, size_t len, int flags);
extern recv_fun recv_f;
typedef ssize_t (*recvfrom_fun) (int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
extern recvfrom_fun recvfrom_f;
typedef ssize_t (*recvmsg_fun) (int sockfd, struct msghdr *msg, int flags);
extern recvmsg_fun recvmsg_f;
// write接口
typedef ssize_t (*write_fun) (int fd, const void *buf, size_t count);
extern write_fun write_f;
typedef ssize_t (*writev_fun) (int fd, const struct iovec *iov, int iovcnt);
extern writev_fun writev_f;
typedef ssize_t (*send_fun) (int sockfd, const void *buf, size_t len, int flags);
extern send_fun send_f;
typedef ssize_t (*sendto_fun) (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
extern sendto_fun sendto_f;
typedef ssize_t (*sendmsg_fun) (int sockfd, const struct msghdr *msg, int flags);
extern sendmsg_fun sendmsg_f;
// fd控制接口
typedef int (*close_fun) (int fd);
extern close_fun close_f;
typedef int (*fcntl_fun) (int fd, int cmd, ... /* arg */ );
extern fcntl_fun fcntl_f;
typedef int (*ioctl_fun) (int fd, unsigned long request, ...);
extern ioctl_fun ioctl_f;
typedef int (*getsockopt_fun) (int sockfd, int level, int optname, void *optval, socklen_t *optlen);
extern getsockopt_fun getsockopt_f;
typedef int (*setsockopt_fun) (int sockfd, int level, int optname, const void *optval, socklen_t optlen);
extern setsockopt_fun setsockopt_f;
// 带时延的connect函数
extern int connect_with_timeout(int fd, const struct sockaddr* addr, socklen_t addrlen, uint64_t timeout_ms);
}
实现文件部分
hook模块初始化
使用XX宏对以上头文件中的原始接口地址进行了保存,命名为xxx_f以便后续调用原始接口,同时还添加了tcp连接超时时间初始化配置项,默认值为5000毫秒
最终将所有初始化逻辑写在一个结构体的构造函数中,全局声明一个该结构体静态变量,让初始化逻辑在main函数之前执行
// tcp连接超时时间配置项
static sylar::ConfigVar
<int>::ptr g_tcp_connect_timeout =
sylar::Config::Lookup("tcp.connect.timeout", 5000, "tcp connect timeout");
// 使用宏批量处理被hook函数的原接口地址
#define HOOK_FUNC(XX) \
XX(sleep) \
XX(usleep) \
XX(nanosleep) \
XX(socket) \
XX(connect) \
XX(accept) \
XX(read) \
XX(readv) \
XX(recv) \
XX(recvfrom) \
XX(recvmsg) \
XX(write) \
XX(writev) \
XX(send) \
XX(sendto) \
XX(sendmsg) \
XX(close) \
XX(fcntl) \
XX(ioctl) \
XX(getsockopt) \
XX(setsockopt)
// hook初始化函数
void hook_init() {
static bool is_init = false;
if(is_init) {
return;
}
// 函数类型命名为xxx_fun 原函数接口地址命名为xxx_f
#define XX(name) name ## _f = (name ## _fun)dlsym(RTLD_NEXT, #name);
HOOK_FUNC(XX);
#undef XX
}
// 全局静态变量在main之前初始化
static uint64_t s_connect_timeout = -1;
struct _HookIniter {
_HookIniter() {
hook_init();
s_connect_timeout = g_tcp_connect_timeout->getValue();
g_tcp_connect_timeout->addListener([](const int& old_value, const int& new_value){
SYLAR_LOG_INFO(g_logger) << "tcp connect timeout changed from"
<< old_value << " to " << new_value;
s_connect_timeout = new_value;
});
}
};
static _HookIniter s_hook_initer;
基本IOhook模板
使用了可变参数模板,适用于不同种类IO操作,大体逻辑如下:
先判断hook是否开启,如果未开启则直接返回原接口
再判断fd状态判断是否继续hook
判断都通过后,先尝试执行一次原接口,检查errno的值,如果为中断,就不断尝试执行直到errno的值为阻塞,这时候向IOManager注册事件,并设置超时时间然后让出调度,被唤醒后检查是因为资源可用还是定时器超时导致的唤醒,如果是定时器超时就返回-1,如果是正常就绪返回就取消定时器,retry执行原接口直到执行成功返回
template<typename OriginFun, typename ... Args>
static ssize_t do_io(int fd, OriginFun fun, const char* hook_fun_name,
uint32_t event, int timeout_so, Args&&... args) {
if(!sylar::t_hook_enable) {
return fun(fd, std::forward
<Args>(args)...);
}
// 取出FdCtx
sylar::FdCtx::ptr ctx = sylar::FdMgr::GetInstance()->get(fd);
if(!ctx) { // 若没有注册
return fun(fd, std::forward
<Args>(args)...);
}
if(ctx->isClose()) { // 若已关闭
errno = EBADF;
return -1;
}
if(!ctx->isSocket() || ctx->getUserNonBlock()) { // 若不是socket或用户手动指定非阻塞
return fun(fd, std::forward
<Args>(args)...);
}
// 获取超时配置(SO_RCVTIMEO / SO_SNDTIMEO)
uint64_t to = ctx->getTimeout(timeout_so);
std::shared_ptr
<timer_info> tinfo(new timer_info);
retry:
// 先尝试执行fun,获取返回值和errno状态
ssize_t n = fun(fd, std::forward
<Args>(args)...);
while(n == -1 && errno == EINTR) { // 若中断,重试
n = fun(fd, std::forward
<Args>(args)...);
}
if(n == -1 && errno == EAGAIN) { // 若为阻塞状态
sylar::IOManager* iom = sylar::IOManager::GetThis();
sylar::Timer::ptr timer;
std::weak_ptr
<timer_info> winfo(tinfo);
// 若有设置超时时间,添加条件定时器
if(to != (uint64_t)-1) {
timer = iom->addConditionTimer(to, [winfo, fd, iom, event](){
auto t = winfo.lock();
if(!t || t->cancelled) {
return;
}
t->cancelled = ETIMEDOUT;
iom->cancelEvent(fd, (sylar::IOManager::Event)(event));
}, winfo);
}
// 注册事件
int rt = iom->addEvent(fd, (sylar::IOManager::Event)(event));
if(rt) { // 注册失败
SYLAR_LOG_ERROR(g_logger) << hook_fun_name << " addEvent("
<< fd << ", " << event << ")";
if(timer) {
timer->cancel();
}
return -1;
} else { // 注册成功
sylar::Fiber::YieldToHold(); // 让出调度
// 被重新调度
if(timer) { // 若timer还在说明是正常就绪返回
timer->cancel();
}
if(tinfo->cancelled) { // 超时返回
errno = tinfo->cancelled;
return -1;
}
goto retry;
}
}
return n;
}
使用该IOhook模板函数,hook了以下接口:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
int fd = do_io(sockfd, accept_f, "accept", sylar::IOManager::READ, SO_RCVTIMEO, addr, addrlen);
if(fd >= 0) {
sylar::FdMgr::GetInstance()->get(fd, true);
}
return fd;
}
ssize_t read(int fd, void *buf, size_t count) {
return do_io(fd, read_f, "read", sylar::IOManager::READ, SO_RCVTIMEO, buf, count);
}
ssize_t readv(int fd, const struct iovec *iov, int iovcnt) {
return do_io(fd, readv_f, "readv", sylar::IOManager::READ, SO_RCVTIMEO, iov, iovcnt);
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
return do_io(sockfd, recv_f, "recv", sylar::IOManager::READ, SO_RCVTIMEO, buf, len, flags);
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags
, struct sockaddr *src_addr, socklen_t *addrlen) {
return do_io(sockfd, recvfrom_f, "recvfrom", sylar::IOManager::READ, SO_RCVTIMEO, buf, len
, flags, src_addr, addrlen);
}
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) {
return do_io(sockfd, recvmsg_f, "recvmsg", sylar::IOManager::READ, SO_RCVTIMEO, msg, flags);
}
ssize_t write(int fd, const void *buf, size_t count) {
return do_io(fd, write_f, "write", sylar::IOManager::WRITE, SO_SNDTIMEO, buf, count);
}
ssize_t writev(int fd, const struct iovec *iov, int iovcnt) {
return do_io(fd, writev_f, "writev", sylar::IOManager::WRITE, SO_SNDTIMEO, iov, iovcnt);
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
return do_io(sockfd, send_f, "send", sylar::IOManager::WRITE, SO_SNDTIMEO, buf, len, flags);
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags
, const struct sockaddr *dest_addr, socklen_t addrlen) {
return do_io(sockfd, sendto_f, "sendto", sylar::IOManager::WRITE, SO_SNDTIMEO, buf, len
, flags, dest_addr, addrlen);
}
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
return do_io(sockfd, sendmsg_f, "sendmsg", sylar::IOManager::WRITE, SO_SNDTIMEO, msg, flags);
}
对sleep系列接口的hook
逻辑是注册定时器时间然后让出调度,定时器的回调是超时后调度自己
// sleep类接口hook(添加定时器,然后yield,定时器超时触发回调)
unsigned int sleep(unsigned int seconds) {
if(!sylar::t_hook_enable) {
return sleep_f(seconds);
}
sylar::Fiber::ptr fiber = sylar::Fiber::GetThis();
sylar::IOManager* iom = sylar::IOManager::GetThis();
iom->addTimer(seconds * 1000, std::bind((void(sylar::Scheduler::*)
(sylar::Fiber::ptr, int thread))&sylar::IOManager::schedule // 强制类型转换明确指定使用Scheduler的schedule()
,iom, fiber, -1));
sylar::Fiber::YieldToHold();
return 0;
}
int usleep(useconds_t usec) {
if(!sylar::t_hook_enable) {
return usleep_f(usec);
}
sylar::Fiber::ptr fiber = sylar::Fiber::GetThis();
sylar::IOManager* iom = sylar::IOManager::GetThis();
iom->addTimer(usec / 1000, std::bind((void(sylar::Scheduler::*)
(sylar::Fiber::ptr, int thread))&sylar::IOManager::schedule
,iom, fiber, -1));
sylar::Fiber::YieldToHold();
return 0;
}
int nanosleep(const struct timespec *req, struct timespec *rem) {
if(!sylar::t_hook_enable) {
return nanosleep_f(req, rem);
}
int timeout_ms = req->tv_sec * 1000 + req->tv_nsec / 1000 /1000;
sylar::Fiber::ptr fiber = sylar::Fiber::GetThis();
sylar::IOManager* iom = sylar::IOManager::GetThis();
iom->addTimer(timeout_ms, std::bind((void(sylar::Scheduler::*)
(sylar::Fiber::ptr, int thread))&sylar::IOManager::schedule
,iom, fiber, -1));
sylar::Fiber::YieldToHold();
return 0;
}
socket类接口hook
对connect,accept等进行hook
// socket类接口hook
int socket(int domain, int type, int protocol) {
if(!sylar::t_hook_enable) {
return socket_f(domain, type, protocol);
}
// 创建socket,放到FdManager中管理
int fd = socket_f(domain, type, protocol);
if(fd == -1) {
return fd;
}
sylar::FdMgr::GetInstance()->get(fd, true);
return fd;
}
int connect_with_timeout(int fd, const struct sockaddr* addr, socklen_t addrlen, uint64_t timeout_ms) {
if(!sylar::t_hook_enable) {
return connect_f(fd, addr, addrlen);
}
// 从FdManager中获取FdCtx结构体
sylar::FdCtx::ptr ctx = sylar::FdMgr::GetInstance()->get(fd);
if(!ctx || ctx->isClose()) { // 若已关闭
errno = EBADF;
return -1;
}
if(!ctx->isSocket()) { // 若不是socket
return connect_f(fd, addr, addrlen);
}
if(ctx->getUserNonBlock()) { // 若被显式指定为非阻塞
return connect_f(fd, addr, addrlen);
}
// 调用系统connect接口尝试连接,由于非阻塞会直接返回-1
int n = connect_f(fd, addr, addrlen);
if(n == 0) { // 若直接成功,返回即可
return 0;
} else if (n != -1 || errno != EINPROGRESS) { // 出现意外错误
return n;
}
sylar::IOManager* iom = sylar::IOManager::GetThis();
sylar::Timer::ptr timer;
std::shared_ptr
<timer_info> tinfo(new timer_info);
std::weak_ptr
<timer_info> winfo(tinfo);
// 若设置了超时时间
if(timeout_ms != (uint64_t)-1) {
// 添加条件定时器
timer = iom->addConditionTimer(timeout_ms, [winfo, fd, iom](){
auto t = winfo.lock();
if(!t || t->cancelled) {
return;
}
t->cancelled = ETIMEDOUT;
iom->cancelEvent(fd, sylar::IOManager::WRITE);
}, winfo);
}
// 添加一个写事件
int rt = iom->addEvent(fd, sylar::IOManager::WRITE);
if(rt == 0) { // 添加成功
sylar::Fiber::YieldToHold(); // 让出调度
// 被重新调度
if(timer) { // timer还在,则是epoll_wait()唤醒,fd事件就绪
timer->cancel();
}
if(tinfo->cancelled) { // 若是超时唤醒
errno = tinfo->cancelled;
return -1;
}
} else { // 添加失败,错误处理
if(timer) {
timer->cancel();
}
SYLAR_LOG_ERROR(g_logger) << "connect addEvent(" << fd << ", WRITE) error";
}
int error = 0;
socklen_t len = sizeof(int);
// 获取socket状态
if(-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len)) {
return -1;
}
if(!error) {
return 0;
} else {
errno = error;
return -1;
}
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
return connect_with_timeout(sockfd, addr, addrlen, sylar::s_connect_timeout);
}
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
int fd = do_io(sockfd, accept_f, "accept", sylar::IOManager::READ, SO_RCVTIMEO, addr, addrlen);
if(fd >= 0) {
sylar::FdMgr::GetInstance()->get(fd, true);
}
return fd;
}
int close(int fd) {
if(!sylar::t_hook_enable) {
return close_f(fd);
}
// 取消fd上所有事件,删除fd
sylar::FdCtx::ptr ctx = sylar::FdMgr::GetInstance()->get(fd);
if(ctx) {
auto iom = sylar::IOManager::GetThis();
if(iom) {
iom->cancelAll(fd);
}
sylar::FdMgr::GetInstance()->del(fd);
}
return close_f(fd);
}
fd状态设置接口hook
保证接口一致性,尊重用户行为
int fcntl(int fd, int cmd, ... /* arg */ ) {
va_list va;
va_start(va, cmd);
switch(cmd) {
case F_SETFL:
{
int arg = va_arg(va, int);
va_end(va);
sylar::FdCtx::ptr ctx = sylar::FdMgr::GetInstance()->get(fd);
if(!ctx || ctx->isClose() || !ctx->isSocket()) {
return fcntl_f(fd, cmd, arg);
}
ctx->setUserNonBlock(arg & O_NONBLOCK); // 记录用户是否主动设置非阻塞
if(ctx->getSysNonBlock()) { // 若该fd是hook系统自动设置非阻塞
arg |= O_NONBLOCK; // 强制设置非阻塞
} else {
arg &= ~O_NONBLOCK; // 设置阻塞
}
return fcntl_f(fd, cmd, arg);
}
break;
case F_GETFL:
{
va_end(va);
int arg = fcntl_f(fd, cmd);
sylar::FdCtx::ptr ctx = sylar::FdMgr::GetInstance()->get(fd);
if(!ctx || ctx->isClose() || !ctx->isSocket()) {
return arg;
}
if(ctx->getUserNonBlock()) { // 若用户主动设置非阻塞
return arg | O_NONBLOCK; // 返回非阻塞
} else {
return arg & ~O_NONBLOCK; // 返回阻塞
}
}
break;
case F_DUPFD:
case F_DUPFD_CLOEXEC:
case F_SETFD:
case F_SETOWN:
case F_SETSIG:
case F_SETLEASE:
case F_NOTIFY:
#ifdef F_SETPIPE_SZ
case F_SETPIPE_SZ:
#endif
{
int arg = va_arg(va, int);
va_end(va);
return fcntl_f(fd, cmd, arg);
}
break;
case F_GETFD:
case F_GETOWN:
case F_GETSIG:
case F_GETLEASE:
#ifdef F_GETPIPE_SZ
case F_GETPIPE_SZ:
#endif
{
va_end(va);
return fcntl_f(fd, cmd);
}
break;
case F_SETLK:
case F_SETLKW:
case F_GETLK:
{
struct flock* arg = va_arg(va, struct flock*);
va_end(va);
return fcntl_f(fd, cmd, arg);
}
break;
case F_GETOWN_EX:
case F_SETOWN_EX:
{
struct f_owner_exlock* arg = va_arg(va, struct f_owner_exlock*);
va_end(va);
return fcntl_f(fd, cmd, arg);
}
break;
default:
va_end(va);
return fcntl_f(fd, cmd);
break;
}
}
int ioctl(int fd, unsigned long request, ...) {
va_list va;
va_start(va, request);
void* arg = va_arg(va, void*);
va_end(va);
// 若request是FIONBIO(设置为非阻塞模式)
if(FIONBIO == request) {
bool user_nonblock = !!*(int*)arg; // 转为bool
// 维护对应fdCtx状态
sylar::FdCtx::ptr ctx = sylar::FdMgr::GetInstance()->get(fd);
if(!ctx || ctx->isClose() || !ctx->isSocket()) {
return ioctl_f(fd, request, arg);
}
ctx->setUserNonBlock(user_nonblock);
}
return ioctl_f(fd, request, arg);
}
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
return getsockopt_f(sockfd, level, optname, optval, optlen);
}
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
if(!sylar::t_hook_enable) {
return setsockopt_f(sockfd, level, optname, optval, optlen);
}
if(level == SOL_SOCKET) {
if(optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
// 使用定时器处理超时
sylar::FdCtx::ptr ctx = sylar::FdMgr::GetInstance()->get(sockfd);
if(ctx) {
const timeval* tv = (const timeval*)optval;
ctx->setTimeout(optname, tv->tv_sec * 1000 + tv->tv_usec / 1000);
}
}
}
return setsockopt_f(sockfd, level, optname, optval, optlen);
}
FdCtx和FdMgr类
在以上的hook实现中使用了自定义的FdCtx和FdMgr类,分别用于记录fd状态和管理全局fd
FdCtx
记录fd上下文
// 文件句柄上下文类
class FdCtx : public std::enable_shared_from_this
<FdCtx> {
public:
typedef std::shared_ptr
<FdCtx> ptr;
FdCtx(int fd); // 构造
~FdCtx(); // 析构
bool init(); // 初始化
bool close(); // 关闭
bool isInit() const { return m_isInit; }
bool isSocket() const { return m_isSocket; }
bool isClose() const { return m_isClosed; }
void setUserNonBlock(bool v) { m_userNonBlock = v; }
bool getUserNonBlock() const { return m_userNonBlock; }
void setSysNonBlock(bool v) { m_sysNonBlock = v; }
bool getSysNonBlock() const { return m_sysNonBlock; }
void setTimeout(int type, uint64_t v); // 设置超时时间
uint64_t getTimeout(int type); // 获取超时时间
private:
// 位域,占用1bit
bool m_isInit : 1; // 是否初始化
bool m_isSocket : 1; // 是否socket
bool m_sysNonBlock : 1; // 是否hook非阻塞
bool m_userNonBlock : 1; // 是否用户主动设置非阻塞
bool m_isClosed : 1; // 是否关闭
int m_fd; // 文件句柄
uint64_t m_recvTimeout; // 读超时时间
uint64_t m_sendTimeout; // 写超时时间
};
bool FdCtx::init() {
if(m_isInit) {
return true;
}
m_recvTimeout = -1;
m_sendTimeout = -1;
// 获取fd上下文状态,存放在stat结构体
struct stat fd_stat;
if(-1 == fstat(m_fd, &fd_stat)) { // 获取失败
m_isInit = false;
m_isSocket = false;
} else { // 获取成功
m_isInit = true;
m_isSocket = S_ISSOCK(fd_stat.st_mode); // 是否是socket
}
if(m_isSocket) {
int flag = fcntl_f(m_fd, F_GETFL, 0); // 获取fd状态
if(!(flag & O_NONBLOCK)) { // 如果不是非阻塞,设置成非阻塞
fcntl_f(m_fd, F_SETFL, flag | O_NONBLOCK);
}
m_sysNonBlock = true; // 标记为hook系统自动设置非阻塞
} else {
m_sysNonBlock = false;
}
m_userNonBlock = false;
m_isClosed = false;
return m_isInit;
}
void FdCtx::setTimeout(int type, uint64_t v) {
if(type ==SO_RCVTIMEO) {
m_recvTimeout = v;
} else {
m_sendTimeout = v;
}
}
uint64_t FdCtx::getTimeout(int type) {
if(type == SO_RCVTIMEO) {
return m_recvTimeout;
} else {
return m_sendTimeout;
}
}
FdMgr(单例模式)
// 文件句柄管理类
class FdManager {
public:
typedef RWMutex RWMUtexType;
FdManager();
FdCtx::ptr get(int fd, bool auto_create = false); // 获取fd,可指定是否自动创建不存在的fd
void del(int fd); // 删除fd
private:
RWMUtexType m_mutex;
std::vector<FdCtx::ptr> m_datas; // 管理的fd
};
// 文件句柄管理类单例
typedef Singleton
<FdManager> FdMgr;
FdManager::FdManager() {
m_datas.resize(64);
}
FdCtx::ptr FdManager::get(int fd, bool auto_create) {
if(fd == -1) {
return nullptr;
}
RWMUtexType::ReadLock lock(m_mutex);
if((int)m_datas.size() <= fd) {
if(auto_create == false) {
return nullptr;
}
} else {
if(m_datas[fd] || !auto_create) {
return m_datas[fd];
}
}
lock.unlock();
RWMUtexType::WriteLock lock2(m_mutex);
FdCtx::ptr ctx(new FdCtx(fd));
if(fd >= (int)m_datas.size()) {
m_datas.resize(fd * 1.5);
}
m_datas[fd] = ctx;
return ctx;
}
void FdManager::del(int fd) {
RWMUtexType::WriteLock lock(m_mutex);
if((int)m_datas.size() <= fd) {
return;
}
m_datas[fd].reset();
}
测试
分别测试了sleep以及socket系列接口hook
#include "sylar/hook.h"
#include "sylar/log.h"
#include "sylar/iomanager.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();
void test_sleep() {
sylar::IOManager iom(2);
iom.schedule([](){
sleep(3);
SYLAR_LOG_INFO(g_logger) << "sleep 3";
});
iom.schedule([](){
sleep(3);
SYLAR_LOG_INFO(g_logger) << "sleep 3";
});
SYLAR_LOG_INFO(g_logger) << "test sleep";
}
void test_sock() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
inet_pton(AF_INET, "111.63.65.103", &addr.sin_addr.s_addr);
SYLAR_LOG_INFO(g_logger) << "begin connect";
int rt = connect(sock, (const sockaddr*)&addr, sizeof(addr));
SYLAR_LOG_INFO(g_logger) << "connect rt=" << rt << " errno=" << errno;
if(rt) {
return;
}
const char data[] = "GET / HTTP/1.0\r\n\r\n";
rt = send(sock, data, sizeof(data), 0);
SYLAR_LOG_INFO(g_logger) << "send rt=" << rt << " errno=" << errno;
if(rt <= 0) {
return;
}
std::string buff;
buff.resize(4096);
rt = recv(sock, &buff[0], buff.size(), 0);
SYLAR_LOG_INFO(g_logger) << "recv rt=" << rt << " errno=" << errno;
if(rt <= 0) {
return;
}
buff.resize(rt);
SYLAR_LOG_INFO(g_logger) << buff;
}
int main(int argc, char** argv) {
test_sleep();
// sylar::IOManager iom;
// iom.schedule(test_sock);
return 0;
}
sleep系列接口测试结果如下,可以看到成功异步执行了两个3000毫秒的sleep

socket系列接口测试结果如下,可以看到虽然url它301了,但是完成了连接,发送以及接收

总结
对网络IO以及其他一些API进行了HOOK,使程序员能在无感知的情况下使用同步的方式写异步代码




world that girl chip