毫秒数_swoole毫秒定时器(Timer)源码解析二

前情提要

在swoole的timer定时器中,添加定时器的步骤其实还有很多的初始化操作,这些操作是很重要的,因为限制于篇幅,于是把这一部分放到这里来说。

起死回生:swoole毫秒定时器(Timer)源码解析一​zhuanlan.zhihu.com
e0078931a98d09326812518ec9041319.png

swoole_timer_add中的初始化

TimerNode *swoole_timer_add(long ms, bool persistent, const TimerCallback &callback, void *private_data) {
    if (sw_unlikely(SwooleTG.timer == nullptr)) {
        SwooleTG.timer = new Timer();
        if (sw_unlikely(!SwooleTG.timer->init())) {
            delete SwooleTG.timer;
            SwooleTG.timer = nullptr;
            return nullptr;
        }
    }
    return SwooleTG.timer->add(ms, persistent, private_data, callback);
}

接着是Timer的构造函数。

// 先看看Timer的实例化
Timer::Timer()
        // 最小堆,默认1024个,使用最小堆的原因是堆顶的定时任务还没到时间执行
        // 那么其余的定时任务也肯定没到时间。
        : heap(1024, Heap::MIN_HEAP) {
    _current_id = -1; // 当前定时器id
    next_msec_ = -1; // 下一次执行时间
    _next_id = 1; // 下一个定时器id
    round = 0;
    now(&base_time);// 初始化启动时间,获取相对毫秒数有用,因为定时器都是下一次执行的
}

Timer的init方法

// SwooleTG.timer->init()
bool Timer::init() {
    // 这步骤我觉得是多余的,因为Timer的构造函数已经执行了这个功能了。
    // 我感觉构造函数那一块的now(&base_time)不需要
    if (now(&base_time) < 0) {
        return false;
    }
    if (SwooleTG.reactor) {
        // 基于事件驱动的
        return init_reactor(SwooleTG.reactor);
    } else {
        // 基于setitimer的定时器
        return init_system_timer();
    }
}

基于事件的定时器初始化

// 基于事件的初始化
bool Timer::init_reactor(Reactor *reactor) {
    reactor_ = reactor;
    // 当出现了时间间隔更加小的定时任务,需要用这个方法实时更新Timer的执行间隔
    set = [](Timer *timer, long exec_msec) -> int {
        timer->reactor_->timeout_msec = exec_msec;
        return SW_OK;
    };
    // 关闭定时器
    close = [](Timer *timer) { timer->set(timer, -1); };
    // reactor线程添加该定时器执行时的函数select,定时器超时就会调用这个函数
    reactor->set_end_callback(Reactor::PRIORITY_TIMER, [this](Reactor *) { select(); });
    // reactor线程退出的条件
    reactor->set_exit_condition(Reactor::EXIT_CONDITION_TIMER,
                                [this](Reactor *reactor, int &event_num) -> bool { return count() == 0; });
    // reactor线程销毁时候要执行的方法。
    reactor->add_destroy_callback([](void *) { swoole_timer_free(); });

    return true;
}

基于setitimer的初始化

// 基于setitimer的初始化
bool Timer::init_system_timer() {
    // 当出现了时间间隔更加小的定时任务,需要用这个方法实时更新Timer的执行间隔
    // 和setitimer的执行间隔
    set = SystemTimer_set;
    // 关闭定时器
    close = [](Timer *timer) { SystemTimer_set(timer, -1); };
    // setitimer触发的是SIGALRM,因此添加该信号的处理器
    swSignal_set(SIGALRM, [](int sig) { SwooleG.signal_alarm = true; });
    return true;
}

Timer::init_system_timer的SystemTimer_set

static int SystemTimer_set(Timer *timer, long next_msec) {
    struct itimerval timer_set;
    struct timeval now;
    if (gettimeofday(&now, nullptr) < 0) {
        swSysWarn("gettimeofday() failed");
        return SW_ERR;
    }

    if (next_msec > 0) {
        // 秒数
        int sec = next_msec / 1000;
        // 毫秒数
        int msec = next_msec % 1000;
        timer_set.it_interval.tv_sec = sec;
        timer_set.it_interval.tv_usec = msec * 1000;
        timer_set.it_value.tv_sec = sec;
        timer_set.it_value.tv_usec = timer_set.it_interval.tv_usec;
        // timer_set.it_value.tv_usec如果超过1s,1e6就是1000000微秒,也就是1s
        if (timer_set.it_value.tv_usec > 1e6) {
            timer_set.it_value.tv_usec = timer_set.it_value.tv_usec - 1e6;
            timer_set.it_value.tv_sec += 1; // 秒数加1
        }
    } else {
        timer_set = {};
    }
    // setitimer设置,ITIMER_REAL表示触发SIGALRM信号
    if (setitimer(ITIMER_REAL, &timer_set, nullptr) < 0) {
        swSysWarn("setitimer() failed");
        return SW_ERR;
    }
    return SW_OK;
}

Timer::init_system_timer的swSignal_set

swSignalHandler swSignal_set(int signo, swSignalHandler handler) {
#ifdef HAVE_SIGNALFD
    // 将信号抽象为一个文件描述符,当有信号发生时可以对其read,
    // 这样可以将信号的监听放到select、poll、epoll等监听队列中,事件监听
    if (SwooleG.use_signalfd) {
        return swSignalfd_set(signo, handler);
    } else
#endif
    {
#ifdef HAVE_KQUEUE // 苹果系统
        // SIGCHLD can not be monitored by kqueue, if blocked by SIG_IGN
        // see https://www.freebsd.org/cgi/man.cgi?kqueue
        // if there's no main reactor, signals cannot be monitored either
        if (signo != SIGCHLD && sw_reactor()) {
            return swKqueueSignal_set(signo, handler);
        } else
#endif
        {
            // 设置信号的一些信息
            signals[signo].handler = handler;
            signals[signo].activated = true;
            signals[signo].signo = signo;
            return swSignal_set(signo, swSignal_async_handler, 1, 0);
        }
    }
}

看看swSignal_set这个重载函数

swSignalHandler swSignal_set(int signo, swSignalHandler func, int restart, int mask) {
    // ignore
    if (func == nullptr) {
        func = SIG_IGN; // 忽略该信号
    }
    // clear
    else if ((long) func == -1) {
        func = SIG_DFL;  // 默认操作
    }
   
    struct sigaction act {
    }, oact{};
    act.sa_handler = func;
    if (mask) {
        // 初始化act.sa_mask指向的信号集,使其包括所有信号
        sigfillset(&act.sa_mask);
    } else {
        // 初始化act.sa_mask指向的信号集,清除所有信号
        sigemptyset(&act.sa_mask);
    }
    act.sa_flags = 0;
    // 注册该信号的关联的处理动作
    if (sigaction(signo, &act, &oact) < 0) {
        return nullptr;
    }
    return oact.sa_handler;
}

swSignalfd_set将signal转化为文件描述符

// 信号抽象为一个文件描述符
static swSignalHandler swSignalfd_set(int signo, swSignalHandler handler) {
    swSignalHandler origin_handler = nullptr;

    if (handler == nullptr && signals[signo].activated) {
        // 如果信号处理函数为nullptr,
        // 将signo信号从signalfd_mask删除
        sigdelset(&signalfd_mask, signo);
        // 重置signals[signo]对应的swSignal
        sw_memset_zero(&signals[signo], sizeof(swSignal));
    } else {
        // 否则,将该信号加入signalfd_mask信号集
        sigaddset(&signalfd_mask, signo);
        // 重新设置该信号的属性
        origin_handler = signals[signo].handler;
        signals[signo].handler = handler;
        signals[signo].signo = signo;
        signals[signo].activated = true;
    }
    // 如果信号描述符signal_fd存在
    if (signal_fd > 0) {
        // SIG_SETMASK表示该信号的新的屏蔽是signalfd_mask指向的值
        sigprocmask(SIG_SETMASK, &signalfd_mask, nullptr);
        // 因为signal_fd 不为-1,所以这里是更新操作
        signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC);
    } else if (sw_reactor()) {
        //否则,安装该信号描述符
        swSignalfd_setup(sw_reactor());
    }

    return origin_handler;
}

swSignalfd_setup安装信号处理器

// 设置信号文件描述符
int swSignalfd_setup(Reactor *reactor) {
    if (signal_fd != 0) {
        return SW_OK;
    }
    
    // -1表示新建一个信号文件描述符
    signal_fd = signalfd(-1, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC);
    if (signal_fd < 0) {
        swSysWarn("signalfd() failed");
        return SW_ERR;
    }
    // 套接字
    signal_socket = swoole::make_socket(signal_fd, SW_FD_SIGNAL);
    // SIG_BLOCK表示新的屏蔽字是当前信号屏蔽字和signalfd_mask的交集。
    if (sigprocmask(SIG_BLOCK, &signalfd_mask, nullptr) == -1) {
        swSysWarn("sigprocmask() failed");
        goto _error;
    }
    // 这部分具体设涉及的我还没看
    // 为socket设置函数处理器
    // 定时器时间到就会执行这个函数swSignalfd_onSignal
    swoole_event_set_handler(SW_FD_SIGNAL, swSignalfd_onSignal);
    if (swoole_event_add(signal_socket, SW_EVENT_READ) < 0) {
        goto _error;
    }
    // reactor线程添加可以退出的条件
    reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNALFD, [](Reactor *reactor, int &event_num) -> bool {
        event_num--;
        return true;
    });

    SwooleG.signal_fd = signal_fd;

    return SW_OK;

_error:
    signal_socket->fd = -1;
    signal_socket->free();
    close(signal_fd);
    signal_fd = 0;

    return SW_ERR;
}

其实看到这里是非常绕的,其实Timer定时器初始化有下面几个步骤:

1.Timer::init()执行的时候通过if决定是要基于事件的定时器还是基于setitimer的定时器。

2.如果选择基于事件的定时器,先初始化一些基本的属性,最主要的是下面这一步

定时器超时就会执行这个命令。

但是reactor如何定时执行我还没有去了解。。。。。

reactor->set_end_callback(Reactor::PRIORITY_TIMER, [this](Reactor *) { select(); });

3.如果是基于setitimer的定时器,先判断系统是否支持signalfd这个函数,支持的话会优先将信号处理为文件描述符,并加入事件机制中等待信号触发。

定时器超时就会执行signalfd_siginfo这个方法。

swoole_event_set_handler(SW_FD_SIGNAL, swSignalfd_onSignal)

如果系统不支持signalfd,那就是通过sigaction注册信号处理函数。

    if (sigaction(signo, &act, &oact) < 0) {
        return nullptr;
    }

这里的注册函数是,定时器超时就会执行这个函数swSignal_async_handler,

swSignal_async_handler里面也是根据signo找到对应的信号处理函数。

[](int sig) { SwooleG.signal_alarm = true; }

这里我的猜测是SwooleG.signal_alarm = true,reactor就能直接通过最小堆拿出堆顶的进行判断。

static void swSignal_async_handler(int signo) {
    if (sw_reactor()) {
        sw_reactor()->singal_no = signo;
    } else {
        // discard signal
        if (_lock) {
            return;
        }
        _lock = 1;
        swSignal_callback(signo);
        _lock = 0;
    }
}

void swSignal_callback(int signo) {
    if (signo >= SW_SIGNO_MAX) {
        swWarn("signal[%d] numberis invalid", signo);
        return;
    }
    
    swSignalHandler callback = signals[signo].handler;
    if (!callback) {
        swoole_error_log(SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, SW_UNREGISTERED_SIGNAL_FMT, swSignal_str(signo));
        return;
    }
    callback(signo);
}

天气很冷,边抖边写的,写的不好的话望斧正。谢谢啦!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值