那為什麼需要loops_per_jiffy這個值?因為Kernel Driver有時需要很短且精準的delay (millisecond, microsecond, and nanosecond delays),此時透過jiffies實現delay是不適用,由於單位不夠小,假設Timer Frequency是250HZ,則一個tick相當於4ms,表示最小delay時間只能到4ms。所以Kernel提供 mdelay(), udelay(), 和 ndelay() 這三個函式來達到極短且精準的delay,而這三個函式是透過loops_per_jiffy實現,去計算需要多少個loop operation來得到精確的delay。
loops_per_jiffy值是透過calibrate_delay()計算得到,定義在init/calibrate.c,分析如下:
#define LPS_PREC 8
void __cpuinit calibrate_delay(void)
{
unsigned long ticks, loopbit;
int lps_precision = LPS_PREC;
static bool printed;
if (preset_lpj) {
loops_per_jiffy = preset_lpj;
if (!printed)
pr_info("Calibrating delay loop (skipped) "
"preset value.. ");
} else if ((!printed) && lpj_fine) {
loops_per_jiffy = lpj_fine;
pr_info("Calibrating delay loop (skipped), "
"value calculated using timer frequency.. ");
} else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) {
if (!printed)
pr_info("Calibrating delay using timer "
"specific routine.. ");
} else {
loops_per_jiffy = (1<<12); //一個jiffy時間內執行多少次delay迴圈,初始值4096。
if (!printed)
pr_info("Calibrating delay loop... ");
/*
* 底下while迴圈,第一次粗略的計算loops_per_jiffy,
* 為下面的計算打好基礎。一般loops_per_sec的初始值
* 並不大(4096),所以循環會逐步加大loops_per_sec的
* 值,直到延時超過一個jiffy。我們可以看出,前一次
* loops_per_sec的值還因太小不合適時,經過一次增大
* ,它提高了兩倍,滿足了循環條件,跳出循環,而這
* 個值實在是誤差太大,所以我們還要經過第二次計算。
*/
while ((loops_per_jiffy <<= 1) != 0) {
/* wait for "start of" clock tick */
ticks = jiffies; //紀錄當前jiffies
while (ticks == jiffies) //等待下一個新的jiffy開始
/* nothing */;
/* Go .. */
ticks = jiffies; //紀錄新的jiffy開始後當前jiffies
__delay(loops_per_jiffy); //依據loops_per_jiffy值進行延時
//下面三行用於判斷執行的延時是否超過一個jiffy時間。
ticks = jiffies - ticks;
if (ticks)
break;
}
/*
* Do a binary approximation to get loops_per_jiffy set to
* equal one clock (up to lps_precision bits)
*
* 這裡開始就是第二次計算了。由於第一次階段得到的
* loops_per_jiffy是粗略值,而且大於正確值很多,
* 所以要向右shift一個bit,既由0b10000變成0b1000。
*/
loops_per_jiffy >>= 1;
/*
* 然後第二次計算就是要找出0b1xyz中的xyz值,
* 底下的while迴圈會先假設a為1,然後測試0b1100,
* 如果0b110超過一個jiffy時間,則x=0,如果沒超過,
* 則x=1,然後再繼續檢查y,以此類推。最後檢查到
* LPS_PREC=8個位數。
*/
loopbit = loops_per_jiffy;
while (lps_precision-- && (loopbit >>= 1)) {
loops_per_jiffy |= loopbit;
ticks = jiffies;
while (ticks == jiffies)
/* nothing */;
ticks = jiffies;
__delay(loops_per_jiffy);
if (jiffies != ticks) /* longer than 1 tick */
loops_per_jiffy &= ~loopbit;
}
}
/*
* BogoMIPS = (loops_per_jiffy * HZ * 2) / (1 million)
*/
if (!printed)
pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100, loops_per_jiffy);
printed = true;
}
註:- HZ : 每隔固定週期Linux Kernel會發出timer interrupt,HZ定義每一秒有幾次timer interrupts。
- Tick (Jiffy) : HZ的倒數,意即timer過多久發生一次interrupt。
- Jiffies : 用來紀錄系統自開幾以來,已經過多少的tick,每發生一次timer interrupt,Jiffies變數會被加一。
沒有留言:
張貼留言