姓名:尤樂航? ? ? ? ? ? 學(xué)號(hào):19029100006 ? 學(xué)院:電子工程學(xué)院
【嵌牛導(dǎo)讀】Linux定時(shí)器
【嵌牛鼻子】嵌入式? Linux系統(tǒng)
【嵌牛提問】Linux定時(shí)器
【嵌牛正文】
6.1定時(shí)器類型
6.1.1sleep() 在不同的Gcc版本編譯器,使用的頭文件可能略有不同,如果不清楚,查一下資料即可。它主要是讓當(dāng)前線程睡眠指定的秒數(shù),注意是second,秒。其函數(shù)原型是unsigned int sleep(unsigned int seconds);其返回值代表被打斷定時(shí)后,剩余的定時(shí)器時(shí)間,和下面的定時(shí)器有相同的效果。它的缺點(diǎn)是精度不太高,而且容易受信號(hào)機(jī)制影響,太多定時(shí)器有可能導(dǎo)致進(jìn)程的整體掛起
6.1.2usleep()
這個(gè)函數(shù)精度比較高,但是基本各個(gè)平臺(tái)都已經(jīng)不再推薦使用。它是非線程安全的。雖然精度高,但精度卻不怎么靠譜。其函數(shù)原型如下:int usleep(int micro_seconds);usleep功能把進(jìn)程掛起一段時(shí)間, 單位是微秒(百萬分之一秒)。如果返回0表示成功,否則表示出現(xiàn)了錯(cuò)誤,可以查看ERRORS的信息來確定錯(cuò)誤原因。
6.1.3nanosleep()
這個(gè)函數(shù)是內(nèi)核從2.0時(shí)才提供的一個(gè)函數(shù),這個(gè)可以推薦使用替代usleep()這個(gè)函數(shù),它的精度更高到了納秒(1秒= 1000^3納秒)。它可以使當(dāng)前進(jìn)程暫停到指定時(shí)間后恢復(fù)執(zhí)行。調(diào)用nanosleep將導(dǎo)致進(jìn)程進(jìn)入TASK_INTERRUPTIBLE,從而使信號(hào)進(jìn)入TASK_RUNNING狀態(tài)。所以一定要小心,這極有可能在沒有到達(dá)指定時(shí)間前而被其它信號(hào)喚醒。可以通過判斷返回值來判斷是否出現(xiàn)這種情況(返回-1),在這種情況下,可以在rem中查詢剩余的時(shí)間。它的函數(shù)原型是:
int nanosleep(const struct timespec * req, struct timespec * rem);
struct timespec
{
????time_t ?tv_sec; ????????// seconds
????long ???tv_nsec; ???????// nanoseconds
};
6.2普通定時(shí)器
在Linux2.6.4之前的版本上沒有高精度定時(shí)器的情況下,低精度的定時(shí)器就成了首選,但是這種低精度的定時(shí)器他有個(gè)缺點(diǎn),他的最高精度只能達(dá)到毫秒級(jí)別,大概是10ms左右。這種內(nèi)核定時(shí)器是內(nèi)核用來控制在未來某個(gè)時(shí)間點(diǎn)(基于jiffies)調(diào)度執(zhí)行某個(gè)函數(shù)的一種機(jī)制,我們可以在他的回調(diào)函數(shù)中實(shí)現(xiàn)一些自己的功能。
struct timer_list {
?????struct list_head entry;
?????unsigned long expires;
?????void (*function)(unsigned long);
?????unsigned long data;
?????struct tvec_base *base;
};
6.3高精度定時(shí)器
而隨著內(nèi)核的不斷演進(jìn),大牛們已經(jīng)對(duì)這種低分辨率定時(shí)器的精度不再滿足,而且,硬件也在不斷地發(fā)展,系統(tǒng)中的定時(shí)器硬件的精度也越來越高,這也給高分辨率定時(shí)器的出現(xiàn)創(chuàng)造了條件。內(nèi)核從2.6.16開始加入了高精度定時(shí)器架構(gòu)。為此,內(nèi)核為高精度定時(shí)器重新設(shè)計(jì)了一套軟件架構(gòu),它可以為我們提供納秒級(jí)的定時(shí)精度,以滿足對(duì)精確時(shí)間有迫切需求的應(yīng)用程序或內(nèi)核驅(qū)動(dòng),例如多媒體應(yīng)用,音頻設(shè)備的驅(qū)動(dòng)程序等等。如用hrtimer(high resolution timer)表示高精度定時(shí)器。
6.1定時(shí)器類型
6.1.1sleep() 在不同的Gcc版本編譯器,使用的頭文件可能略有不同,如果不清楚,查一下資料即可。它主要是讓當(dāng)前線程睡眠指定的秒數(shù),注意是second,秒。其函數(shù)原型是unsigned int sleep(unsigned int seconds);其返回值代表被打斷定時(shí)后,剩余的定時(shí)器時(shí)間,和下面的定時(shí)器有相同的效果。它的缺點(diǎn)是精度不太高,而且容易受信號(hào)機(jī)制影響,太多定時(shí)器有可能導(dǎo)致進(jìn)程的整體掛起
6.1.2usleep()
這個(gè)函數(shù)精度比較高,但是基本各個(gè)平臺(tái)都已經(jīng)不再推薦使用。它是非線程安全的。雖然精度高,但精度卻不怎么靠譜。其函數(shù)原型如下:int usleep(int micro_seconds);usleep功能把進(jìn)程掛起一段時(shí)間, 單位是微秒(百萬分之一秒)。如果返回0表示成功,否則表示出現(xiàn)了錯(cuò)誤,可以查看ERRORS的信息來確定錯(cuò)誤原因。
6.1.3nanosleep()
這個(gè)函數(shù)是內(nèi)核從2.0時(shí)才提供的一個(gè)函數(shù),這個(gè)可以推薦使用替代usleep()這個(gè)函數(shù),它的精度更高到了納秒(1秒= 1000^3納秒)。它可以使當(dāng)前進(jìn)程暫停到指定時(shí)間后恢復(fù)執(zhí)行。調(diào)用nanosleep將導(dǎo)致進(jìn)程進(jìn)入TASK_INTERRUPTIBLE,從而使信號(hào)進(jìn)入TASK_RUNNING狀態(tài)。所以一定要小心,這極有可能在沒有到達(dá)指定時(shí)間前而被其它信號(hào)喚醒。可以通過判斷返回值來判斷是否出現(xiàn)這種情況(返回-1),在這種情況下,可以在rem中查詢剩余的時(shí)間。它的函數(shù)原型是:
int nanosleep(const struct timespec * req, struct timespec * rem);
struct timespec
{
????time_t ?tv_sec; ????????// seconds
????long ???tv_nsec; ???????// nanoseconds
};
6.2普通定時(shí)器
在Linux2.6.4之前的版本上沒有高精度定時(shí)器的情況下,低精度的定時(shí)器就成了首選,但是這種低精度的定時(shí)器他有個(gè)缺點(diǎn),他的最高精度只能達(dá)到毫秒級(jí)別,大概是10ms左右。這種內(nèi)核定時(shí)器是內(nèi)核用來控制在未來某個(gè)時(shí)間點(diǎn)(基于jiffies)調(diào)度執(zhí)行某個(gè)函數(shù)的一種機(jī)制,我們可以在他的回調(diào)函數(shù)中實(shí)現(xiàn)一些自己的功能。
struct timer_list {
?????struct list_head entry;
?????unsigned long expires;
?????void (*function)(unsigned long);
?????unsigned long data;
?????struct tvec_base *base;
};
6.3高精度定時(shí)器
而隨著內(nèi)核的不斷演進(jìn),大牛們已經(jīng)對(duì)這種低分辨率定時(shí)器的精度不再滿足,而且,硬件也在不斷地發(fā)展,系統(tǒng)中的定時(shí)器硬件的精度也越來越高,這也給高分辨率定時(shí)器的出現(xiàn)創(chuàng)造了條件。內(nèi)核從2.6.16開始加入了高精度定時(shí)器架構(gòu)。為此,內(nèi)核為高精度定時(shí)器重新設(shè)計(jì)了一套軟件架構(gòu),它可以為我們提供納秒級(jí)的定時(shí)精度,以滿足對(duì)精確時(shí)間有迫切需求的應(yīng)用程序或內(nèi)核驅(qū)動(dòng),例如多媒體應(yīng)用,音頻設(shè)備的驅(qū)動(dòng)程序等等。如用hrtimer(high resolution timer)表示高精度定時(shí)器。
內(nèi)核的開發(fā)者考察了多種數(shù)據(jù)結(jié)構(gòu),例如基數(shù)樹、哈希表等等,最終他們選擇了紅黑樹(rbtree)來組織hrtimer,紅黑樹已經(jīng)以庫的形式存在于內(nèi)核中,并被成功地使用在內(nèi)存管理子系統(tǒng)和文件系統(tǒng)中,隨著系統(tǒng)的運(yùn)行,hrtimer不停地被創(chuàng)建和銷毀,新的hrtimer按順序被插入到紅黑樹中,樹的最左邊的節(jié)點(diǎn)就是最快到期的定時(shí)器,內(nèi)核用一個(gè)hrtimer結(jié)構(gòu)來表示一個(gè)高精度定時(shí)器:
struct hrtimer {
struct timerqueue_node node;
ktime_t _softexpires;
enum hrtimer_restart (*function)(struct hrtimer *);
struct hrtimer_clock_base *base;
unsigned long state;
......
};
6.4動(dòng)態(tài)時(shí)鐘
Linux中的時(shí)鐘事件都是由一個(gè)周期時(shí)鐘提供,不管系統(tǒng)中的clock_event_device是工作于周期觸發(fā)模式,還是工作于單觸發(fā)模式,也不管定時(shí)器系統(tǒng)是工作于低分辨率模式,還是高精度模式,內(nèi)核都竭盡所能,用不同的方式提供周期時(shí)鐘,以產(chǎn)生定期的tick事件,tick事件或者用于全局的時(shí)間管理(jiffies和時(shí)間的更新),或者用于本地cpu的進(jìn)程統(tǒng)計(jì)、時(shí)間輪定時(shí)器框架等等。周期性時(shí)鐘雖然簡(jiǎn)單有效,但是也帶來了一些缺點(diǎn),尤其在系統(tǒng)的功耗上,因?yàn)榫退阆到y(tǒng)目前無事可做,也必須定期地發(fā)出時(shí)鐘事件,激活系統(tǒng)。為此,內(nèi)核的開發(fā)者提出了動(dòng)態(tài)時(shí)鐘這一概念,我們可以通過內(nèi)核的配置項(xiàng)CONFIG_NO_HZ來激活特性。有時(shí)候這一特性也被叫做tickless,不過還是把它稱呼為動(dòng)態(tài)時(shí)鐘比較合適,因?yàn)椴⒉皇钦娴臎]有tick事件了,只是在系統(tǒng)無事所做的idle階段,我們可以通過停止周期時(shí)鐘來達(dá)到降低系統(tǒng)功耗的目的,只要有進(jìn)程處于活動(dòng)狀態(tài),時(shí)鐘事件依然會(huì)被周期性地發(fā)出。
在動(dòng)態(tài)時(shí)鐘正確工作之前,系統(tǒng)需要切換至動(dòng)態(tài)時(shí)鐘模式,而要切換至動(dòng)態(tài)時(shí)鐘模式,需要一些前提條件,最主要的一條就是cpu的時(shí)鐘事件設(shè)備必須要支持單觸發(fā)模式,當(dāng)條件滿足時(shí),系統(tǒng)切換至動(dòng)態(tài)時(shí)鐘模式,接著,由idle進(jìn)程決定是否可以停止周期時(shí)鐘,退出idle進(jìn)程時(shí)則需要恢復(fù)周期時(shí)鐘。
切換到高精度模式后,高精度定時(shí)器系統(tǒng)需要使用一個(gè)高精度定時(shí)器來模擬傳統(tǒng)的周期時(shí)鐘,其中利用了tick_sched結(jié)構(gòu)中的一些字段,事實(shí)上,tick_sched結(jié)構(gòu)也是實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘的一個(gè)重要的數(shù)據(jù)結(jié)構(gòu),在smp系統(tǒng)中,內(nèi)核會(huì)為每個(gè)cpu都定義一個(gè)tick_sched結(jié)構(gòu),這通過一個(gè)percpu全局變量tick_cpu_sched來實(shí)現(xiàn),它在kernel/time/tick-sched.c中定義:static DEFINE_PER_CPU(struct tick_sched, tick_cpu_sched);
6.5時(shí)鐘中斷處理
在Linux的0號(hào)中斷是一個(gè)定時(shí)器中斷。在固定的時(shí)間間隔都發(fā)生一次中斷,也是說每秒發(fā)生該中斷的頻率都是固定的。該頻率是常量HZ,該值一般是在100 ~ 1000之間。該中斷的作用是為了定時(shí)更新系統(tǒng)日期和時(shí)間,使系統(tǒng)時(shí)間不斷地得到跳轉(zhuǎn)。另外該中斷的中斷處理函數(shù)除了更新系統(tǒng)時(shí)間外,還需要更新本地CPU統(tǒng)計(jì)數(shù)。指的是調(diào)用scheduler_tick遞減進(jìn)程的時(shí)間片,若進(jìn)程的時(shí)間片遞減到0,進(jìn)程則被調(diào)度出去而放棄CPU使用權(quán)。
Linux的OS時(shí)鐘的物理產(chǎn)生原因是可編程定時(shí)/計(jì)數(shù)器產(chǎn)生的輸出脈沖,這個(gè)脈沖送入CPU,就可以引發(fā)一個(gè)中斷請(qǐng)求信號(hào),我們就把它叫做時(shí)鐘中斷。
“時(shí)鐘中斷”是特別重要的一個(gè)中斷,因?yàn)檎麄€(gè)操作系統(tǒng)的活動(dòng)都受到它的激勵(lì)。系統(tǒng)利用時(shí)鐘中斷維持系統(tǒng)時(shí)間、促使環(huán)境的切換,以保證所有進(jìn)程共享CPU;利用時(shí)鐘中斷進(jìn)行記帳、監(jiān)督系統(tǒng)工作以及確定未來的調(diào)度優(yōu)先級(jí)等工作??梢哉f,“時(shí)鐘中斷”是整個(gè)操作系統(tǒng)的脈搏。
操作系統(tǒng)對(duì)可編程定時(shí)/計(jì)數(shù)器進(jìn)行有關(guān)初始化,然后定時(shí)/計(jì)數(shù)器就對(duì)輸入脈沖進(jìn)行計(jì)數(shù)(分頻),產(chǎn)生的三個(gè)輸出脈沖Out0、Out1、Out2各有用途,很多接口書都介紹了這個(gè)問題,我們只看Out0上的輸出脈沖,這個(gè)脈沖信號(hào)接到中斷控制器8259A_1的0號(hào)管腳,觸發(fā)一個(gè)周期性的中斷,我們就把這個(gè)中斷叫做時(shí)鐘中斷,時(shí)鐘中斷的周期,也就是脈沖信號(hào)的周期,我們叫做“滴答”或“時(shí)標(biāo)”(tick)。從本質(zhì)上說,時(shí)鐘中斷只是一個(gè)周期性的信號(hào),完全是硬件行為,該信號(hào)觸發(fā)CPU去執(zhí)行一個(gè)中斷服務(wù)程序,但是為了方便,我們就把這個(gè)服務(wù)程序叫做時(shí)鐘中斷。
6.6延遲執(zhí)行
在linux中,可以設(shè)定延時(shí)執(zhí)行命令,以及定時(shí)執(zhí)行命令,延時(shí)任務(wù)是指在多久以后或在指定的時(shí)間系統(tǒng)自動(dòng)執(zhí)行命令,延時(shí)任務(wù)一次設(shè)定只能起效一次;定時(shí)任務(wù)是指在指定的某個(gè)時(shí)間自動(dòng)執(zhí)行某個(gè)命令,或程序,它與延時(shí)的區(qū)別在于定時(shí)任務(wù)可以重復(fù)的執(zhí)行,例如每天的1點(diǎn)關(guān)機(jī),就是定時(shí)任務(wù)。
常用at命令創(chuàng)建延時(shí)任務(wù),具體步驟為:
1.安裝at,執(zhí)行sudo apt install at
2.安裝完成后,Ctrl+T打開終端,輸入at + [開始執(zhí)行的時(shí)間],回車;
3.在出現(xiàn)的命令行內(nèi)輸入需要執(zhí)行的指令;
4.按回車可以繼續(xù)輸入第二條命令;
5.所有命令輸入完畢后,按Ctrl+d執(zhí)行。
at·命令可以接受多種時(shí)間指定方式,比如at now+2min表示在2分鐘后開始執(zhí)行,at 21:00表示在晚上9點(diǎn)開始執(zhí)行,at 2018/10/22表示在2018年10月22日開始執(zhí)行。需要注意的是,對(duì)于指定具體時(shí)間的運(yùn)行方式,如果指定的時(shí)間已經(jīng)過了,系統(tǒng)將會(huì)在次日這個(gè)時(shí)間運(yùn)行程序。
通過at命令開始的程序,在開始執(zhí)行后似乎沒有直接的方法能夠停止程序,所以建議在設(shè)置命令前,最好確保輸入的命令正確。且at是一次性命令,即執(zhí)行完畢就退出了,不會(huì)循環(huán)執(zhí)行。
at可以查看當(dāng)前等待執(zhí)行的任務(wù)列表和任務(wù)id,并允許取消還未執(zhí)行的列表內(nèi)任務(wù)(atrm)。具體操作可以在終端輸入at查看幫助