延遲和定時器管理

文章目錄

  • 1 內核中時間概念
  • 2 標準定時器
    • jiffies和HZ
    • 定時器API
    • 標準定時器案例
  • 3 高精度定時器(HRT)
    • 高精度定時器案例
  • 4 內核中延遲和睡眠
    • 原子上下文
    • 非原子上下文

1 內核中時間概念

時間概念對計算機來說有些模糊,事實上內核必須在硬件的幫助下才能計算和管理時間。硬件為內核提供了一個系統定時器用于計算流失的時間。系統定時器以某種頻率自發觸發時鐘中斷,該頻率可以通過編程預定,稱為節拍率(系統定時器的頻率)。

因為節拍率對內核來說是可知的,所以內核知道連續兩次時鐘中斷的間隔時間,這個間隔時間就是節拍。內核就是靠這種已知的時鐘中斷間隔來計算實際時間(絕對時間)和系統運行時間。實際時間,就是某一天的日期和時間,內核通過控制時鐘中斷維護實際時間,另外內核也為用戶空間提供了一組系統調用以獲取實際日期和實際時間。系統運行時間:就是系統啟動開始所經過的時間,對用戶空間和內核都很用,因為許多程序都必須清楚流失的時間,通過兩次讀取運行時間再計算它們的差,就可以得到相對的流逝過的時間了。

系統定時器(內核定時器)是我們接下來討論的內容。

2 標準定時器

標準定時器是內核定時器,以jiffies為粒度運行

jiffies和HZ

jiffies是在<linux/jiffies.h>中聲明的內核時間單元。為了理解jiffies,需要引入一個新的常量HZ,注意,這是個常量,系統定時器頻率(節拍率)是通過靜態預處理定義的,也就是HZ(赫茲),在系統啟動時按照HZ值對硬件進行設置。它是jiffies在1s內增加的次數,HZ的大小取決于硬件和內核版本,決定了時鐘中斷觸發的頻率。

全局變量jiffies用來記錄自系統啟動以來產生的節拍總數。啟動時,內核將該變量初始化為0,此后,每次時鐘中斷處理程序都會增加該變量的值。因為一秒內時鐘中斷的次數等于HZ,所以jiffies一秒內增加的值也就為HZ。

jiffies變量總是無符號長整數(unsigned long),因此,在32位體系結構上是32位,在64位體系結構上是64位。32位的jiffies變量,可能會溢出,如果頻率為1000Hz,49.7天后就會溢出。而使用64位的jiffies,不會看到它溢出。為了解決這個問題,<linux/jiffies.h>引入和定義了另一個變量:

extern u64 jiffies_64;

32位系統上采用這種方式時,jiffies取整個64位jiffies_64變量的低32位。jiffies_64將指向高位。在64位平臺上,jiffies = jiffies_64

定時器API

定時器由結構timer_list表示,定義在文件linux/timer.h中:

struct timer_list {struct list_head entry;		/* 定時器鏈表的入口 */unsigned long expires;		/* 以jiffies為單位的定時值 */spinlock_t lock;			/* 保護定時器的鎖 */unsigned long magic;		void (*function)(unsigned long);	/* 定時器處理函數 */unsigned long data;					/* 傳給處理函數的長整型參數 */struct tvec_t_base_s *base;			/* 定時器內部值,用戶不要使用 */
};

定時器的使用很簡單,你只需要執行一些初始化工作,設置一個超時時間,指定超時發生后執行的函數,然后激活定時器就可以了。指定的函數將在定時器到期時自動執行,注意定時器并不周期運行,它在超時后就自動銷毀,這也是這種定時器被稱為動態定時器的一個原因,動態定時器不斷創建和銷毀,而且它的運行次數也不受限制。

  1. 設置定時器
    設置定時器,可以用setup_timer函數:
void setup_timer(struct timer_list *timer,void (*function)(unsigned long),unsigned long data)

也可以使用init_time函數:

init_timer(struct timer_list *timer);

setup_timer是對init_timer的包裝。

  1. 激活定時器
    函數add_timer( )根據參數struct timer_list變量的expires值將定時器插入到合適的動態定時器的鏈表中,并激活定時器。函數首先檢測定時器是否處于掛起狀態,如果掛起給出警告信息并退出,否則插入合適的定時器鏈表。
void add_timer(struct timer_list *timer)
  1. 設置過期時間
 int mod_timer(struct timer_list *timer, unsigned long expires);

函數mod_timer( )主要用于更改動態定時器的到期時間,從而可更改定時器的執行順序,相當于執行如下代碼序列:

del_timer(timer);
timer->expires=expires;
add_timer(timer);

mod_timer會修改過期時間并激活定時器

  1. 如果需要在定時器超時前停止定時器,可以使用del_timer()函數
int del_timer(struct timer_list * timer);
int del_timer_sync(struct timer_list * timer);

函數del_timer( )返回整數,可能的取值是0和1,對于活動定時器,返回1,對于不活動定時器返回0。del_timer_sync等待定時器處理函數執行完成函數。
當del_timer返回后,可以保證的只是:定時器不會再被激活,但是在多處理器機器上定時器可能已經在其他處理器上運行了,所以刪除定時器是需要等待可能在其他處理器上運行的定時器處理程序都退出,這時就要使用del_timer_sync()函數執行刪除工作。
應該在模塊清理例程中釋放定時器,可以單獨檢查定時器是否正在運行:

int timer_pending(struct timer_list *timer);

這個函數檢查是否有觸發的定時器回調函數掛起。

標準定時器案例

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>static struct timer_list my_timer;void my_timer_callback(unsigned long data)
{printk("%s callled (%ld).\n",__FUNCTION__,jiffies );
}static int __init my_init(void)
{int retval;printk("Timer module loaded\n");setup_timer(&my_timer,my_timer_callback,0);printk("Setup timer to fire in 300ms (%ld)\n",jiffies);retval = mod_timer(&my_timer,jiffies+msecs_to_jiffies(300));if(ret){printk("Timer firing failed\n");}return 0;
}static void my_exit(void)
{int retval;retval = del_timer(&my_timer);if(retval){printk("The timer is still in use..\n");}pr_info("Tiner module unloaded.\n");
}module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

3 高精度定時器(HRT)

標準定時器不夠準確,其準確度為毫秒,內核2.6.16引入了高精度定時器,其精度達到微秒(取決于平臺,最高可達納秒)。標準定時器區取決于HZ(因為它們依賴于jiffies),而HRT實現基于ktime。

在系統上使用HRT時,要確認內核和硬件支持它。換句話說,必須用于平臺相關的代碼來訪問硬件HRT。

需要頭文件:

#include <linux/hrttimer.h>

在內核中HRT表示為hrttimer的實例:

*** struct hrtimer - the basic hrtimer structure* @node:	timerqueue node, which also manages node.expires,*		the absolute expiry time in the hrtimers internal*		representation. The time is related to the clock on*		which the timer is based. Is setup by adding*		slack to the _softexpires value. For non range timers*		identical to _softexpires.* @_softexpires: the absolute earliest expiry time of the hrtimer.*		The time which was given as expiry time when the timer*		was armed.* @function:	timer expiry callback function* @base:	pointer to the timer base (per cpu and per clock)* @state:	state information (See bit values above)* @is_rel:	Set if the timer was armed relative** The hrtimer structure must be initialized by hrtimer_init()*/
struct hrtimer {struct timerqueue_node		node;ktime_t				_softexpires;enum hrtimer_restart		(*function)(struct hrtimer *);struct hrtimer_clock_base	*base;u8				state;u8				is_rel;
};

HRT的初始化的步驟如下:

  1. 初始化hrttimer。hrttimer初始化之前,需要設置ktime,它代表持續時間。hrttimer_init初始化高精度定時
extern void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,enum hrtimer_mode mode);
  1. 啟動hrttimer,使用hrttimer_start函數:
/*** hrtimer_start - (re)start an hrtimer* @timer:	the timer to be added* @tim:	expiry time* @mode:	timer mode: absolute (HRTIMER_MODE_ABS) or*		relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED);*		softirq based mode is considered for debug purpose only!*/
static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim,const enum hrtimer_mode mode)

mode代表到期模式,對于實際時間,它應該是HRTIMER_MODE_ABS,對于相對于現在的時間,是HRTIMER_MODE_REL

  1. 取消hrtimer。
extern int hrtimer_cancel(struct hrtimer *timer);
extern int hrtimer_try_to_cancel(struct hrtimer *timer);

如果定時器處于激活狀態或者回調函數正在運行,hrtimer_try_to_cancel會失敗,返回-1,hrtimer_cancel會等待回調函數完成。

為了防止定時器自動重啟,hrtimer回調函數必須返回HRTIMER_NORESTART

高精度定時器案例

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>#define MS_TO_NS(x) (x * 1E6L)static struct hrtimer hr_timer;enum hrtimer_restart my_hrtimer_callback( struct hrtimer *timer )
{pr_info( "my_hrtimer_callback called (%ld).\n", jiffies );return HRTIMER_NORESTART;
}static int hrt_init_module( void )
{ktime_t ktime;unsigned long delay_in_ms = 200L;pr_info("HR Timer module installing\n");/** ktime = ktime_set(0, 200 * 1000 * 1000);* 200 ms = 10 * 1000 * 1000 ns*/ktime = ktime_set( 0, MS_TO_NS(delay_in_ms) );hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );hr_timer.function = &my_hrtimer_callback;pr_info( "Starting timer to fire in %ldms (%ld)\n", \delay_in_ms, jiffies );hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );return 0;
}static void hrt_cleanup_module( void )
{int ret;ret = hrtimer_cancel( &hr_timer );if (ret)pr_info("The timer was still in use...\n");pr_info("HR Timer module uninstalling\n");return;
}module_init(hrt_init_module);
module_exit(hrt_cleanup_module);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Madieu <john.madieu@gmail.com>");
MODULE_DESCRIPTION("Standard timer example");

4 內核中延遲和睡眠

延遲有兩種類型,取決于代碼運行的上下文:原子的或非原子的。處理內核延遲要包含的頭文件是#include <linux/delay.h>

原子上下文

原子上下文中的任務(如ISR)不能進入睡眠狀態,無法進行調度。這就是原子上下文延遲必須使用循環-等待循環的原因。內核提供Xdelay系列函數,在繁忙循環中消耗足夠長的時間(基于jiffies),得到所需的延遲。

  • ndelay(unsigned long nsecs)
  • udelay(unsigned long usecs)
  • mdelay(unsigned long msecs)

應該時鐘使用udelay(),因為ndelay()的精度取決于硬件定時器的精度。不建議使用mdelay()。

非原子上下文

在非原子上下文中,內核提供sleep系列函數,使用那個函數取決于需要延遲多長時間。

  • udelay(unsigned long usecs):基于繁忙-等待循環。如果需要睡眠數微秒(小于等于10us左右),則應該使用該函數。
  • usleep_range(unsigned long min,unsigned long max):依賴于hrttimer,睡眠數微秒到數毫秒(10us-20ms)時建議使用它,避免使用udelay()的繁忙-等待循環
  • msleep(unsigned long msecs):由jiffies傳統定時器支持,對于數毫秒以上的長睡眠(10ms+)請使用該函數。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379495.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379495.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379495.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Web開發工具(插件)收集

1.IE Developer Toolbar 瀏覽和修改&#xff0c;選定Web頁上的特定元素&#xff0c;查看HTML對象的類名、ID&#xff0c;以及類似鏈接路徑、tab順序、快捷鍵等。 2.HttpWatch Professional 一款強大的網頁數據分析工具,可以查看當前網頁的http數據 FireFox插件 FireFox下插件實…

cin、cin.get()、cin.getline()、getline()、gets()等函數的用法

轉載&#xff0c;并經過本人補充cin、cin.get()、cin.getline()、getline()、gets()等函數的用法2007/10/27 22:51學C的時候&#xff0c;這幾個輸入函數弄的有點迷糊&#xff1b;這里做個小結&#xff0c;為了自己復習&#xff0c;也希望對后來者能有所幫助&#xff0c;如果有差…

Java StringBuilder subSequence()方法與示例

StringBuilder類subSequence()方法 (StringBuilder Class subSequence() method) subSequence() method is available in java.lang package. subSequence()方法在java.lang包中可用。 subSequence() method is used to return the new set of a character sequence that is a …

Linux設備驅動開發---設備樹的概念

文章目錄1 設備樹機制命名約定別名、標簽和phandleDT編譯器2 表示和尋址設備SPI和I2C尋址平臺設備尋址3 處理資源提取特定應用數據文本字符串單元格和無符號的32位整數布爾提取并分析子節點4 平臺驅動程序與DTOF匹配風格處理非設備樹平臺平臺數據與DT設備樹&#xff08;DT&…

【轉】C#中數組復制的4種方法

C#中數組復制的4種方法 from&#xff1a;http://blog.csdn.net/burningcpu/article/details/1434167今天旁邊的同事MM叫我調了一段程序&#xff0c;她想復制一個數組&#xff0c;int[] pins {9,3,4,9};int [] alias pins;這里出了錯誤&#xff0c;也是錯誤的根源&#xff0c…

Java StringBuilder codePointAt()方法與示例

StringBuilder類codePointAt()方法 (StringBuilder Class codePointAt() method) codePointAt() method is available in java.lang package. codePointAt()方法在java.lang包中可用。 codePointAt() method is used to return the Unicode code point at the given indices an…

用戶虛擬地址轉化成物理地址,物理地址轉換成內核虛擬地址,內核虛擬地址轉換成物理地址,虛擬地址和對應頁的關系

文章目錄1. 用戶虛擬地址轉換成物理地址2. 內核虛擬地址轉換成物理地址3. 物理地址轉換成內核虛擬地址4 內核虛擬地址和對應頁5 根據進程號獲取進程描述符1. 用戶虛擬地址轉換成物理地址 static void get_pgtable_macro(void) {printk("PAGE_OFFSET 0x%lx\n", PAGE…

簡單三層架構(登錄)

1&#xff0c;首先導包 dao //獲取數據String username request.getParameter("username");String password request.getParameter("password");//傳遞到Service層UserService service new UserService();//這里的UserService 需要創建到service包下Use…

通過隱藏option實現select的聯動效果

開始的時候需求是根據一定條件隱藏一部分<option>標簽&#xff0c;類似聯動效果&#xff0c;但是目前的html規范并沒有為<option>提供隱藏的效果&#xff0c;因此常用的設置display或者visibility無效。網上大部分解決方案是刪除<option>節點或<option>…

Java SimpleTimeZone setEndRule()方法與示例

SimpleTimeZone類setEndRule()方法 (SimpleTimeZone Class setEndRule() method) Syntax: 句法&#xff1a; public void setEndRule(int en_mm, int en_dd, int en_time);public void setEndRule(int en_mm, int en_dd, int en_dow, int en_time);public void setEndRule(int…

Linux設備驅動開發--- DMA

文章目錄1 設置DMA映射緩存一致性和DMADMA映射一致映射流式DMA映射2 完成的概念3 DMA引擎API分配DMA從通道設置從設備和控制器指定參數獲取事務描述符提交事務發布待處理DMA請求并等待回調通知4 程序單緩沖區映射分散聚集映射DMA是計算機系統的一項功能&#xff0c;它允許設備在…

類加載器

一、類加載器 1&#xff0c;什么是類加載器&#xff1f; 類加載器就是用來加載字節碼文件 2&#xff0c;類加載器的種類有哪些&#xff1f; 1&#xff09;BootStrap&#xff1a;引導類加載器&#xff1a;加載都是最基礎的文件 2&#xff09;ExtClassLoader&#xff1a;擴展類加…

一個用java讀取XML文件的簡單方法(轉)

XML文件 book.xml <book> <person> <first>Kiran</first> <last>Pai</last> <age>22</age> </person> <person> <first>Bill</first> <last>Gates</last> <age>46</age&g…

Java ObjectStreamField getName()方法與示例

ObjectStreamField類的getName()方法 (ObjectStreamField Class getName() method) getName() method is available in java.io package. getName()方法在java.io包中可用。 getName() method is used to get the name of this ObjectStreamField field. getName()方法用于獲取…

【css】CSS中折疊margin的問題

為什么要翻譯這篇說明&#xff1f;css2本有人已翻譯過&#xff0c;但看一下&#xff0c;很粗糙&#xff08;不是說自己就怎么怎么樣啊&#xff0c;翻譯者真的是很值得敬佩的&#xff01;&#xff09;&#xff0c;近來跟css與xhtml接觸得越來越多&#xff0c;但接觸得越多&#…

算法---鏈表

文章目錄反轉鏈表合并兩個有序鏈表刪除重復元素反轉鏈表 反轉鏈表包括兩種&#xff0c;反轉全部元素或者反轉部分元素。在這里&#xff0c;我們約定&#xff1a;數據元素類型是struct LinkNode&#xff0c;要反轉鏈表的第一個節點是head&#xff0c;head的前面一個節點是pre&a…

SSM

二、環境設置&#xff08;MyEclipse&#xff09; 1&#xff0c;字體設置 window–>Preference->General->Appearance->Colors and Fonts->Basic Text->Font 2&#xff0c;workspace字符集設置 window–>Preference->General->Appearance->W…

IOS NSArray,NSDictionary

小結&#xff1a; NSArray有序的集合&#xff1b; NSDictionary無序的集合&#xff0c;可排序&#xff1b; 增刪改查 ------NSArray----------- create : 1)NSArray *array [NSArray arrayWithObjects:"Henry","Jones", "Susan", "Smith&q…

Java PropertyPermission equals()方法與示例

PropertyPermission類equals()方法 (PropertyPermission Class equals() method) equals() method is available in java.util package. equals()方法在java.util包中可用。 equals() method is used to check whether this object and the given object (ob) are equal or not…

c#配合oracle快速導入excel方法--原創(6萬條記錄5分鐘左右)

原理&#xff1a;用c#采用讀取Excel數據源方式將數據讀入c#的datatable,循環datatable,將datatable中的數據用stringbuilder拼成insert into (字段名) valus (值);每5條插入一個符號&#xff08;作用是將sql字符串限制在4000字符以內&#xff09;&#xff0c;然后將拼成的字符串…