內核中的時間概念
HZ:
Linux 核心每隔固定週期會發出timer interrupt (IRQ 0),HZ是用來定義每一秒有幾次timer interrupts。舉例來說,HZ爲1000,代表每秒有1000次timer interrupts
jiffies:
全局變量jiffies用於記錄系統啓動以來產生的節拍的總數。
啓動時,jiffies初始化爲0,此後每次時鐘中斷處理程序都會增加該變量的值。
linux提供了4個宏來比較節拍計數
#include <linux/jiffies.h>
#define time_after(a, b) // b > a
#define time_before(a, b) // b < a
#define time_after_eq(a, b) // b >= a
#define time_before_eq(a, b) // b <= a
時間獲取
驅動程序中一般不需要知道牆鍾時間(也就是年月日的時間)。但驅動可能需要處理絕對時間。
爲此,內核提供了兩個結構體,都定義在
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
(2)採用秒和納秒值保存時間。
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
(3)用do_gettimeofday()用於獲得timeval
#include <linux/time.h>
void do_gettimeofday(struct timeval *tv);
(4) current_kernel_time() 用於獲得timespec
#include <linux/time.h>
struct timespec current_kernel_time(void);
延遲操作
1.長延遲
(1)忙等待
如果對延遲的精確度要求不高,最簡單的方法是實現一個監視jiffies計時器的循環。
unsigned long delay = jiffies + 5*HZ;
while(time_before(jiffies, delay))
cpu_relax();
(2)超時
#include <linux/sched.h>
signed long schedule_timeout(signed long timeout);
timeout是用jiffies表示的延遲時間,正常值返回0.
schedule_timeout在使用前需要設置當前進程狀態。
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ); /* 睡2秒 */
進程經過2秒後會被喚醒。
第一行調用set_current_state已設置當前進程狀態,調度器只有在超時到期且其狀態爲TASK_RUNNING時纔會運行這個進程。如果不希望被用戶空間打斷,可以將進程狀態設置爲TASK_UNINTERRUPTIBLE。
(3)讓出處理器
while(time_before(jiffies, j1))
schedule();
在等待期間可以讓出處理器,減少CPU的負擔。
2.短延遲
對於那些最多幾十個毫秒的延遲,不需要依賴時間滴答
#include <Linux/delay.h>
void ndelay(unsigned long nsecs); /*延遲納秒 */
void udelay(unsigned long usecs); /*延遲微秒 */
void mdelay(unsigned long msecs); /*延遲毫秒 */
這三個延遲函數均是忙等待函數,在延遲過程中無法運行其他任務。 它們的實現使用了軟件循環。
實現毫秒級(或者更長)延遲還有一種方法,這種方法不涉及忙等待
#include <Linux/delay.h>
void msleep(unsigned int millisecs); /*休眠毫秒 */
void ssleep(unsigned int seconds); /*休眠秒 */
unsigned long msleep_interruptible(unsigned int millisecs);/*休眠毫秒,中斷可以喚醒*/