那為什麼需要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變數會被加一。
沒有留言:
張貼留言