時間單位:
毫秒(ms)、微秒 (μs)、納秒(ns)、皮秒(ps)、飛秒(fs)、阿秒、渺秒
1 s = 10^3 ms = 10^6 us = 10^9 ns = 10^12 ps = 10^15 fs=10^18阿秒=10^21渺秒=10^43普朗克常數
在Linux Driver開發中,經常要用到延遲函數:msleep,mdelay/udelay.
雖然msleep和mdelay都有延遲的作用,但他們是有區別的.
mdeday還忙等待函數(相當於for循環),在延遲過程中無法運行其他任務.這個延遲的時間是准確的.是需要等待多少時間就會真正等待多少時間.而msleep是休眠函數,它不涉及忙等待.你如果是msleep(10),那實際上延遲的時間,大部分時候是要多於10ms的,是個不定的時間值.
他們的差異,平時我也講的出來,可是真正用起來的時候,就忘記了.曾在兩個driver的i2c的code中,需要用到delay函數,而我用了msleep函數,一直I2C速度超慢.而我又不知道哪里出了問題,我潛意識中,認為我只delay了1ms,可是,實際上是十幾毫秒.
這幾個函數都是內核的延時函數:
1.
udelay(); mdelay(); ndelay();實現的原理本質上都是忙等待,ndelay和mdelay都是通過udelay衍生出來的,我們使用這些函數的實現往往會碰到編譯器的警告implicit declaration of function'udelay',這往往是由於頭文件的使用不當造成的。在include/asm-???/delay.h中定義了udelay(),而在include/linux/delay.h中定義了mdelay和ndelay.(這點弄錯了吧,應該是ndelay最小吧)
udelay一般適用於一個比較小的delay,如果你填的數大於2000,系統會認為你這個是一個錯誤的delay函數,因此如果需要2ms以上的delay需要使用mdelay函數。
2.由於這些delay函數本質上都是忙等待,對於長時間的忙等待意味這無謂的耗費着cpu的資源,因此對於毫秒級的延時,內核提供了msleep,ssleep等函數,這些函數將使得調用它的進程睡眠參數指定的時間。
應用層:
#include <unistd.h>
1、unsigned int sleep(unsigned int seconds); 秒級
2、int usleep(useconds_t usec); 微秒級:1/10^-6
#define _POSIX_C_SOURCE 199309
#include <time.h>
3、int nanosleep(const struct timespec *req, struct timespec *rem);
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
// The value of the nanoseconds field must be in the range 0 to 999999999.
內核層:
include <linux/delay.h>
1、void ndelay(unsigned long nsecs); 納秒級:1/10^-10
2、void udelay(unsigned long usecs); 微秒級: 1/10^-6
3、void mdelay(unsigned long msecs); 毫秒級:1/10^-3
sleep_on(), interruptible_sleep_on();
sleep_on_timeout(), interruptible_sleep_on_timeout();
根據你的情況選用這些函數,注意: sleep操作在kernel必須小心、小心。。。
udelay()等函數是cpu忙等,沒有傳統意義上的sleep。這些函數相當於我們平時的阻塞讀、寫之類的語義,主要用於等外設完成某些操作
------
nanosleep:
struct timespec
{
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
這個函數功能是暫停某個進程直到你規定的時間后恢復,參數req就是你要暫停的時間,其中req->tv_sec是以秒為單位,而tv_nsec以毫微秒為單位(10的-9次方秒)。由於調用nanosleep是是進程進入TASK_INTERRUPTIBLE,這種狀態是會相應信號而進入TASK_RUNNING狀態的,這就意味着有可能會沒有等到你規定的時間就因為其它信號而喚醒,此時函數返回-1,切還剩余的時間會被記錄在rem中。
看到這里剛剛看到他的實現是:將其狀態設置成TASK_INTERRUPTIBLE,脫離就緒隊列,然后進行一次進程調度再由內核在規定的時間后發送信號來喚醒這個進程。
在我剛開始學習編程時候,那時候我也曾試圖使上下2條指令相隔一定時間來運行,那時我的做法是在這2條指令之間加上了一個400次的循環。這也算一種實現方式,我管它叫作延遲,但沒有利用進程休眠來實現的好。但有一種特殊情況,使用休眠就無法實現了。
我們知道這里肯定脫離不了時鍾中斷,沒有時鍾中斷的計時我們是無法實現這一功能的。那么假設時鍾種中斷是10毫秒一次(這種CPU還是有的),那么我們可以看到在函數調用的時候我們可以以毫微秒來暫停,如果我tv_sec = 0, tv_nsec = 2,那么時鍾中斷一定是在10微秒后來喚醒這個進程的,如果非實時性任務差個8微秒估計沒什么大不了,不幸的是LINUX支持實時性任務SCHED_FIFO和SCHED_RR.(我們以前談到過)。
這時8微秒的差距就是不能容忍了,這是就不能靠休眠和時鍾中斷來實現了,這是linux采用就是延遲辦法,執行一個循環來達到暫停的目的。
這2種實現的差別就是休眠實現的話,進程會進入休眠狀態,而延遲實現的話,CPU是在執行循環不會進入休眠態。所以可以說雖然名為nanosleep,但它不一定會使進程進入sleep狀態,當然不進入sleep 態的條件太苛刻(沒多少人會寫實時任務,且還是暫停要小於CPU時鍾頻率,加上現在CPU的頻率是如此之高,這種情況一般發生在要求外設中斷不小於某個特定值,而且應該是采用比較老的CPU或者嵌入式中)。
喚醒問題:
msleep:睡眠之后不可喚醒;
msleep_interuptible:睡眠之后可喚醒;
ssleep:s延時,睡眠時候不可喚醒;