自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux時間管理之clocksource

系統(tǒng) Linux
前面提到了Linux下的時間相關(guān)的硬件。TSC PIT,HPET,ACPI_PM,這些硬件以一定的頻率產(chǎn)生時鐘中斷,來幫助我們計(jì)時。Linux為了管理這些硬件,抽象出來clocksource。

前面提到了Linux下的時間相關(guān)的硬件。TSC PIT,HPET,ACPI_PM,這些硬件以一定的頻率產(chǎn)生時鐘中斷,來幫助我們計(jì)時。Linux為了管理這些硬件,抽象出來clocksource。

 

  1. struct clocksource { 
  2.     /* 
  3.      * Hotpath data, fits in a single cache line when the 
  4.      * clocksource itself is cacheline aligned. 
  5.      */ 
  6.     cycle_t (*read)(struct clocksource *cs); 
  7.     cycle_t cycle_last; 
  8.     cycle_t mask; 
  9.     u32 mult; 
  10.     u32 shift; 
  11.     u64 max_idle_ns; 
  12.     u32 maxadj; 
  13. #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA 
  14.     struct arch_clocksource_data archdata; 
  15. #endif 
  16.   
  17.     const char *name; 
  18.     struct list_head list; 
  19.     int rating; 
  20.     int (*enable)(struct clocksource *cs); 
  21.     void (*disable)(struct clocksource *cs); 
  22.     unsigned long flags; 
  23.     void (*suspend)(struct clocksource *cs); 
  24.     void (*resume)(struct clocksource *cs); 
  25.   
  26.     /* private: */ 
  27. #ifdef CONFIG_CLOCKSOURCE_WATCHDOG 
  28.     /* Watchdog related data, used by the framework */ 
  29.     struct list_head wd_list; 
  30.     cycle_t cs_last; 
  31.     cycle_t wd_last; 
  32. #endif 
  33. } ____cacheline_aligned; 

 

這些參數(shù)當(dāng)中,比較重要的是rating,shift,mult。其中rating在上一篇博文提到了:

  • 1--99: 不適合于用作實(shí)際的時鐘源,只用于啟動過程或用于測試;
  • 100--199:基本可用,可用作真實(shí)的時鐘源,但不推薦;
  • 200--299:精度較好,可用作真實(shí)的時鐘源;
  • 300--399:很好,精確的時鐘源;
  • 400--499:理想的時鐘源,如有可能就必須選擇它作為時鐘源;

我們基本在前面看到:    

 

  1. include/linux/acpi_pmtmr.h 
  2. ------------------------------------------ 
  3. #define PMTMR_TICKS_PER_SEC 3579545 
  4.   
  5. drivers/clocksource/acpi_pm.c 
  6. --------------------------------------------- 
  7. static struct clocksource clocksource_acpi_pm = { 
  8.           .name = "acpi_pm"
  9.           .rating = 200
  10.           .read = acpi_pm_read
  11.           .mask = (cycle_t)ACPI_PM_MASK, 
  12.           .mult = 0, /*to be calculated*/ 
  13.           .shift = 22
  14.           .flags = CLOCK_SOURCE_IS_CONTINUOUS
  15.   
  16.  }; 
  17.   
  18. dmesg output 
  19. ------------------------ 
  20. [ 0.664201] hpet0: 8 comparators, 64-bit 14.318180 MHz counter 
  21.   
  22. arch/86/kernel/hpet.c 
  23. -------------------------------- 
  24. static struct clocksource clocksource_hpet = { 
  25.     .name = "hpet"
  26.     .rating = 250
  27.     .read = read_hpet
  28.     .mask = HPET_MASK
  29.     .flags = CLOCK_SOURCE_IS_CONTINUOUS
  30.     .resume = hpet_resume_counter
  31. #ifdef CONFIG_X86_64 
  32.     .archdata = { .vclock_mode = VCLOCK_HPET }, 
  33. #endif 
  34. }; 
  35.   
  36.   
  37. dmesg output: 
  38. ----------------------------- 
  39. [ 0.004000] Detected 2127.727 MHz processor.  
  40.   
  41.   
  42. arch/x86/kernel/tsc.c 
  43. -------------------------------------- 
  44. static struct clocksource clocksource_tsc = { 
  45.     .name = "tsc"
  46.     .rating = 300
  47.     .read = read_tsc
  48.     .resume = resume_tsc
  49.     .mask = CLOCKSOURCE_MASK(64), 
  50.     .flags = CLOCK_SOURCE_IS_CONTINUOUS | 
  51.                   CLOCK_SOURCE_MUST_VERIFY, 
  52. #ifdef CONFIG_X86_64 
  53.     .archdata = { .vclock_mode = VCLOCK_TSC }, 
  54. #endif 
  55. }; 

 

從上面可以看到,acpi_pm,hpet tsc的rating分別是200,250,300,他們的rating基本是和他們的frequency符合,TSC以2127.727MHz的頻率技壓群雄,等級rating=300最高,被選擇成current_clocksource:

 

  1. root@manu:~# cat /sys/devices/system/clocksource/clocksource0/available_clocksource  
  2. tsc hpet acpi_pm  
  3. root@manu:~# cat /sys/devices/system/clocksource/clocksource0/current_clocksource  
  4. tsc 

 

除此外,還有兩個參數(shù)shift和mult,這兩個參數(shù)是干啥的呢?

我們想一下,假如我們需要給你個以一定頻率輸出中斷的硬件,你如何計(jì)時?比如我有一個頻率是1000Hz的硬件,當(dāng)前時鐘源計(jì)數(shù)是3500,過了一段時間,我抬頭看了下時鐘源計(jì)數(shù)至是5500,過去了2000cycles,我就知道了過去了2000/1000 =2 second。

 

  1. times_elapse = cycles_interval / frequency  

從上面的例子中,我抬頭看了下當(dāng)前計(jì)數(shù)值這個肯定是瞎掰了,實(shí)際上要想獲取時鐘源還是需要和硬件打交道的。在clocksource中有一個成員變量是read,這個就是一個時鐘源注冊的時候,提供的一個函數(shù),如果你想獲得我的當(dāng)前計(jì)數(shù)值,請調(diào)用這個read 函數(shù)。以TSC時鐘為例:

 

  1. static struct clocksource clocksource_tsc = { 
  2.     .name = "tsc"
  3.     .rating = 300
  4.     .read = read_tsc
  5.     .resume = resume_tsc
  6.     .mask = CLOCKSOURCE_MASK(64), 
  7.     .flags = CLOCK_SOURCE_IS_CONTINUOUS | 
  8.                   CLOCK_SOURCE_MUST_VERIFY, 
  9. #ifdef CONFIG_X86_64 
  10.     .archdata = { .vclock_mode = VCLOCK_TSC }, 
  11. #endif 
  12. }; 
  13.   
  14. /*--------- arch/x86/kernel/tsc.c -------------------*/ 
  15. static cycle_t read_tsc(struct clocksource *cs) 
  16.     cycle_t ret = (cycle_t)get_cycles(); 
  17.   
  18.     return ret >= clocksource_tsc.cycle_last ? 
  19.         ret : clocksource_tsc.cycle_last; 
  20.   
  21. /*------- arch/x86/include/asm/tsc.h----------------------*/ 
  22. static inline cycles_t get_cycles(void) 
  23.     unsigned long long ret = 0
  24.   
  25. #ifndef CONFIG_X86_TSC 
  26.     if (!cpu_has_tsc) 
  27.         return 0; 
  28. #endif 
  29.     rdtscll(ret); 
  30.   
  31.     return ret; 
  32.   
  33. /*------arch/x86/include/asm/msr.h-----------------*/ 
  34. #define rdtscll(val)                        \ 
  35.     ((val) = __native_read_tsc()) 
  36.   
  37. static __always_inline unsigned long long __native_read_tsc(void) 
  38.     DECLARE_ARGS(val, low, high); 
  39.   
  40.     asm volatile("rdtsc" : EAX_EDX_RET(val, low, high)); 
  41.   
  42.     return EAX_EDX_VAL(val, low, high); 

 

根據(jù)這個脈絡(luò),我們知道,最終就是rdtsc這條指令來獲取當(dāng)前計(jì)數(shù)值cycles。

扯了半天read這個成員變量,可以回到shift和mult了。其實(shí)shift和mult是為了解決下面這個公式的:

 

  1. times_elapse = cycles_interval / frequency 

就像上面的公式,有頻率就足以計(jì)時了。為啥弄出來個shift和mult。原因在于kernel搞個除法不太方便,必須轉(zhuǎn)化乘法和移位。Kernel中有很多這種把除法轉(zhuǎn)化成乘法的樣例。那么公式變成了:

 

  1. times_elapse = cycles_interval * mult >> shift 

Kernel用乘法+移位來替換除法:根據(jù)cycles來計(jì)算過去了多少ns。

 

  1. /** 
  2.  * clocksource_cyc2ns - converts clocksource cycles to nanoseconds 
  3.  * @cycles:    cycles 
  4.  * @mult:    cycle to nanosecond multiplier 
  5.  * @shift:    cycle to nanosecond pisor (power of two) 
  6.  * 
  7.  * Converts cycles to nanoseconds, using the given mult and shift. 
  8.  * 
  9.  * XXX - This could use some mult_lxl_ll() asm optimization 
  10.  */ 
  11. static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift) 
  12.     return ((u64) cycles * mult) >> shift; 

 

單純從精度上講,肯定是mult越大越好,但是計(jì)算過程可能溢出,所以mult也不能無限制的大,這個計(jì)算中有個magic number 600 :

 

  1. void __clocksource_updatefreq_scale(struct clocksource *cs, u32 scale, u32 freq) 
  2.     u64 sec; 
  3.     /* 
  4.      * Calc the maximum number of seconds which we can run before 
  5.      * wrapping around. For clocksources which have a mask > 32bit 
  6.      * we need to limit the max sleep time to have a good 
  7.      * conversion precision. 10 minutes is still a reasonable 
  8.      * amount. That results in a shift value of 24 for a 
  9.      * clocksource with mask >= 40bit and f >= 4GHz. That maps to 
  10.      * ~ 0.06ppm granularity for NTP. We apply the same 12.5% 
  11.      * margin as we do in clocksource_max_deferment() 
  12.      */ 
  13.     sec = (cs->mask - (cs->mask >> 3)); 
  14.     do_p(sec, freq); 
  15.     do_p(sec, scale); 
  16.     if (!sec) 
  17.         sec = 1
  18.     else if (sec > 600 && cs->mask > UINT_MAX) 
  19.         sec = 600
  20.   
  21.     clocks_calc_mult_shift(&cs->mult, &cs->shift, freq, 
  22.              NSEC_PER_SEC / scale, sec * scale); 
  23.   
  24.     /* 
  25.      * for clocksources that have large mults, to avoid overflow. 
  26.      * Since mult may be adjusted by ntp, add an safety extra margin 
  27.      * 
  28.      */ 
  29.     cs->maxadj = clocksource_max_adjustment(cs); 
  30.     while ((cs->mult + cs->maxadj < cs->mult) 
  31.         || (cs->mult - cs->maxadj > cs->mult)) { 
  32.         cs->mult >>= 1; 
  33.         cs->shift--; 
  34.         cs->maxadj = clocksource_max_adjustment(cs); 
  35.     } 
  36.   
  37.     cs->max_idle_ns = clocksource_max_deferment(cs); 

 

這個600的意思是600秒,表示的Timer兩次計(jì)算當(dāng)前計(jì)數(shù)值的差不會超過10分鐘。主要考慮的是系統(tǒng)進(jìn)入IDLE狀態(tài)之后,時間信息不會被更新,10分鐘內(nèi)只要退出IDLE,clocksource還是可以成功的轉(zhuǎn)換時間。當(dāng)然了,最后的這個時間不一定就是10分鐘,它由clocksource_max_deferment計(jì)算并將結(jié)果存儲在max_idle_ns中。

#p#

筒子比較關(guān)心的問題是如何計(jì)算,精度如何,其實(shí)我不太喜歡這種計(jì)算,Kernel總是因?yàn)槟承┰虬汛a寫的很蛋疼。反正揣摩代碼意圖要花不少時間,收益嘛其實(shí)也不太大.如何實(shí)現(xiàn)我也不解釋了,我以TSC為例子我評估下這種mult+shift的精度。

 

  1. #include<stdio.h> 
  2. #include<stdlib.h> 
  3.   
  4. typedef unsigned int u32; 
  5. typedef unsigned long long u64; 
  6.   
  7. #define NSEC_PER_SEC 1000000000L 
  8.   
  9. void 
  10. clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec) 
  11.     u64 tmp; 
  12.     u32 sft, sftacc32
  13.   
  14.     /* 
  15.      * * Calculate the shift factor which is limiting the conversion 
  16.      * * range: 
  17.      * */ 
  18.     tmp = ((u64)maxsec * from) >> 32; 
  19.     while (tmp) { 
  20.             tmp >>=1; 
  21.             sftacc--; 
  22.         } 
  23.   
  24.     /* 
  25.      * * Find the conversion shift/mult pair which has the best 
  26.      * * accuracy and fits the maxsec conversion range: 
  27.      * */ 
  28.     for (sft = 32; sft > 0; sft--) { 
  29.             tmp = (u64) to << sft
  30.             tmp += from / 2; 
  31.             //do_p(tmp, from); 
  32.             tmptmp = tmp/from; 
  33.             if ((tmp >> sftacc) == 0) 
  34.                 break; 
  35.         } 
  36.     *mult = tmp
  37.     *shift = sft
  38.   
  39.   
  40. int main() 
  41. {  
  42.     u32 tsc_mult; 
  43.     u32 tsc_shift ; 
  44.   
  45.     u32 tsc_frequency = 2127727000/1000; //TSC frequency(KHz) 
  46.     clocks_calc_mult_shift(&tsc_mult,&tsc_shift,tsc_frequency,NSEC_PER_SEC/1000,600*1000); //NSEC_PER_SEC/1000是因?yàn)門SC的注冊是clocksource_register_khz 
  47.   
  48.     fprintf(stderr,"mult = %d shift = %d\n",tsc_mult,tsc_shift); 
  49.     return 0; 

 

600是根據(jù)TSC clocksource的MASK算出來的的入?yún)?,感興趣可以自己推算看下結(jié)果:

 

  1. mult = 7885042 shift = 24 
  2. root@manu:~/code/c/self/time# python 
  3. Python 2.7.3 (default, Apr 10 2013, 05:46:21)  
  4. [GCC 4.6.3] on linux2 
  5. Type "help", "copyright", "credits" or "license" for more information. 
  6. >>> (2127727000*7885042)>>24 
  7. 1000000045L 
  8. >>>  

 

我們知道TSC的frequency是2127727000Hz,如果cycle走過2127727000,就意味過去了1秒,或者說10^9(us)。按照我們的算法得出的時間是1000000045us.。這個誤差是多大呢,每走10^9秒,誤差是45秒,換句話說,運(yùn)行257天,產(chǎn)生1秒的計(jì)算誤差。考慮到NTP的存在,這個運(yùn)算精度還可以了。

接下來是注冊和各大clocksource PK。

各大clocksource會調(diào)用clocksource_register_khz或者clocksource_register_hz來注冊。

 

  1. HPET (arch/x86/kernel/hpet) 
  2. ---------------------------------------- 
  3. hpet_enable 
  4. |_____hpet_clocksource_register 
  5.            |_____clocksource_register_hz 
  6.   
  7. TSC  (arch/x86/kernel/tsc.c) 
  8. ---------------------------------------- 
  9. device_initcall(init_tsc_clocksource); 
  10.   
  11. init_tsc_clocksource 
  12. |_____clocksource_register_khz 
  13.   
  14.   
  15. ACPI_PM(drivers/cloclsource/acpi_pm.c) 
  16. ------------------------------------------- 
  17. fs_initcall(init_acpi_pm_clocksource); 
  18.   
  19. init_acpi_pm_clocksource 
  20. |_____clocksource_register_hz 

 

最終都會調(diào)用__clocksource_register_scale. 

 

  1. int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq) 
  2.   
  3.     /* Initialize mult/shift and max_idle_ns */ 
  4.     __clocksource_updatefreq_scale(cs, scale, freq); 
  5.   
  6.     /* Add clocksource to the clcoksource list */ 
  7.     mutex_lock(&clocksource_mutex); 
  8.     clocksource_enqueue(cs); 
  9.     clocksource_enqueue_watchdog(cs); 
  10.     clocksource_select(); 
  11.     mutex_unlock(&clocksource_mutex); 
  12.     return 0; 

 

第一函數(shù)是__clocksource_updatefreq_scale,計(jì)算shift,mult還有max_idle_ns,前面講過了。

clocksource_enqueue是將clocksource鏈入全局鏈表,根據(jù)的是rating,rating高的放前面。

clocksource_select會選擇最好的clocksource記錄在全局變量curr_clocksource,同時會通知timekeeping,切換最好的clocksource會有內(nèi)核log:   

 

  1. manu@manu:~$ dmesg|grep Switching 
  2. [ 0.673002] Switching to clocksource hpet 
  3. [ 1.720643] Switching to clocksource tsc 

 

clocksource_enqueue_watchdog會將clocksource掛到watchdog鏈表。watchdog顧名思義,監(jiān)控所有clocksource:

 

  1. #define WATCHDOG_INTERVAL (HZ >> 1) 
  2. #define WATCHDOG_THRESHOLD (NSEC_PER_SEC >> 4) 

 

如果0.5秒內(nèi),誤差大于0.0625s,表示這個clocksource精度極差,將rating設(shè)成0。

責(zé)任編輯:奔跑的冰淇淋 來源: ChinaUnix
相關(guān)推薦

2013-10-11 13:31:10

2020-07-07 11:01:04

Linux工具命令

2018-12-12 09:44:07

Linux命令行時間管理

2023-02-23 09:02:40

CIO領(lǐng)域管理

2021-03-18 13:00:51

JupyterPython編程語言

2012-07-31 09:55:50

時間管理管理

2023-07-03 22:31:40

2013-03-14 17:17:34

2020-02-24 11:11:10

IT企業(yè)技術(shù)

2022-01-13 13:24:16

工具底層邏輯

2012-07-27 10:17:05

開發(fā)

2020-05-06 11:10:28

Python代碼開發(fā)

2012-04-12 14:49:31

程序員

2012-09-26 09:52:57

項(xiàng)目項(xiàng)目回顧時間管理

2012-05-14 17:42:46

ibmdw

2021-05-08 15:14:50

鴻蒙HarmonyOS應(yīng)用

2009-12-04 10:00:31

無線路由器功能介紹

2009-10-27 11:30:00

系統(tǒng)集成項(xiàng)目管理師試題答案

2011-01-11 13:53:33

Linux管理磁盤

2011-01-11 13:47:27

Linux管理進(jìn)程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號