Linux 下的推迟执行
準備中秋節
說個活動,評論文章點贊排名,用心評論哦,前5名獲得每人 19 心意紅包。
感謝大家的支持
我最近在用freertos,想讓一個任務在某個時間后再執行,找了一圈,竟然沒有這樣才處理機制,因為也是新手入門freertos,可能需要自己實現,當然了,自己實現的話,機制就很多了,但是有個問題是,自己實現的話,就感覺不夠規范,因為這樣的原因,我還特意從Linux上移植了time_before和time_after過去,用了下,感覺還是很爽的。
Linux 有延遲執行的機制,有幾種辦法
1、忙等待
聽到這個就知道了,如果是忙等待的話,肯定是占用cpu的,所以忙等待其實也是使用了time_before這個宏來實現。
#define time_after(a,b) \ (typecheck(unsigned long, a) && \ typecheck(unsigned long, b) && \ ((long)((b) - (long)(a)) < 0)) time_before(a,b) time_after(b,a)這個是實現的原型,time_before也就是time_after反過來而已,我們之前有一篇文章討論了time_after宏的實現和用法。不清楚的同學可以去看看,其中把無符號強制轉成有符號是關鍵。
那我們怎么使用這個忙等待呢
很簡單
unsigned long timeout = jiffies +10; while(time_before(jiffies,timeout));while 循環會一直執行,因為time_before會一直返回true,知道jiffies的時間超過timeout的時間,這時候就會返回false。
也可以是這樣使用
unsigned long timeout = jiffies +2*HZ; while(time_before(jiffies,timeout));這個是等待2秒,一秒鐘的節拍數是HZ,所以2秒就是2*HZ,這個好像太簡單了些。
2、短延遲
這個也類似于忙等待,但是這個忙等待使用的函數不同,我們使用jiffies使用的是系統軟件滴答數來做延遲,精度和時間上都有一定的局限性,但是使用delay函數的話,會相對好一些,時間的精準度會比較好。
void udelay(unsigned long usecs) void ndelay(unsigned long usecs) void mdelay(unsigned long usecs)學習單片機的同學都知道,CPU執行的時間可以通過指令周期來確定時間,指令周期就是執行一條簡單的指令所花費的時間,80C51下我知道是多少,ARM我還不懂,但是這些我們也不用太關心,每個體系結構下的delay實現,他們都自己計算實現好了,這也是使用系統和單片機的好處,封裝什么的都搞好,就是要會使用才是關鍵。
用延遲實現的弊端就是會一直占用CPU時間,系統調用需要非常良好的性能,所以我們使用上面delay函數的時候,如果大于1ms的話,就可以換一種實現方式了。
1、時鐘周期 = 振蕩周期,名稱不同而已,都是等于單片機晶振頻率的倒數,如常見的外接12M晶振,那它的時鐘周期=1/12M。
2、機器周期,8051系列單片機的機器周期=12*時鐘周期,之所以這樣分是因為單個時鐘周期根本干不了一件完整的事情(如取指令、寫寄存器、讀寄存器等),而12個時鐘周期就能基本完成一項基本操作了。
3、指令周期。一個機器周期能完成一項基本操作,但一條指令常常是需要多項基本操作結合才能完成,完成一條指令所需的時間就是指令周期,當然不同的指令,其指令周期就不一樣的了。
3、schedule_timeout
在上面兩種方法的局限下,這個應該是最好的實現方式了,它的好是因為他可以睡眠,睡眠有一個好處就是不需要占用CPU資源,等時間到了再起床去干活就好了。
set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(S*HZ);上面的代碼是讓當前任務進入不可中斷狀態,任何睡眠S秒后再起床,使用schedule_timeout的時候,一定要記得設置狀態,不然不能睡覺就麻煩了,也要注意你自己寫的代碼能不能睡眠,要不然引起問題就更尷尬了。
fastcall signed long __sched schedule_timeout(signed long timeout) { struct timer_list timer; unsigned long expire; switch (timeout) { case MAX_SCHEDULE_TIMEOUT: /* * These two special cases are useful to be comfortable * in the caller. Nothing more. We could take * MAX_SCHEDULE_TIMEOUT from one of the negative value * but I' d like to return a valid offset (>=0) to allow * the caller to do everything it want with the retval. */ schedule(); goto out; default: /* * Another bit of PARANOID. Note that the retval will be * 0 since no piece of kernel is supposed to do a check * for a negative retval of schedule_timeout() (since it * should never happens anyway). You just have the printk() * that will tell you if something is gone wrong and where. */ if (timeout < 0) { printk(KERN_ERR "schedule_timeout: wrong timeout " "value %lx from %p\n", timeout, __builtin_return_address(0)); current->state = TASK_RUNNING; goto out; } } expire = timeout + jiffies; init_timer(&timer); timer.expires = expire; timer.data = (unsigned long) current; timer.function = process_timeout; add_timer(&timer); schedule(); del_singleshot_timer_sync(&timer); timeout = expire - jiffies; out: return timeout < 0 ? 0 : timeout; }網上有挺多文章說明了這個函數的實現,首先是初始化一個timer,然后往timer里去傳初始化參數,其中有一個參數是current,這個是一個宏,這個宏的作用是獲取當前的task,然后再設置超時時間,超時時間到了之后,通過調用process_timeout去喚醒之前休眠的task。
我們可以這樣使用,當做一個delay來嵌入自己的代碼中
inline static void snd_xx_delay_long(void) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); }掃碼或長按關注
總結
以上是生活随笔為你收集整理的Linux 下的推迟执行的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 广播接收
- 下一篇: Linux 5.7 将支持国产 RISC