[時間] 4つのサブシステム、低精度のタイマー

  タイマーによって、我々は、将来の特定の時間に特定のアクションを実行するためにコンピュータを制御することができます。従来のタイマクロックは、時間単位として(ジフィー)をマダニ、それは、より低い精度(例えばHZ = 1000、1ミリ秒の精度のために)、我々はまた、タイマーの精度が低い呼を有しています。

1.タイマーを初期化します

  私たちは、概要で導入init_timerでタイマーカーネルを初期化していた、3つの最も重要な情報でタイマーがある:成熟度、有効期限処理機能、期限切れ処理関数パラメータ。タイマーとマクロ構造の構造体timer_list init_timer(構造体タイマは、より適切な名前であってもよい)は、以下のように定義されます。

linux/include/linux/timer.h:

#define init_timer(timer)                       
    __init_timer((timer), 0)

#define __init_timer(_timer, _flags)            
    init_timer_key((_timer), (_flags), NULL, NULL)

struct timer_list {
    /*
     * All fields that change during normal runtime grouped to the
     * same cacheline
     */
    struct list_head entry; /*用于将当前定时器挂到CPU的tvec_base链表中*/
    unsigned long expires; /*定时器到期时间*/
    struct tvec_base *base; /*定时器所属的tvec_base*/

    void (*function)(unsigned long); /*到期处理函数*/
    unsigned long data; /*到期处理函数的参数*/

    int slack; /*允许的偏差值*/

    ...
};

  実装init_timer_key、CPUの初期化動作を実行するためにタイマー構造をtvec_baseを指します。各CPUコアは、各CPU上のタイマに関連するグローバル情報を記録するために使用される構造体tvec_baseオブジェクトを、(我々は次のセクションで詳細に説明する)が割り当てられます。

linux/kernel/timer.c:

/**
 * init_timer_key - initialize a timer
 * @timer: the timer to be initialized
 * @flags: timer flags
 * @name: name of the timer
 * @key: lockdep class key of the fake lock used for tracking timer
 *       sync lock dependencies
 *
 * init_timer_key() must be done to a timer prior calling *any* of the
 * other timer functions.
 */
void init_timer_key(struct timer_list *timer, unsigned int flags,
    const char *name, struct lock_class_key *key)
{
    debug_init(timer);
    do_init_timer(timer, flags, name, key);
}

static void do_init_timer(struct timer_list *timer, unsigned int flags,
    const char *name, struct lock_class_key *key)
{
    struct tvec_base *base = __raw_get_cpu_var(tvec_bases);

    timer->entry.next = NULL;
    timer->base = (void *)((unsigned long)base | flags);
    timer->slack = -1;
    ...
}

struct tvec_base {
    spinlock_t lock; /*同步当前tvec_base的链表操作*/
    struct timer_list *running_timer; /*正在运行(到期触发)的定时器*/
    unsigned long timer_jiffies; /*用于判断定时器是否到期的当前时间,通常和系统的jiffies值相等*/
    unsigned long next_timer; /*下一个到期的定时器的到期时间*/
    unsigned long active_timers; /*激活的定时器的个数*/
    struct tvec_root tv1; /*tv1~tv5是用于保存已添加定时器的链表,也称为时间轮*/
    struct tvec tv2;
    struct tvec tv3;
    struct tvec tv4;
    struct tvec tv5;
} ____cacheline_aligned;

/*
 * per-CPU timer vector definitions:
 */
#define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6)
#define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
#define MAX_TVAL ((unsigned long)((1ULL << (TVR_BITS + 4*TVN_BITS)) - 1))

struct tvec {
    struct list_head vec[TVN_SIZE];
};

struct tvec_root {
    struct list_head vec[TVR_SIZE];
};

2.タイマーを追加します。

  add_timerでtvec_baseのリストラウンドCPU時間を実行するためにタイマーを追加します。次のようにコアは、それぞれタイマーリストに配列の5つのレベルを連結タイマ満了時間jiffy単位は(小さい方の値が以前の有効期限が切れる)、現在の時刻との差に応じて、低レベルのリストは、以前に満了します表:

配列リスト 時差
TV1 0-255(2 ^ 8)
TV2 256から16383(2 ^ 14)
TV3 16384-1048575(2 ^ 20)
TV4 1048576-67108863(2 ^ 26)
TV5 67108864から4294967295(2 ^ 32)

  配列のサイズは、TV1 TVR_SIZE、TV2 TV3 TV4 TV5配列サイズは、設定項目に応じてTVN_SIZE、CONFIG_BASE_SMALLでされ、彼らは異なるサイズを有します。デフォルトでは、TVN_SIZEサイズは16で、時間TVR_SIZEサイズは64で、それはメモリ空間を節約したい場合には、TVN_SIZEサイズは64で、TVR_SIZEサイズは256で、CONFIG_BASE_SMALLを有効にCONFIG_BASE_SMALLを有効にすることができません以下の議論はCONFIG_BASE_SMALLの状況を作ることができない私に基づいています。新しいタイマーを追加するときに、タイマーの差分値に応じて、システムはタイマーを決定するために、jiffy単位のtimer_jiffiesフィールドを満了するTV5にTV1に配置された端の配列、システムのすべての下記に示すような構造のタイマー:

  ビューのadd_timerコードの実装の観点から、それは最終的にはタイマーと差がで適切なリストに追加された時間に応じて__internal_add_timer呼び出します。

linux/kernel/timer.c:

static void
__internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
    unsigned long expires = timer->expires;
    unsigned long idx = expires - base->timer_jiffies; /*idx即为时间差*/
    struct list_head *vec;

    if (idx < TVR_SIZE) {
        int i = expires & TVR_MASK; /*以超时时间(而非时间差idx)作为索引寻找对应的链表,方便后续的超时处理*/
        vec = base->tv1.vec + i;
    } else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
        int i = (expires >> TVR_BITS) & TVN_MASK;
        vec = base->tv2.vec + i;
    } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
        int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
        vec = base->tv3.vec + i;
    } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
        int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
        vec = base->tv4.vec + i;
    } else if ((signed long) idx < 0) {
        /*
         * Can happen if you add a timer with expires == jiffies,
         * or you set a timer to go off in the past
         */
        vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
    } else {
        int i;
        /* If the timeout is larger than MAX_TVAL (on 64-bit
         * architectures or with CONFIG_BASE_SMALL=1) then we
         * use the maximum timeout.
         */
        if (idx > MAX_TVAL) {
            idx = MAX_TVAL;
            expires = idx + base->timer_jiffies;
        }
        i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
        vec = base->tv5.vec + i;
    }
    /*
     * Timers are FIFO:
     */
    list_add_tail(&timer->entry, vec);
}

3.トリガタイマ

  セクション割り込みクロックでは、我々は毎回割り込み地元のタイマーを扱うときに処理がrun_local_timers呼び出されます言及しました。

linux/kernel/timer.c:

/*
 * Called by the local, per-CPU timer interrupt on SMP.
 */
void run_local_timers(void)
{
    ...
    raise_softirq(TIMER_SOFTIRQ); /*最终在中断返回时进入软中断处理函数run_timer_softirq*/
}

/*
 * This function runs timers and the timer-tq in bottom half context.
 */
static void run_timer_softirq(struct softirq_action *h)
{
    struct tvec_base *base = __this_cpu_read(tvec_bases);

    ...

    if (time_after_eq(jiffies, base->timer_jiffies)) /*实际当前时间晚于base中记录的当前时间,说明需要更新base中时间或者有定时器到期*/
        __run_timers(base);
}

  処理ロジックタイマーが切れる、プロセスは、リストがすべてTV1空の場合は、必ずTV1における最初のタイマーであるし、リストから移動し、TV1のTV2に追加し直す; TV1とTV2が空の場合、その後からようにと、携帯TV1およびTV2のリストに再追加TV3。コードは次のように実装されています。

linux/kernel/timer.c:

/**
 * __run_timers - run all expired timers (if any) on this CPU.
 * @base: the timer vector to be processed.
 *
 * This function cascades all vectors and executes all expired timer
 * vectors.
 */
static inline void __run_timers(struct tvec_base *base)
{
    struct timer_list *timer;

    spin_lock_irq(&base->lock);
    while (time_after_eq(jiffies, base->timer_jiffies)) {
        struct list_head work_list;
        struct list_head *head = &work_list;
        int index = base->timer_jiffies & TVR_MASK; /*以base中的当前时间为索引取出已到期的定时器*/

        
      
      大专栏  【时间子系统】四、低精度定时器span>/*
         * Cascade timers:
         */
        /*如果低级链表为空,则从高级别链表中移动添加到低级别中*/
        if (!index &&
            (!cascade(base, &base->tv2, INDEX(0))) &&
            (!cascade(base, &base->tv3, INDEX(1))) &&
            !cascade(base, &base->tv4, INDEX(2)))
                cascade(base, &base->tv5, INDEX(3));
        ++base->timer_jiffies; /*累加base中当前时间*/
        list_replace_init(base->tv1.vec + index, &work_list);
        /*处理已到期的定时期的回调函数*/
        while (!list_empty(head)) {
            void (*fn)(unsigned long);
            unsigned long data;
            bool irqsafe;

            timer = list_first_entry(head, struct timer_list,entry);
            fn = timer->function;
            data = timer->data;
            irqsafe = tbase_get_irqsafe(timer->base);

            timer_stats_account_timer(timer);

            base->running_timer = timer;
            detach_expired_timer(timer, base);

            if (irqsafe) {
                spin_unlock(&base->lock);
                call_timer_fn(timer, fn, data);
                spin_lock(&base->lock);
            } else {
                spin_unlock_irq(&base->lock);
                call_timer_fn(timer, fn, data);
                spin_lock_irq(&base->lock);
            }
        }
    }
    base->running_timer = NULL;
    spin_unlock_irq(&base->lock);
}

#define INDEX(N) ((base->timer_jiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)

static int cascade(struct tvec_base *base, struct tvec *tv, int index)
{
    /* cascade all the timers from tv up one level */
    struct timer_list *timer, *tmp;
    struct list_head tv_list;

    list_replace_init(tv->vec + index, &tv_list);

    /*
     * We are removing _all_ timers from the list, so we
     * don't have to detach them individually.
     */
    list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
        BUG_ON(tbase_get_base(timer->base) != base);
        /* No accounting, while moving them */
        __internal_add_timer(base, timer);
    }

    return index;
}


指定してください再現:ウーさんのブログ >> [時間] 4つのサブシステムを、低精度のタイマー

おすすめ

転載: www.cnblogs.com/sanxiandoupi/p/11691967.html