新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > linux驅動之內核定時器驅動設計

        linux驅動之內核定時器驅動設計

        作者: 時間:2016-12-01 來源:網絡 收藏
        我的環境:
        Fedora 14 內核版本為2.6.38.1
        開發板:ARM9 TQ2440
        移植內核版本:linux-2.6.30.4
        定時器在linux內核中主要是采用一個結構體實現的。但是需要注意定時器是一個只運行一次的對象,也就是當一個定時器結束以后,還需要重現添加定時器。但是可以采用mod_timer()函數動態的改變定時器到達時間。
        這個驅動主要實現內核定時器的基本操作。內核定時器主要是是通過下面的結構體struct timer_list實現。需要的頭文件包括#include,但是在實際開發過程中不需要包含該頭文件,因為在sched.h中包含了該頭文件。
        struct timer_list {
        struct list_head entry;
        unsigned long expires;
        void (*function)(unsigned long);
        unsigned long data;
        struct tvec_base *base;
        #ifdef CONFIG_TIMER_STATS
        void *start_site;
        char start_comm[16];
        int start_pid;
        #endif
        #ifdef CONFIG_LOCKDEP
        struct lockdep_map lockdep_map;
        #endif
        };
        定時器的實現主要是該結構體的填充和部分函數的配合即可完成。其中紅色的部分是最主要的幾個元素,1、expires主要是用來定義定時器到期的時間,通常采用jiffies這個全局變量和HZ這個全局變量配合設置該元素的值。比如expires = jiffies + n*HZ,其中jiffies是自啟動以來的滴答數,HZ是一秒種的滴答數。
        2、function可以知道是一個函數指針,該函數就是定時器的處理函數,類似我們在中斷中的中斷函數,其實定時器和中斷有很大的相似性。定時器處理函數是自己定義的函數。
        3、data通常是實現參數的傳遞,從function的參數類型可以知道,data可以作為定時器處理函數的參數。
        其他的元素可以通過內核的函數來初始化。
        初始化函數為:
        init_timer(struct timer_list * timer);
        或者直接DEFINE_TIMER宏實現定義和初始化操作。
        #define DEFINE_TIMER(_name, _function, _expires, _data)
        struct timer_list _name =
        TIMER_INITIALIZER(_function, _expires, _data)
        添加定時器到內核的函數:
        void add_timer(struct timer_list *timer)
        {
        BUG_ON(timer_pending(timer));
        mod_timer(timer, timer->expires);
        }
        刪除定時器函數,如果定時器的定時時間還沒有到達,那么才可以刪除定時器:
        int del_timer(struct timer_list *timer)
        修改定時器的到達時間,該函數的特點是,不管定時器是否到達時間,都會重現添加一個定時器到內核。所以可以在定時處理函數中可以調用該函數修改需要重新定義的到達時間。
        int mode_timer(struct timer_list *timer,unsigned long expires)
        int mod_timer(struct timer_list *timer, unsigned long expires)
        {
        /*
        * This is a common optimization triggered by the
        * networking code - if the timer is re-modified
        * to be the same thing then just return:
        */
        if (timer->expires == expires && timer_pending(timer))
        return 1;
        /*注意調用的條件,也就是說明當前的定時器為鏈表的最后一個*/
        return __mod_timer(timer, expires, false);
        }
        static inline int
        __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
        {
        struct tvec_base *base, *new_base;
        unsigned long flags;
        int ret;
        ret = 0;
        timer_stats_timer_set_start_info(timer);
        BUG_ON(!timer->function);
        base = lock_timer_base(timer, &flags);
        if (timer_pending(timer)) {
        detach_timer(timer, 0);
        ret = 1;
        } else {
        if (pending_only)
        goto out_unlock;
        }
        debug_timer_activate(timer);
        new_base = __get_cpu_var(tvec_bases);
        if (base != new_base) {
        /*
        * We are trying to schedule the timer on the local CPU.
        * However we cant change timers base while it is running,
        * otherwise del_timer_sync() cant detect that the timers
        * handler yet has not finished. This also guarantees that
        * the timer is serialized wrt itself.
        */
        if (likely(base->running_timer != timer)) {
        /* See the comment in lock_timer_base() */
        timer_set_base(timer, NULL);
        spin_unlock(&base->lock);
        base = new_base;
        spin_lock(&base->lock);
        timer_set_base(timer, base);
        }
        }
        timer->expires = expires;
        internal_add_timer(base, timer);
        out_unlock:
        spin_unlock_irqrestore(&base->lock, flags);
        return ret;
        }
        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;
        struct list_head *vec;
        if (idx < TVR_SIZE) {
        int i = expires & TVR_MASK;
        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 0xffffffff on 64-bit
        * architectures then we use the maximum timeout:
        */
        if (idx > 0xffffffffUL) {
        idx = 0xffffffffUL;
        expires = idx + base->timer_jiffies;
        }
        i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
        vec = base->tv5.vec + i;
        }
        /*
        * Timers are FIFO:
        */
        /*添加到鏈表的最后,這說明mod_timer實現了重新注冊一個定時器的操作*/
        list_add_tail(&timer->entry, vec);
        }
        從上面的分析可以看出,mod_timer的實現過程比較復雜,但是基本上說明了mod_timer函數重新注冊定時器的操作過程。
        一般而言定時器的基本操作主要是上面的幾個函數。
        我的基于內核定時器的驅動函數如下,參考了宋寶華的Linux設備驅動開發詳解(第二版)。
        上一頁 1 2 下一頁

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 化隆| 利辛县| 娱乐| 仁布县| 南木林县| 鹿邑县| 嘉义县| 海丰县| 独山县| 龙南县| 丰顺县| 饶河县| 临邑县| 连南| 威宁| 临漳县| 定边县| 柳州市| 老河口市| 明光市| 嘉定区| 马边| 天峻县| 凤冈县| 泰州市| 朔州市| 格尔木市| 将乐县| 扎鲁特旗| 雅安市| 衡山县| 陵水| 鱼台县| 历史| 从化市| 朝阳区| 鄂托克前旗| 桐梓县| 留坝县| 澄江县| 盐池县|