1. 程式人生 > >[轉載]linux 核心定時器詳解

[轉載]linux 核心定時器詳解

Linux核心2.4版中去掉了老版本核心中的靜態定時器機制,而只留下動態定時器。相應地在timer_bh()函式中也不再通過run_old_timers()函式來執行老式的靜態定時器。動態定時器與靜態定時器這二個概念是相對於Linux核心定時器機制的可擴充套件功能而言的,動態定時器是指核心的定時器佇列是可以動態變化的,然而就定時器本身而言,二者並無本質的區別。考慮到靜態定時器機制的能力有限,因此Linux核心2.4版中完全去掉了以前的靜態定時器機制。

7.6.1 Linux核心對定時器的描述

Linux在include/linux/timer.h標頭檔案中定義了資料結構timer_list來描述一個核心定時器:

struct timer_list { 
    struct list_head list; 
    unsigned long expires; 
    unsigned long data; 
    void (*function)(unsigned long); 
};

各資料成員的含義如下:

  1. 雙向連結串列元素list:用來將多個定時器連線成一條雙向迴圈佇列。
  2. expires:指定定時器到期的時間,這個時間被表示成自系統啟動以來的時鐘滴答計數(也即時鐘節拍數)。當一個定時器的expires值小於或等於jiffies變數時,我們就說這個定時器已經超時或到期了。在初始化一個定時器後,通常把它的expires域設定成當前expires變數的當前值加上某個時間間隔值(以時鐘滴答次數計)。
  3. 函式指標function:指向一個可執行函式。當定時器到期時,核心就執行function所指定的函式。而data域則被核心用作function函式的呼叫引數。

核心函式init_timer()用來初始化一個定時器。實際上,這個初始化函式僅僅將結構中的list成員初始化為空。如下所示(include/linux/timer.h):

static inline void init_timer(struct timer_list * timer) 
{ timer
->list.next = timer->list.prev = NULL; }

時間比較操作

在定時器應用中經常需要比較兩個時間值,以確定timer是否超時,所以Linux核心在timer.h標頭檔案中定義了4個時間關係比較操作巨集。這裡我們說時刻a在時刻b之後,就意味著時間值a≥b。

Linux強烈推薦使用者使用它所定義的下列4個時間比較操作巨集(include/linux/timer.h):

#define time_after(a,b) ((long)(b) - (long)(a) < 0) 
#define time_before(a,b) time_after(b,a) 

#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) 
#define time_before_eq(a,b) time_after_eq(b,a)

7.6.2 動態核心定時器機制的原理

Linux是怎樣為其核心定時器機制提供動態擴充套件能力的呢?其關鍵就在於“定時器向量”的概念。所謂“定時器向量”就是指這樣一條雙向迴圈定時器佇列(對列中的每一個元素都是一個timer_list結構):對列中的所有定時器都在同一個時刻到期,也即對列中的每一個timer_list結構都具有相同的expires值。顯然,可以用一個timer_list結構型別的指標來表示一個定時器向量。顯然,定時器expires成員的值與jiffies變數的差值決定了一個定時器將在多長時間後到期。在32位系統中,這個時間差值的最大值應該是0xFFFFFFFF。因此如果是基於“定時器向量”基本定義,核心將至少要維護0xFFFFFFFF個timer_list結構型別的指標,這顯然是不現實的。另一方面,從核心本身這個角度看,它所關心的定時器顯然不是那些已經過期而被執行過的定時器(這些定時器完全可以被丟棄),也不是那些要經過很長時間才會到期的定時器,而是那些當前已經到期或者馬上就要到期的定時器(注意!時間間隔是以滴答次數為計數單位的)。

基於上述考慮,並假定一個定時器要經過interval個時鐘滴答後才到期(interval=expires-jiffies),則Linux採用了下列思想來實現其動態核心定時器機制:對於那些0≤interval≤255的定時器,Linux嚴格按照定時器向量的基本語義來組織這些定時器,也即Linux核心最關心那些在接下來的255個時鐘節拍內就要到期的定時器,因此將它們按照各自不同的expires值組織成256個定時器向量。而對於那些256≤interval≤0xFFFFFFFF的定時器,由於他們離到期還有一段時間,因此核心並不關心他們,而是將它們以一種擴充套件的定時器向量語義(或稱為“鬆散的定時器向量語義”)進行組織。

所謂“鬆散的定時器向量語義”就是指:各定時器的expires值可以互不相同的一個定時器佇列。具體的組織方案可以分為兩大部分:

(1)對於核心最關心的、interval值在[0,255]之間的前256個定時器向量,核心是這樣組織它們的:這256個定時器向量被組織在一起組成一個定時器向量陣列,並作為資料結構timer_vec_root的一部分,該資料結構定義在kernel/timer.c檔案中,如下述程式碼段所示:

/* 
 * Event timer code 
 */ 
#define TVN_BITS 6 
#define TVR_BITS 8 
#define TVN_SIZE (1 << TVN_BITS)    //64
#define TVR_SIZE (1 << TVR_BITS)    //256
#define TVN_MASK (TVN_SIZE - 1)     //63
#define TVR_MASK (TVR_SIZE - 1)     //255

struct timer_vec { 
    int index; 
    struct list_head vec[TVN_SIZE]; //64
}; 

struct timer_vec_root { 
    int index; 
    struct list_head vec[TVR_SIZE]; //256
}; 

static struct timer_vec tv5; 
static struct timer_vec tv4; 
static struct timer_vec tv3; 
static struct timer_vec tv2; 
static struct timer_vec_root tv1; 

static struct timer_vec * const tvecs[] = { 
    (struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5 
}; 

#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0]))

基於資料結構timer_vec_root,Linux定義了一個全域性變數tv1,以表示核心所關心的前256個定時器向量。這樣核心在處理是否有到期定時器時,它就只從定時器向量陣列tv1.vec[256]中的某個定時器向量內進行掃描。而tv1的index欄位則指定當前正在掃描定時器向量陣列tv1.vec[256]中的哪一個定時器向量,也即該陣列的索引,其初值為0,最大值為255(以256為模)。每個時鐘節拍時index欄位都會加1。顯然,index欄位所指定的定時器向量tv1.vec[index]中包含了當前時鐘節拍內已經到期的所有動態定時器。而定時器向量tv1.vec[index+k]則包含了接下來第k個時鐘節拍時刻將到期的所有動態定時器。當index值又重新變為0時,就意味著核心已經掃描了tv1變數中的所有256個定時器向量。在這種情況下就必須將那些以鬆散定時器向量語義來組織的定時器向量補充到tv1中來。

(2)而對於核心不關心的、interval值在[0xff,0xFFFFFFFF]之間的定時器,它們的到期緊迫程度也隨其interval值的不同而不同。顯然interval值越小,定時器緊迫程度也越高。因此在將它們以鬆散定時器向量進行組織時也應該區別對待。通常,定時器的interval值越小,它所處的定時器向量的鬆散度也就越低(也即向量中的各定時器的expires值相差越小);而interval值越大,它所處的定時器向量的鬆散度也就越大(也即向量中的各定時器的expires值相差越大)。核心規定,對於那些滿足條件:0x100≤interval≤0x3FFF的定時器,只要表示式(interval>>8)具有相同值的定時器都將被組織在同一個鬆散定時器向量中。因此,為組織所有滿足條件0x100≤interval≤0x3FFF的定時器,就需要1>>6=64個鬆散定時器向量。同樣地,為方便起見,這64個鬆散定時器向量也放在一起形成陣列,並作為資料結構timer_vec的一部分。基於資料結構timer_vec,Linux定義了全域性變數tv2,來表示這64條鬆散定時器向量。如上述程式碼段所示。

  • 對於那些滿足條件0x4000≤interval≤0xFFFFF的定時器,只要表示式(interval>>8+6)的值相同的定時器都將被放在同一個鬆散定時器向量中。同樣,要組織所有滿足條件0x4000≤interval≤0xFFFFF的定時器,也需要1>>6=64個鬆散定時器向量。類似地,這64個鬆散定時器向量也可以用一個timer_vec結構來描述,相應地Linux定義了tv3全域性變數來表示這64個鬆散定時器向量。
  • 對於那些滿足條件0x100000≤interval≤0x3FFFFFF的定時器,只要表示式(interval>>8+6+6)的值相同的定時器都將被放在同一個鬆散定時器向量中。同樣,要組織所有滿足條件0x100000≤interval≤0x3FFFFFF的定時器,也需要1>>6=64個鬆散定時器向量。類似地,這64個鬆散定時器向量也可以用一個timer_vec結構來描述,相應地Linux定義了tv4全域性變數來表示這64個鬆散定時器向量。
  • 對於那些滿足條件0x4000000≤interval≤0xFFFFFFFF的定時器,只要表示式(interval>>8+6+6+6)的值相同的定時器都將被放在同一個鬆散定時器向量中。同樣,要組織所有滿足條件0x4000000≤interval≤0xFFFFFFFF的定時器,也需要1>>6=64個鬆散定時器向量。類似地,這64個鬆散定時器向量也可以用一個timer_vec結構來描述,相應地Linux定義了tv5全域性變數來表示這64個鬆散定時器向量。

最後,為了引用方便,Linux定義了一個指標陣列tvecs[],來分別指向tv1、tv2、…、tv5結構變數。如上述程式碼所示。

7.6.3 核心動態定時器機制的實現

在核心動態定時器機制的實現中,有三個操作時非常重要的:

  1. 將一個定時器插入到它應該所處的定時器向量中。
  2. 定時器的遷移,也即將一個定時器從它原來所處的定時器向量遷移到另一個定時器向量中。
  3. 掃描並執行當前已經到期的定時器。

7.6.3.1 動態定時器機制的初始化

函式init_timervecs()實現對動態定時器機制的初始化。該函式僅被sched_init()初始化例程所呼叫。動態定時器機制初始化過程的主要任務就是將tv1、tv2、…、tv5這5個結構變數中的定時器向量指標陣列vec[]初始化為NULL。如下所示(kernel/timer.c):

void init_timervecs (void) 
{ 
    int i; 

    for (i = 0; i < TVN_SIZE; i++) { 
        INIT_LIST_HEAD(tv5.vec + i); 
        INIT_LIST_HEAD(tv4.vec + i); 
        INIT_LIST_HEAD(tv3.vec + i); 
        INIT_LIST_HEAD(tv2.vec + i); 
    } 
    for (i = 0; i < TVR_SIZE; i++) {
        INIT_LIST_HEAD(tv1.vec + i);
    }        
}

上述函式中的巨集TVN_SIZE是指timer_vec結構型別中的定時器向量指標陣列vec[]的大小,值為64。巨集TVR_SIZE是指timer_vec_root結構型別中的定時器向量陣列vec[]的大小,值為256。

7.6.3.2 動態定時器的時鐘滴答基準timer_jiffies

由於動態定時器是在時鐘中斷的Bottom Half中被執行的,而從TIMER_BH向量被啟用到其timer_bh()函式真正執行這段時間內可能會有幾次時鐘中斷髮生。因此核心必須記住上一次執行定時器機制是什麼時候,也即核心必須儲存上一次執行定時器機制時的jiffies值。為此,Linux在kernel/timer.c檔案中定義了全域性變數timer_jiffies來表示上一次執行定時器機制時的jiffies值。該變數的定義如下所示:

static unsigned long timer_jiffies;

7.6.3.3 對核心動態定時器連結串列的保護

由於核心動態定時器連結串列是一種系統全域性共享資源,為了實現對它的互斥訪問,Linux定義了專門的自旋鎖timerlist_lock來保護。任何想要訪問動態定時器連結串列的程式碼段都首先必須先持有該自旋鎖,並且在訪問結束後釋放該自旋鎖。其定義如下(kernel/timer.c):

/* Initialize both explicitly - let's try to have them in the same cache line */ 
spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED;

7.6.3.4 將一個定時器插入到連結串列中

函式add_timer()用來將引數timer指標所指向的定時器插入到一個合適的定時器連結串列中。它首先呼叫timer_pending()函式判斷所指 定的定時器是否已經位於在某個定時器向量中等待執行。如果是,則不進行任何操作,只是列印一條核心告警資訊就返回了;如果不是,則呼叫 internal_add_timer()函式完成實際的插入操作。其原始碼如下(kernel/timer.c):

void add_timer(struct timer_list *timer) 
{ 
    unsigned long flags; 
    spin_lock_irqsave(&timerlist_lock, flags); 
    if (timer_pending(timer)) 
        goto bug; 
    internal_add_timer(timer); 
    spin_unlock_irqrestore(&timerlist_lock, flags); 
    return; 
bug: 
    spin_unlock_irqrestore(&timerlist_lock, flags); 
    printk("bug: kernel timer added twice at %p.\n", __builtin_return_address(0)); 
}

函式internal_add_timer()用於將一個不處於任何定時器向量中的定時器插入到它應該所處的定時器向量中去(根據定時器的expires值來決定)。如下所示(kernel/timer.c):

static inline void internal_add_timer(struct timer_list *timer)  /* * must be cli-ed when calling this */ 
{
    unsigned long expires = timer->expires; 
    unsigned long idx = expires - timer_jiffies; 
    struct list_head * vec; 
    if (idx < TVR_SIZE) { // idx < 256
        int i = expires & TVR_MASK; 
        vec = tv1.vec + i; 
    } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { // [0x100, 0x3fff)
        int i = (expires >> TVR_BITS) & TVN_MASK; 
        vec = tv2.vec + i; 
    } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) { // [0x4000, 0xfffff]
        int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; 
        vec = tv3.vec + i; 
    } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) { // [0x100000, 0x3ffffff]
        int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; 
        vec = 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 = tv1.vec + tv1.index;
    } else if (idx <= 0xffffffffUL) { 
        int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; 
        vec = tv5.vec + i; 
    } else { 
        /* Can only get here on architectures with 64-bit jiffies */ 
        INIT_LIST_HEAD(&timer->list); 
        return; 
    } 
    /* Timers are FIFO! */ 
    list_add(&timer->list, vec->prev); 
}

對該函式的註釋如下:

  • 首先,計算定時器的expires值與timer_jiffies的插值(注意!這裡應該使用動態定時器自己的時間基準),這個差值就表示這個定時器相對於上一次執行定時器機制的那個時刻還需要多長時間間隔才到期。區域性變數idx儲存這個差值。
  • 根據idx的值確定這個定時器應被插入到哪一個定時器向量中。其具體的確定方法我們在7.6.2節已經說過了,這裡不再詳述。最後,定時器向量的頭部指標vec表示這個定時器應該所處的定時器向量連結串列頭部。
  • 最後,呼叫list_add()函式將定時器插入到vec指標所指向的定時器佇列的尾部。

7.6.3.5 修改一個定時器的expires值

當一個定時器已經被插入到核心動態定時器連結串列中後,我們還可以修改該定時器的expires值。函式mod_timer()實現這一點。如下所示(kernel/timer.c):

int mod_timer(struct timer_list *timer, unsigned long expires) 
{ 
    int ret; 
    unsigned long flags; 
    spin_lock_irqsave(&timerlist_lock, flags); 
    timer->expires = expires; 
    ret = detach_timer(timer); 
    internal_add_timer(timer); 
    spin_unlock_irqrestore(&timerlist_lock, flags); 
    return ret; 
}

該函式首先根據引數expires值更新定時器的expires成員。然後呼叫detach_timer()函式將該定時器從它原來所屬的連結串列中刪除。最後呼叫internal_add_timer()函式將該定時器根據它新的expires值重新插入到相應的連結串列中。函 數detach_timer()首先呼叫timer_pending()來判斷指定的定時器是否已經處於某個連結串列中,如果定時器原來就不處於任何連結串列中, 則detach_timer()函式什麼也不做,直接返回0值,表示失敗。否則,就呼叫list_del()函式將定時器從它原來所處的連結串列中摘除。如下 所示(kernel/timer.c):

static inline int detach_timer (struct timer_list *timer) 
{ 
    if (!timer_pending(timer)) 
        return 0; 
    list_del(&timer->list); 
    return 1; 
}

7.6.3.6 刪除一個定時器

函式del_timer()用來將一個定時器從相應的核心定時器佇列中刪除。該函式實際上是對detach_timer()函式的高層封裝。如下所示(kernel/timer.c):

int del_timer(struct timer_list * timer) 
{ 
    int ret; 
    unsigned long flags; 

    spin_lock_irqsave(&timerlist_lock, flags); 
    ret = detach_timer(timer); 
    timer->list.next = timer->list.prev = NULL; 
    spin_unlock_irqrestore(&timerlist_lock, flags); 
    return ret; 
}

7.6.3.7 定時器遷移操作

由於一個定時器的interval值會隨著時間的不斷流逝(即jiffies值的不斷增大)而不斷變小,因此那些原本到期緊迫程度較低的定時器會隨著jiffies值的不斷增大而成為既把馬上到期的定時器。比如定時器向量tv2.vec[0]中的定時器在經過256個時鐘滴答後會成為未來256個時鐘滴答內會到期的定時器。因此,定時器在核心動態定時器連結串列中的位置也應相應地隨著改變。

改變的規則是:當tv1.index重新變為0時(意味著tv1中的256個定時器向量都已被核心掃描一遍了,從而使tv1中的256個定時器向量變為空),則用tv2.vec[index]定時器向量中的定時器去填充tv1,同時使tv2.index加1(它以64為模)。當tv2.index重新變為0(意味著tv2中的64個定時器向量都已經被全部填充到tv1中去了,從而使得tv2變為空),則用tv3.vec[index]定時器向量中的定時器去填充tv2。如此一直類推下去,直到tv5。

函式cascade_timers()完成這種定時器遷移操作,該函式只有一個timer_vec結構型別指標的引數tv。這個函式把把定時器向量tv->vec[tv->index]中的所有定時器重新填充到上一層定時器向量中去。如下所示(kernel/timer.c):

static inline void cascade_timers(struct timer_vec *tv) 
{ 
    /* cascade all the timers from tv up one level */ 
    struct list_head *head, *curr, *next; 

    head = tv->vec + tv->index; 
    curr = head->next; 
    /* 
    * We are removing _all_ timers from the list, so we don't have to 
    * detach them individually, just clear the list afterwards. 
    */ 
    while (curr != head) { 
        struct timer_list *tmp; 

        tmp = list_entry(curr, struct timer_list, list); 
        next = curr->next; 
        list_del(curr); // not needed 
        internal_add_timer(tmp); 
        curr = next; 
    } 
    INIT_LIST_HEAD(head); 
    tv->index = (tv->index + 1) & TVN_MASK; 
}

對該函式的註釋如下:

  1. 首先,用指標head指向定時器頭部向量頭部的list_head結構。指標curr指向定時器向量中的第一個定時器。
  2. 然後,用一個while{}迴圈來遍歷定時器向量tv->vec[tv->index]。由於定時器向量是一個雙向迴圈佇列,因此迴圈的終止條件是curr=head。對於每一個被掃描的定時器,迴圈體都先呼叫list_del()函式把當前定時器從連結串列中摘除,然後呼叫internal_add_timer()函式重新確定該定時器應該被放到哪個定時器向量中去。
  3. 當從while{}迴圈退出後,定時器向量tv->vec[tv->index]中所有的定時器都已被遷移到其它地方(到它們該呆的地方:-),因此它本身就成為一個空佇列。這裡我們顯示地呼叫INIT_LIST_HEAD()巨集來把定時器向量的表頭結構初始化為空。
  4. 最後,把tv->index值加1,當然它是以64為模。

7.6.4.8 掃描並執行當前已經到期的定時器

函式run_timer_list()完成這個功能。如前所述,該函式是被timer_bh()函式所呼叫的,因此核心定時器是在時鐘中斷的Bottom Half中被執行的。記住這一點非常重要。全域性變數timer_jiffies表示了核心上一次執行run_timer_list()函式的時間,因此jiffies與timer_jiffies的差值就表示了自從上一次處理定時器以來,期間一共發生了多少次時鐘中斷,顯然run_timer_list()函式必須為期間所發生的每一次時鐘中斷補上定時器服務。該函式的原始碼如下(kernel/timer.c):

static inline void run_timer_list(void) 
{ 
    spin_lock_irq(&timerlist_lock); 
    while ((long)(jiffies - timer_jiffies) >= 0) { 
        struct list_head *head, *curr; 
        if (!tv1.index) { 
            int n = 1; 
            do { 
                cascade_timers(tvecs[n]); 
            } while (tvecs[n]->index == 1 && ++n < NOOF_TVECS); 
        } 
repeat: head
= tv1.vec + tv1.index; curr = head->next; if (curr != head) { struct timer_list *timer; void (*fn)(unsigned long); unsigned long data; timer = list_entry(curr, struct timer_list, list); fn = timer->function; data= timer->data; detach_timer(timer); timer->list.next = timer->list.prev = NULL; timer_enter(timer); spin_unlock_irq(&timerlist_lock); fn(data); spin_lock_irq(&timerlist_lock); timer_exit(); goto repeat; } ++timer_jiffies; tv1.index = (tv1.index + 1) & TVR_MASK; } spin_unlock_irq(&timerlist_lock); }

函式run_timer_list()的執行過程主要就是用一個大while{}迴圈來為時鐘中斷執行定時器服務,每一次迴圈服務一次時鐘中斷。因此一共要執行(jiffies-timer_jiffies+1)次迴圈。迴圈體所執行的服務步驟如下:

  1.  首先,判斷tv1.index是否為0,如果為0則需要從tv2中補充定時器到tv1中來。但tv2也可能為空而需要從tv3中補充定時器,因此用一個do{}while迴圈來呼叫cascade_timer()函式來依次視需要從tv2中補充tv1,從tv3中補充tv2、…、從tv5中補充tv4。顯然如果tvi.index=0(2≤i≤5),則對於tvi執行cascade_timers()函式後,tvi.index肯定為1。反過來講,如果對tvi執行過cascade_timers()函式後tvi.index不等於1,那麼可以肯定在未對tvi執行cascade_timers()函式之前,tvi.index值肯定不為0,因此這時tvi不需要從tv(i+1)中補充定時器,這時就可以終止do{}while迴圈。
  2. 接下來,就要執行定時器向量tv1.vec[tv1.index]中的所有到期定時器。因此這裡用一個goto repeat迴圈從頭到尾依次掃描整個定時器對列。由於在執行定時器的關聯函式時並不需要關CPU中斷,所以在用detach_timer()函式把當前定時器從對列中摘除後,就可以呼叫spin_unlock_irq()函式進行解鎖和開中斷,然後在執行完當前定時器的關聯函式後重新用spin_lock_irq()函式加鎖和關中斷。
  3. 當執行完定時器向量tv1.vec[tv1.index]中的所有到期定時器後,tv1.vec[tv1.index]應該是個空佇列。至此這一次定時器服務也就宣告結束。
  4. 最後,把timer_jiffies值加1,把tv1.index值加1,當然它的模是256。然後,回到while迴圈開始下一次定時器服務。

相關推薦

[轉載]linux 核心定時

Linux核心2.4版中去掉了老版本核心中的靜態定時器機制,而只留下動態定時器。相應地在timer_bh()函式中也不再通過run_old_timers()函式來執行老式的靜態定時器。動態定時器與靜態定時器這二個概念是相對於Linux核心定時器機制的可擴充套件功能而言的,動態定時器是指核心的定時器佇列

Linux核心定時

static struct pin_desc *irq_pd; /* 鍵值: 按下時, 0x01, 0x02, 0x03, 0x04 */ /* 鍵值: 鬆開時, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; struct pin_desc{u

Linux定時

今天在看linux的定時任務,瞭解了一下crontab命令,下面我們來一起學習一下。 首先要知道 crontab 檔案的格式: {minute} {hour} {day-of-month} {month} {day-of-week} {full-path-to-shell-script} o m

Linux——vim編輯

linux——vim編輯器詳解 vim 十六、使用vim編輯多個文件用法: vim FILE1 FILE2 FILE3文件之間切換:末行模式下: :next 切換至下一個文件 :prev 切換至前一個文件 :last 切換至最後一個文件 :first 切換至第

Node 定時

來源:阮一峰的網路日誌,作者:阮一峰,微博@ruanyf 連結:ruanyifeng.com/blog/2018/02/node-event-loop.html(點選尾部閱讀原文前往) JavaScript 是單執行緒執行,非同步操作特別重要。 只要用到引擎之外的

linux 核心 - ioctl 函式

1. 概念 ioctl 是裝置驅動程式中裝置控制介面函式,一個字元裝置驅動通常會實現裝置開啟、關閉、讀、寫等功能,在一些需要細分的情境下,如果需要擴充套件新的功能,通常以增設 ioctl() 命令的方式實現。 在檔案 I/O 中,ioctl 扮演著重要角色,本文將以驅動開發為側重

linux crontab定時任務

使用crontab你可以在指定的時間執行一個shell指令碼或者一系列Linux命令。例如系統管理員安排一個備份任務使其每天都執行 如何往 cron 中新增一個作業? # crontab –e0 5 * * * /root/bin/backup.sh 這將會在每天早上5點執行 /root/bin/bac

linux核心定時 記錄

驅動程式中使用timer的幾個必要的操作 1.分配 static struct timer_list pwm_timer; 2.設定、新增 pwm_timer.function = pwm_timer_function; pwm_timer.expires  = jiffies

jmeter 定時

meter提供了很多元件,幫助我們更好的完成各種場景的效能測試,其中,定時器(timer)是很重要的一個元件,最新的3.0版本jemter提供了9種定時器(之前6種),下面一一介紹: 一、定時器的作用域 1、定時器是在每個sampler(取樣器)之前執行的,而不是之後(無論定時器位置在

javascript定時

JS定時器 定時器是javascript的重點部分,在以後的很多實戰專案裡都會用到。 在javascript中,與定時器有關的方法是: setInterval、clearInterval 以及 setTimeout、clearTimeout 這些方法都是定義在window物件上面的,因此我們寫wi

Node定時

//次輪迴圈執行 setTimeout(() => console.log(1)); setImmediate(() => console.log(2)); //本輪迴圈執行 process.nextTick(() => console.log(3)); Promise.r

Linux核心定時-- timer_list

一.概述       核心經常要推後執行某些程式碼,如底半部機制就是為了將工作推後執行。timer_list為我們提供一種方式,使工作能夠在指定時間點上執行。       定時器使用簡單,只須執行一些初始化工作,設定一個超時時間,指定超時發生後執行的函式,然後啟用定時器就可

STM32F103定時

STM32F103系列的微控制器一共有11個定時器,其中: 2個高階定時器 4個普通定時器 2個基本定時器 2個看門狗定時器1個系統嘀嗒定時器 除去看門狗定時器和系統滴答定時器的八個定時器列表; 8個定時器分成3個組;TIM1和TIM8是高階定時器 TIM2-TIM5

作業系統之--linux核心編譯步驟

ORIGIN 作為自由軟體,linux 核心版本不斷更新,新核心會修訂舊核心的 bug,並增加若干新特性,如支援更多的硬體、具備更好的系統管理能力、執行速度更快、更穩定等。使用者若想要使用這些新特性,或希望根據自身系統需求定製一個更高效、更穩定的核心,就需要重

Linux 核心編譯選項

linux核心編譯選項詳解(一):General setup [*]Prompt for development and/or incomplete code/drivers 顯示尚在開發中或尚未完成的程式碼與驅動.你應該選擇它,因為有許多裝置可能必需選擇這個選項才能進行配

Linux 核心定時使用 一 低精度定時

核心定時器是一個數據結構,它告訴核心在使用者定義的時間點使用使用者定義的引數來執行一個使用者定義的函式。其實現位於 <linux/timer.h>中。 核心提供了一組用來宣告、註冊和刪除核心定時器的函式,相關介面如下: struct timer_list {

微控制器中斷原理及定時

一、中斷系統1.1.中斷的概念什麼是中斷:CPU在處理某一事件A時,發生的另外某一事件B請求CPU去處理(產生了中斷),隨後CPU暫時中斷當前正在執行的任務,去對事件B進行處理,CPU處理完事件B後再返回之前中斷的位置繼續執行原來的事件A,這一過程總稱為中斷。1.2.中斷流程

Node定時----深入node

javascript是單執行緒執行,非同步操作特別重要 libuv庫可以與核心對話 node提供四個定時器,讓任務可以在指定的時間執行 setTimeout() setInterval() setImmediate() process.nextTick(

嵌入式Linux核心I2C子系統

1.1 I2C匯流排知識 1.1.1  I2C匯流排物理拓撲結構      I2C匯流排在物理連線上非常簡單,分別由SDA(序列資料線)和SCL(序列時鐘線)及上拉電阻組成。 通訊原理是通過對SCL和SDA線高低電平時序的控制,來產生I2C匯流排協議所需

linux核心 RCU機制

 簡介         RCU(Read-Copy Update)是資料同步的一種方式,在當前的Linux核心中發揮著重要的作用。RCU主要針對的資料物件是連結串列,目的是提高遍歷讀取資料的效率,為了達到目的使用RCU機制讀取資料的時候不對連結串列進行耗時的加鎖操作。這樣在