嵌入式Linux裝置驅動開發筆記(二)

2021-08-02 16:55:29 字數 3215 閱讀 3388

一、核心的時間

(1)tick(滴答)

核心採用了乙個新的時間單位來進行計時。該時間單位稱為tick(滴答),乙個tick對應硬體定時器兩次中斷之間的時間間隔。當前核心每秒鐘硬體定時器會發生hz次中斷。tick和秒的換算關係為: 1 tick = 1/hz秒。

hz是在核心make menuconfig(核心的.config檔案)時確定,如果要修改hz值,需要重新編譯核心。

(2)相對時間

核心從開機開始記錄乙個相對時間。核心利用全域性變數jiffies來記錄從開機到當前時間所經過的tick的總量。

jiffies++的工作是由硬體定時器的中斷處理函式完成的。jiffies的型別是unsigned long,在32位平台上最大值為4g。因此,當hz為1000時,大約49.7天會溢位一次。為了避免溢位,可以使用64位的變數jiffies_64,大約2000億天會溢位一次。

核心裡的很多延遲和定時,都是用jiffies進行判斷的。如果要訪問jiffies,應包含標頭檔案」linux/sched.h」。

(3)絕對時間

#include struct timeval tval;

struct timespec tspec;

呼叫核心的函式來獲得絕對時間

do_gettimeofday(&tval);

getnstimeofday(&tspec);

列印絕對時間

printk("%lds : %ldus\n", tval.tv_sec, tval.tv_usec);

printk("%lds : %ldns\n", tspec.tv_sec, tspec.tv_nsec);

二、核心的延遲和定時

延遲:當前程式停下來,等待某個條件滿足。延遲是不得已的,如果程式可以執行,就不應該延遲;

定時:啟動定時器,由核心(硬體定時器的中斷處理函式)在未來的某個時間為啟動定時器者完成某項工作。

(1)基於忙迴圈的延遲

如果是比較短時間的延遲,則可以通過在cpu上執行一段迴圈來延遲,如果要延遲較長的時間,則需要將當前程序置入睡眠來延遲。

#include ndelay(10); //延遲10ns

udelay(20); //延遲20us

mdelay(30); //延遲30ms

核心用for迴圈實現上述3個函式。其中udelay用的最多,一般用於暫存器間設定的間隔。延遲時間和for迴圈次數的轉換是乙個經驗值。核心在開機時會執行1s(或1個tick)的迴圈,然後記錄迴圈的次數。udealy等的時間就通過該次數進行轉換。這個次數記錄在/proc/cpuinfo檔案的變數bogomips中。

(2)基於等待佇列的延遲(基於睡眠)

a、程序狀態和執行佇列

程序的核心結構體為「linux/sched.h」中的task_struct,區分程序的3個核心狀態:

task_running

task_interruptible

task_uninterruptible

處於task_running狀態的程序會組織在乙個執行佇列中,2.6.23核心以後,通過cfs排程器(completely fair scheduler, 完全公平排程器)來排程,執行佇列中的程序用rb_tree組織在一起。

b、直接睡眠延時(基於等待佇列)

set_current_state(state);

state可以用task_interrupt或task_uninterruptible

schedule_timeout(delaytime);

delaytime以tick為單位,例如睡眠3s,則dalaytime可設定為3*hz。

c、等待佇列延時

每個要等待的條件都可以分配對應的等待佇列,每個佇列有乙個等待佇列頭(wait_queue_head_t),等待佇列定義在「linux/wait.h」標頭檔案中。

#include //宣告等待佇列頭

wait_queue_head_t mywait;

//佇列頭使用前要初始化

init_waitqueue_head(&mywait);

//在char驅動的write函式中,如果緩衝區滿則睡眠

ssize_t my_write(...)

//如果緩衝區非滿,則可寫

...}//在ioctl函式中,可以讓緩衝區復位,此時可以喚醒等待佇列中的睡眠程序

int my_ioctl(***)

(3)定時器

定時器的特徵:

a、啟動定時器的人和執行定時器的人不一樣

一般啟動定時器的常常是某個程序或者中斷,而核心中負責執行定時器的是硬體定時器的中斷處理函式

b、定時器的執行時間一定是未來

核心利用jiffies來確定定時器的執行時間

c、定時器對應的函式只執行一次

一般誰準備定時器,誰提供執行函式;執行函式的人是核心的硬體定時器中斷。如果希望實現乙個迴圈的定時器,則需要在執行函式中自行將定時器再重新啟動定時器。

d、定時器的函式在中斷上下文(context)執行

因此,執行函式中不能睡眠

定時器的核心結構體定義在標頭檔案「linux/timer.h」中,為timer_list。timer_list由啟動者準備,當啟動定時器後,timer_list會形成乙個鍊錶,由核心的硬體定時器中斷來檢查鍊錶,看有沒有定時器到時。

#include //宣告定時器

struct timer_list mytimer;

//定時器的執行函式

//當定時器到期後,由硬體定時器中斷執行一次

static void

my_timer_func(unsigned long data)

//初始化定時器

setup_timer(&mytimer, my_timer_func, data);

初始化定時器時傳入的引數為timer_list的指標;執行函式;傳給執行函式的引數

//啟動定時器

mod_timer(&mytimer, jiffies+hz);

//定時器一旦啟動,就會加入乙個timer_list的鍊錶,一旦到時,就會被執行。

//啟動定時器的人和執行的人不是乙個。即使啟動者退出,定時器仍然執行。

//刪除定時器

del_timer(&mytimer);

如果模組要rmmod,在解除安裝之前,必須刪除所有沒執行的定時器。

嵌入式Linux裝置驅動開發筆記(一)

一 linux裝置的分類 字元裝置 塊裝置 網路裝置,三種裝置之間的區別是資料的互動模式,分別為 位元組流 資料塊 資料報。二 vfs核心結構體 vfs核心結構體定義在 linux fs.h 標頭檔案中。1 struct inode結構體 記錄檔案的屬主 訪問時間等資訊。當第一次開啟檔案的時候由vf...

嵌入式Linux裝置驅動開發 1

使用者程序是通過裝置檔案來與實際的硬體打交道。每個裝置檔案都有其檔案屬性 c b 如表示是字元裝置還是塊裝置。另外每個檔案都有兩個裝置號,第乙個是主裝置號,用於標識驅動程式 第二個是從裝置號,用於標識使用同一裝置驅動的不同的硬體裝置。裝置檔案的主裝置號必須與裝置驅動程式在登記時申請的主裝置號一致,否...

嵌入式linux字元裝置驅動

arm linux 驅動 抵岸科技 1.我們需要先呼叫register chrdev region 或 alloc chrdev region 來向系統申請裝置號 int register chrdev region dev t first,unsigned int count,char name ...