bind 定时器

本文详细介绍了BIND中定时器的实现,包括数据结构如timermgr的内存分配器、互斥锁和小根堆,以及定时器管理器的创建、调度和销毁过程。定时器分为ticker、once、limited和inactive四种类型,用于执行定时任务,如interface_timer、heartbeat_timer和pps_timer。文章还阐述了如何通过isc_timer_reset、isc_timer_create和isc_event_allocate等函数进行管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

BIND中有一些操作是定时任务,server.c的run_server函数中创建了三个定时任务,分别执行interface_timer_tick、heartbeat_timer_tickpps_timer_tick;其他模块中还有很多时间任务。定时器的实现文件时timer.h和timer.c,位于lib/isc目录下。

数据结构

struct isc_timermgr {
    /* Not locked. */
    unsigned int            magic;
    isc_mem_t *         mctx;
    isc_mutex_t         lock;
    /* Locked by manager lock. */
    isc_boolean_t           done;
    LIST(isc_timer_t)       timers;
    unsigned int            nscheduled;
    isc_time_t          due;
#ifdef ISC_PLATFORM_USETHREADS
    isc_condition_t         wakeup;
    isc_thread_t            thread;
#else /* ISC_PLATFORM_USETHREADS */
    unsigned int            refs;
#endif /* ISC_PLATFORM_USETHREADS */
    isc_heap_t *            heap;
};

mtx是timermgr的内存分配器,它负责timermgr的内存分配和回收;
lock是timermgr的互斥锁,用来在多线程中保护done,timers,nscheduled等数据;
timers是timermgr管理timer,BIND中分三类timer,分别是定时执行的ticker、只执行一次的once、限制执行次数的limited和非活动的inactive,刚创建的timer都是inactive类型的,之后用isc_timer_reset将其变为前三种;
due是该事件管理器中所有时间的最小的到期时间(绝对时间);
wakeup是用来实现定时任务的条件变量
thread是执行定时任务调度的线程;
refs是该timermgr的引用计数
heap是一些供调度的timer按照过期时间组成的一个小根堆。

调用关系

定时器管理器ns_g_timermgr只在server.c的create_managers中创建一次,创建时调用isc_timermgr_create函数。

定时器管理器内部调用关系

定时器管理器内部调用关系
这里写图片描述

isc_timermgr_create的说明 :

初始化各变量;
启用线程thread执行函数run

run线程是真正执行时间调度的线程,它获取当前的绝对时间now,然后调用dispatch做时间调度,之后如果没有要调度的时间了就用pthread_cond_wait等待直到有新的时间到达;如果还有要调度的时间,则调用pthread_cond_timedwait等待最近的那个timer的到期时间(保存在manager->due中)。

dispatch(manage, now)是真正做timer调度的函数,它从manager的堆heap中取出堆顶的timer,然后判断当前时间now是否大于了timer的过期时间due,如果不大于,则更新manager->due;如果now超过了timer的过期时间,则根据timer的类型做各种判断,判断是否需要重新调度,判断是否需要为此分发事件…..如果需要为此timer分发事件,则调用isc_event_allocate新建一个事件,然后调用isc_task_send将此event分发到任务系统中,此时任务系统中监听的线程就会执行为此timer绑定的函数。之后将此timer从manager->heap中取出,将它管理的timer数量减一,如果需要重新调度,则调用schedule重新调度此timer。

schedule:它的主要任务是将一个timer加入到调度堆中。中间需要做一些时间的修正,如果需要唤醒thread线程,则调用pthread_cond_signal唤醒thread线程。

deschedule,它将一个线程从manager->heap中删除,并唤醒thread线程

定时器管理器销毁
定时器管理器销毁

时间类

创建3个定时任务。
3个定时器

时间对象timer是timer manager管理的单位,BIND中分三类timer,分别是定时执行的ticker、只执行一次的once、限制执行次数的limited和非活动的inactive,刚创建的timer一般都是inactive类型的,之后用isc_timer_reset将其变为前三种中的一种。

下面看看timer的数据结构:

struct isc_timer {
    /*! Not locked. */
    unsigned int            magic;
    isc_timermgr_t *        manager;
    isc_mutex_t         lock;
    /*! Locked by timer lock. */
    unsigned int            references;
    isc_time_t          idle;
    /*! Locked by manager lock. */
    isc_timertype_t         type;
    isc_time_t          expires;
    isc_interval_t          interval;
    isc_task_t *            task;
    isc_taskaction_t        action;
    void *              arg;
    unsigned int            index;
    isc_time_t          due;
    LINK(isc_timer_t)       link;
};

ticker类型ticker类型的timer每隔interval被分发到事件驱动中
server中有三个ticker类型的timer,他们分别是interface_timer, heartbeat_timer和pps_timer拿interface_timer做分析:

1,首先在run_server中被初始化:
在isc_timer_create中,为它初始化各成员后,只是简单地将它加入到timer manager的timers队列中。

2,之后在load_configuration中被reset为ticker类型
isc_timer_reset(server->interface_timer, isc_timertype_ticker,NULL, &interval, ISC_FALSE)在isc_timter_reset中,为它的interval成员赋值&interval

3,然后调用schedule将其加入调度队列:
schedule(timer, &now, ISC_TRUE)
在schedule中,为其计算到期时间due;如果它未被调度过,就调用isc_heap_insert(manager->heap, timer)将它插入到timer manager的调度堆heap中,将它从堆中floatup;最后根据情况唤醒timer manager中执行run的thread线程

4,在thead线程中,线程首先检查它的到期时间timer->due是否到如果到了,则为它分配新的事件,然后调用isc_task_send(timer->task,ISC_EVENT_PTR(&event))将它加入到事件驱动器中。把它从heap中取出,然后重新调用schedule;将它加入到下一次调度中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值