linux內核模塊編程(二)----timer定時器

先給自己打個廣告,本人的微信公衆號正式上線了,搜索:張笑生的地盤,主要關注嵌入式軟件開發,足球等等,希望大家多多關注,有問題可以直接留言給我,一定盡心盡力回答大家的問題
在這裏插入圖片描述

一 why

一般地,在我們嵌入式軟件開發中,使用定時器的目的是爲了實現週期性地執行某項工作;同樣地,linux內核也實現了一種定時器,用於實現內核週期性執行某項工作。

二 what

linux內核實現的這種定時器,稱之爲動態定時器,是內核用來控制在未來某個時間點(基於jiffies)調度執行某個函數的一種機制,它的實現位於Linux/timer.h 和 kernel/timer.c 文件中。
被調度的函數是異步執行的,類似於一種“軟件中斷”,處於非進程的上下文中,所以這個被調度函數應該遵循如下規則

1. 沒有 current 指針,不允許訪問用戶空間。因爲沒有進程上下文,相關代碼和被中斷的進程沒有任何聯繫。
2. 不能執行休眠(或可能引起休眠的函數)和調度。
3. 任何被訪問的數據結構都應該針對併發訪問進行保護,以防止競爭條件。
4. 被調度函數運行過一次後就不會再被運行了(相當於自動註銷),但可以通過在被調度的函數中重新調度自己來週期運行。

2.1 內核定時器的數據結構

struct timer_list {
    struct list_head entry;  // 當一個定時器被註冊到內核之後,entry 字段用來連接該定時器到一個內核鏈表中
    unsigned long expires;   // 表示期望定時器執行的 jiffies 值,到達該 jiffies 值時,將調用 function 函數
    void (*function)(unsigned long);
    unsigned long data; 
    struct tvec_base *base; //內核內部實現所用的
    /* ... */
};

2.2 初始化
由於linux內核版本的不同,關於初始化部分的實現有不同的函數,低版本的用init_timer,高版本是timer_setup

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
    init_timer(&dev->getIntrTimer);
    dev->getIntrTimer.data = (unsigned long) dev;
    dev->getIntrTimer.function = GetIntrTimerCallback;
    /* ... */
    add_timer(&dev->getIntrTimer);
#else
    timer_setup(&dev->getIntrTimer, GetIntrTimerCallback, 0);
    /* the third argument may include TIMER_* flags */
    /* ... */
#endif

比如當前我的linux版本是4.15,所以在我的實驗中,使用的就是timer_setup
2.3 註冊
初始化定時器成功之後,我們需要註冊該定時器,函數爲

add_timer

2.4 修改
定時運行過程中的定時器週期修改函數爲

mod_timer

前面我們知道,當定時週期來到時,會執行被調函數,如果我們此時不修改定時器到期時間,則接下來就不會在執行被調函數了,我們可以在被調函數中修改定時器到期時間,實現週期性執行被調函數。

三 how

定時器內核編程示例

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/jiffies.h>


static struct timer_list test_timer;

static void irq_test_timer_function(struct timer_list  *timer)
{
	printk("22 again in irq_test_timer_function\n");
	mod_timer(&test_timer, jiffies + 2 * HZ);
}

static int timer_drv_init(void)
{
	test_timer.expires  = jiffies + 2 * HZ;
	timer_setup(&test_timer, irq_test_timer_function, 0);
	
	add_timer(&test_timer);
	
	printk(KERN_INFO "already init and add timer\n");
	
	return 0;
}

static void timer_drv_exit(void)
{
	printk(KERN_INFO "exit timer drv\n");
	del_timer(&test_timer);
}

module_init(timer_drv_init);
module_exit(timer_drv_exit);
MODULE_LICENSE("GPL");

Makefile文件

obj-m:=test_timer.o

KDIR:= /lib/modules/$(shell uname -r)/build
PWD:= $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean
	

四 test

1. 上傳到Ubuntu系統
2. 編譯 make
3. 加載 sudo insmod test_timer.ko
4. 查看運行情況 dmesg | tail
5. 卸載 sudo rmmod test_timer

加載時打印
在這裏插入圖片描述
卸載時打印
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章