国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

嵌入式培訓機構(gòu)四個月實訓課程筆記(完整版)-Linux ARM驅(qū)動編程第六天-ARM Linux編程之使用jiffies計數(shù)器 (物聯(lián)技術(shù)666)

這篇具有很好參考價值的文章主要介紹了嵌入式培訓機構(gòu)四個月實訓課程筆記(完整版)-Linux ARM驅(qū)動編程第六天-ARM Linux編程之使用jiffies計數(shù)器 (物聯(lián)技術(shù)666)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

鏈接:https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd=1688
提取碼:1688

使用jiffies計數(shù)器

包含在<linux/jiffies.h>中,但是通常只需使用<linux/sched.h>,前者會自動包含

jiffies與jiffies_64均應被看做只讀變量

jiffies變量應被聲明為volatile

使用舉例:

#include<linux/jiffies.h>

unsigned long j,stamp_1,stamp_half,stamp_n;

j=jiffies;? //read the current value

stamp_1=j+HZ;? //1second in the future

stamp_half=j+HZ/2;? //0.5second in the future

stamp_n=j+n*HZ/1000;? // n milliseconds

比較緩存值(例如上述的stamp_1)與當前值:

#include<linux/jiffies.h>

int time_after(unsigned long a,unsigned long b);

int time_before(unsigned long a,unsigned long b);

int time_after_eq(unsigned long a,unsigned long b);

int time _before_eq(unsigned long a,unsigned long b);

上述幾個宏會將計數(shù)器值轉(zhuǎn)換為signed long,相減,然后比較結(jié)果。如果需要以安全的方式計算兩個jiffies實例之間的差,如下:

diff = (long) t2 - (long) t1;

而通過下面的方法,可將兩個jiffies的差轉(zhuǎn)換為毫秒值:

msec = diff *1000/HZ;

用戶空間和內(nèi)核空間的時間表述方法的轉(zhuǎn)換:

用戶空間方法:timeval,timespec

內(nèi)核空間方法:jiffies

#include<linux/time.h>

struct timespec {

??? time_t??? tv_sec;??????? /* seconds */

??? long??? tv_nsec;??? /* nanoseconds */

};

struct timeval {

??? time_t??????? tv_sec;??????? /* seconds */

??? SUSEconds_t??? tv_usec;??? /* microseconds */

};

unsigned long timespec_to_jiffies(struct timespec *value);

void jiffies_to_timespec(unsigned long jiffies,struct timespec *value);

unsigned long timeval_to_jiffies(struct timeval *value);

void jiffies_to _timeval(unsigned long jiffies,struct timeval *value);

讀取64為jiffies:jiffies_64

#include<linux/jiffies.h>

u64 get_jiffies_64(void);

處理器特定的寄存器

如果需要精度很高的計時,jiffies已不可滿足需要,這時就引入了一種技術(shù)就是CPU包含一個隨時鐘周期不斷遞增的計數(shù)寄存器。這是完成高分辨率計時任務的唯一可靠途徑。

1.不管該寄存器是否置0,我們都強烈建議不要重置它。

2.TSC:這是一個64位寄存器,記錄CPU的時鐘周期數(shù),從內(nèi)核空間和用戶空間都可以讀取它。

<asm/msr.h>

以下宏是與體系結(jié)構(gòu)相關(guān)的,上述頭文件是x86專用頭文件

rdtsc(low32,high32);

rdtscl(low32);

rdtscll(var64);

第一個宏原子性的把64位變量讀到兩個32位的變量中。

第二個讀取低32位,廢棄高32位。

第三個把64值讀到一個long long型變量中。

舉例:

下面代碼完成測量指令自身運行時間

unsigned long ini,end;

rdtscl(ini);

rdtscl(end);

printk("time lapse:%li\n",end-ini);

現(xiàn)提供一個與體系結(jié)構(gòu)無關(guān)的函數(shù),可以替代rdtsc

<linux/timex.h>

cycles_t get_cycles(void);

在各種平臺上都可以使用這個函數(shù),在沒有時鐘周期計數(shù)寄存器的平臺上它總是返回0。cycles_t類型是能裝入讀取值的合適的無符號類型。

獲取當前時間

jiffies用來測量時間間隔

墻鐘時間-->jiffies時間:

#include<linux/time.h>

unsigned long mktime(unsigned int year,unsigned int month,

??????????????????????????????????????? unsigned int day, unsigned int? hour,

??????????????????????????????????????? unsigned int? minute,unsigned int second);

為了處理絕對時間, <linux/time.h> 導出了 do_gettimeofday 函數(shù),它填充一個指向 struct timeval 的指針變量。絕對時間也可來自 xtime 變量,一個 struct timespec 值,為了原子地訪問它,內(nèi)核提供了函數(shù) current_kernel_time。它們的精確度由硬件決定,原型是:

#include<linux/time.h>

void do_gettimeofday(structtimeval*tv);

struct timespec current_kernel_time(void);

/*得到的數(shù)據(jù)都表示當前時間距UNIX時間基準1970-01-01 00:00:00的相對時間*/

以上兩個函數(shù)在ARM平臺都是通過 xtime 變量得到數(shù)據(jù)的。

全局變量xtime:它是一個timeval結(jié)構(gòu)類型的變量,用來表示當前時間距UNIX時間基準1970-01-01 00:00:00的相對秒數(shù)值。

結(jié)構(gòu)timeval是Linux內(nèi)核表示時間的一種格式(Linux內(nèi)核對時間的表示有多種格式,每種格式都有不同的時間精度),其時間精度是微秒。該結(jié)構(gòu)是內(nèi)核表示時間時最常用的一種格式,它定義在頭文件include/linux/time.h中,如下所示:

struct timeval {

time_t tv_sec; /* seconds */

SUSEconds_t tv_usec; /* microseconds */

};

其中,成員tv_sec表示當前時間距UNIX時間基準的秒數(shù)值,而成員tv_usec則表示一秒之內(nèi)的微秒值,且1000000>tv_usec>=0。

Linux內(nèi)核通過timeval結(jié)構(gòu)類型的全局變量xtime來維持當前時間,該變量定義在kernel/timer.c文件中,如下所示:

/* The current time */

volatile struct timeval xtime __attribute__ ((aligned (16)));

但是,全局變量xtime所維持的當前時間通常是供用戶來檢索和設(shè)置的,而其他內(nèi)核模塊通常很少使用它(其他內(nèi)核模塊用得最多的是jiffies),因此對xtime的更新并不是一項緊迫的任務,所以這一工作通常被延遲到時鐘中斷的底半部(bottom half)中來進行。由于bottom half的執(zhí)行時間帶有不確定性,因此為了記住內(nèi)核上一次更新xtime是什么時候,Linux內(nèi)核定義了一個類似于jiffies的全局變量wall_jiffies,來保存內(nèi)核上一次更新xtime時的jiffies值。時鐘中斷的底半部分每一次更新xtime的時侯都會將wall_jiffies更新為當時的jiffies值。全局變量wall_jiffies定義在kernel/timer.c文件中:

/* jiffies at the most recent update of wall time */

unsigned long wall_jiffies;

延遲

長延遲

忙等待

若想延遲執(zhí)行若干個時鐘嘀噠,精度要求不高。最容易的( 盡管不推薦 ) 實現(xiàn)是一個監(jiān)視 jiffy 計數(shù)器的循環(huán)。這種忙等待實現(xiàn)的代碼如下:

while(time_before(jiffies, j1))

??? cpu_relax();

對 cpu_relex 的調(diào)用將以體系相關(guān)的方式執(zhí)行,在許多系統(tǒng)中它根本不做任何事,這個方法應當明確地避免。對于ARM體系來說:

#define cpu_relax()??????????? barrier()

也就是說在ARM上運行忙等待相當于:

while(time_before(jiffies, j1)) ;

這種忙等待嚴重地降低了系統(tǒng)性能。如果未配置內(nèi)核為搶占式, 這個循環(huán)在延時期間完全鎖住了處理器,計算機直到時間 j1 到時會完全死掉。如果運行一個可搶占的內(nèi)核時會改善一點,但是忙等待在可搶占系統(tǒng)中仍然是浪費資源的。更糟的是, 當進入循環(huán)時如果中斷碰巧被禁止, jiffies 將不會被更新, 并且 while 條件永遠保持真,運行一個搶占的內(nèi)核也不會有幫助, 唯一的解決方法是重啟。

讓出處理器

忙等待加重了系統(tǒng)負載,必須找出一個更好的技術(shù):不需要CPU時釋放CPU 。 這可通過調(diào)用schedule函數(shù)實現(xiàn)(在 <linux/sched.h> 中聲明):

while(time_before(jiffies, j1)){

??? schedule();

}

在計算機空閑時運行空閑任務(進程號 0, 由于歷史原因也稱為swapper)可減輕處理器工作負載、降低溫度、增加壽命。

超時

實現(xiàn)延遲的最好方法應該是讓內(nèi)核為我們完成相應的工作。

(1)若驅(qū)動使用一個等待隊列來等待某些其他事件,并想確保它在一個特定時間段內(nèi)運行,可使用:

#include<linux/wait.h>

long wait_event_timeout(wait_queue_head_t q, condition,long timeout);

long wait_event_interruptible_timeout(wait_queue_head_t q, condition,long timeout);

/*這些函數(shù)在給定隊列上睡眠, 但是它們在超時(以 jiffies 表示)到后返回。如果超時,函數(shù)返回 0; 如果這個進程被其他事件喚醒,則返回以 jiffies 表示的剩余的延遲實現(xiàn);返回值從不會是負值*/

(2)為了實現(xiàn)進程在超時到期時被喚醒而又不等待特定事件(避免聲明和使用一個多余的等待隊列頭),內(nèi)核提供了 schedule_timeout 函數(shù):

#include<linux/sched.h>

signed long schedule_timeout(signedlong timeout);

/*timeout 是要延時的 jiffies 數(shù)。除非這個函數(shù)在給定的 timeout 流失前返回,否則返回值是 0 。schedule_timeout 要求調(diào)用者首先設(shè)置當前的進程狀態(tài)。為獲得一個不可中斷的延遲, 可使用 TASK_UNINTERRUPTIBLE 代替。如果你忘記改變當前進程的狀態(tài), 調(diào)用 schedule_time 如同調(diào)用 shcedule,建立一個不用的定時器。一個典型調(diào)用如下:*/

set_current_state(TASK_INTERRUPTIBLE);

schedule_timeout (delay);

短延遲

當一個設(shè)備驅(qū)動需要處理硬件的延遲(latency潛伏期), 涉及到的延時通常最多幾個毫秒,在這個情況下, 不應依靠時鐘嘀噠,而是內(nèi)核函數(shù) ndelay, udelay和 mdelay ,他們分別延后執(zhí)行指定的納秒數(shù), 微秒數(shù)或者毫秒數(shù),定義在 <asm/delay.h>,原型如下:

#include<linux/delay.h>

void ndelay(unsignedlong nsecs);

void udelay(unsignedlong usecs);

void mdelay(unsignedlong msecs);

重要的是記住這 3 個延時函數(shù)是忙等待; 其他任務在時間流失時不能運行。每個體系都實現(xiàn) udelay, 但是其他的函數(shù)可能未定義; 如果它們沒有定義, <linux/delay.h> 提供一個缺省的基于 udelay 的版本。在所有的情況中, 獲得的延時至少是要求的值, 但可能更多。udelay 的實現(xiàn)使用一個軟件循環(huán), 它基于在啟動時計算的處理器速度和使用整數(shù)變量 loos_per_jiffy確定循環(huán)次數(shù)。

為避免在循環(huán)計算中整數(shù)溢出, 傳遞給udelay 和 ndelay的值有一個上限,如果你的模塊無法加載和顯示一個未解決的符號:__bad_udelay, 這意味著你調(diào)用 udleay時使用太大的參數(shù)。

作為一個通用的規(guī)則:若試圖延時幾千納秒, 應使用 udelay 而不是 ndelay; 類似地, 毫秒規(guī)模的延時應當使用 mdelay 完成而不是一個更細粒度的函數(shù)。

有另一個方法獲得毫秒(和更長)延時而不用涉及到忙等待的方法是使用以下函數(shù)(在<linux/delay.h> 中聲明):

void msleep(unsignedint millisecs);

unsigned long msleep_interruptible(unsignedint millisecs);

void ssleep(unsignedint seconds)

若能夠容忍比請求的更長的延時,應使用 schedule_timeout, msleep 或 ssleep。

內(nèi)核定時器

當需要調(diào)度一個以后發(fā)生的動作, 而在到達該時間點時不阻塞當前進程, 則可使用內(nèi)核定時器。內(nèi)核定時器用來調(diào)度一個函數(shù)在將來一個特定的時間(基于時鐘嘀噠)執(zhí)行,從而可完成各類任務。

內(nèi)核定時器是一個數(shù)據(jù)結(jié)構(gòu), 它告訴內(nèi)核在一個用戶定義的時間點使用用戶定義的參數(shù)執(zhí)行一個用戶定義的函數(shù),函數(shù)位于 <linux/timer.h> 和 kernel/timer.c 。被調(diào)度運行的函數(shù)幾乎確定不會在注冊它們的進程在運行時運行,而是異步運行。實際上, 內(nèi)核定時器通常被作為一個"軟件中斷"的結(jié)果而實現(xiàn)。當在進程上下文之外(即在中斷上下文)中運行程序時, 必須遵守下列規(guī)則:

(1)不允許訪問用戶空間;

(2)current 指針在原子態(tài)沒有意義;

(3)不能進行睡眠或者調(diào)度. 例如:調(diào)用 kmalloc(..., GFP_KERNEL) 是非法的,信號量也不能使用因為它們可能睡眠。

通過調(diào)用函數(shù) in_interrupt()能夠告知是否它在中斷上下文中運行,無需參數(shù)并如果處理器當前在中斷上下文運行就返回非零。

通過調(diào)用函數(shù) in_atomic()能夠告知調(diào)度是否被禁止,若調(diào)度被禁止返回非零; 調(diào)度被禁止包含硬件和軟件中斷上下文以及任何持有自旋鎖的時候。

在后一種情況, current 可能是有效的,但是訪問用戶空間是被禁止的,因為它能導致調(diào)度發(fā)生. 當使用 in_interrupt()時,都應考慮是否真正該使用的是 in_atomic 。他們都在 <asm/hardirq.h> 中聲明。

內(nèi)核定時器的另一個重要特性是任務可以注冊它本身在后面時間重新運行,因為每個 timer_list 結(jié)構(gòu)都會在運行前從激活的定時器鏈表中去連接,因此能夠立即鏈入其他的鏈表。一個重新注冊它自己的定時器一直運行在同一個 CPU.

即便在一個單處理器系統(tǒng),定時器是一個潛在的態(tài)源,這是異步運行直接結(jié)果。因此任何被定時器函數(shù)訪問的數(shù)據(jù)結(jié)構(gòu)應當通過原子類型或自旋鎖被保護,避免并發(fā)訪問。

定時器 API

內(nèi)核提供給驅(qū)動許多函數(shù)來聲明、注冊以及刪除內(nèi)核定時器:

#include <linux/timer.h>

struct timer_list {

??? struct list_head entry;

??? unsigned long expires;/*期望定時器運行的絕對 jiffies 值,不是一個 jiffies_64 值,因為定時器不被期望在將來很久到時*/

??? void (*function)(unsigned long); /*期望調(diào)用的函數(shù)*/

??? unsigned long data;/*傳遞給函數(shù)的參數(shù),若需要在參數(shù)中傳遞多個數(shù)據(jù)項,可以將它們捆綁成單個數(shù)據(jù)結(jié)構(gòu)并且將它的指針強制轉(zhuǎn)換為 unsiged long 的指針傳入。這種做法在所有支持的體系上都是安全的并且在內(nèi)存管理中相當普遍*/

??? struct tvec_t_base_s *base;

#ifdef CONFIG_TIMER_STATS

??? void *start_site;

??? char start_comm[16];

??? int start_pid;

#endif

};

/*這個結(jié)構(gòu)必須在使用前初始化,以保證所有的成員被正確建立(包括那些對調(diào)用者不透明的初始化):*/

void init_timer(struct timer_list *timer);

struct timer_list TIMER_INITIALIZER(_function, _expires, _data);

/*在初始化后和調(diào)用 add_timer 前,可以改變 3 個公共成員:expires、function和data*/

void add_timer(struct timer_list * timer);

int del_timer(struct timer_list * timer);/*在到時前禁止一個已注冊的定時器*/

int del_timer_sync(struct timer_list *timer); /*如同 del_timer ,但還保證當它返回時, 定時器函數(shù)不在任何 CPU 上運行,以避免在 SMP 系統(tǒng)上競態(tài), 并且在 單處理器內(nèi)核中和 del_timer 相同。這個函數(shù)應當在大部分情況下優(yōu)先考慮。 如果它被從非原子上下文調(diào)用, 這個函數(shù)可能睡眠,但是在其他情況下會忙等待。當持有鎖時要小心調(diào)用 del_timer_sync ,如果這個定時器函數(shù)試圖獲得同一個鎖, 系統(tǒng)會死鎖。如果定時器函數(shù)重新注冊自己, 調(diào)用者必須首先確保這個重新注冊不會發(fā)生; 這通常通過設(shè)置一個" 關(guān)閉 "標志來實現(xiàn), 這個標志被定時器函數(shù)檢查*/

int mod_timer(struct timer_list *timer, unsigned long expires); /*更新一個定時器的超時時間, 常用于超時定時器。也可在正常使用 add_timer時在不活動的定時器上調(diào)用mod_timer*/

int timer_pending(const struct timer_list * timer); /*通過調(diào)用timer_list結(jié)構(gòu)中一個不可見的成員,返回定時器是否在被調(diào)度運行*/

內(nèi)核定時器的實現(xiàn)《LDD3》介紹的比較籠統(tǒng),以后看《ULK3》的時候再細細研究。

?一個內(nèi)核定時器還遠未完善,因為它受到 jitter 、硬件中斷,還有其他定時器和其他異步任務的影響。雖然一個簡單數(shù)字 I/O關(guān)聯(lián)的定時器對簡單任務是足夠的,但不合適在工業(yè)環(huán)境中的生產(chǎn)系統(tǒng),對于這樣的任務,你將最可能需要實時內(nèi)核擴展(RT-Linux).文章來源地址http://www.zghlxwxcb.cn/news/detail-832285.html

到了這里,關(guān)于嵌入式培訓機構(gòu)四個月實訓課程筆記(完整版)-Linux ARM驅(qū)動編程第六天-ARM Linux編程之使用jiffies計數(shù)器 (物聯(lián)技術(shù)666)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包