[C++]sylar高性能服务器框架学习记录:HOOK模块

记录一下最近这学期学习的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

image-20260310114522940

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

image-20260310114656296

总结

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

评论

  1. sankkooos
    Android Chrome 142.0.7444.173
    2 周前
    2026-3-11 18:28:44

    world that girl chip

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇