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

Linux時間管理之hardware

系統(tǒng) Linux
一直以來對Linux下的時間管理知之不詳,GFree_wind在微博發(fā)起過幾次Linux下時鐘的討論,和Godbach這些大牛比,我完全插不上話,因為不懂。近來閑暇時間研究了下Linux下的時間管理,分享出來,請大家指正。

一直以來對Linux下的時間管理知之不詳,GFree_wind在微博發(fā)起過幾次Linux下時鐘的討論,和Godbach這些大牛比,我完全插不上話,因為不懂。近來閑暇時間研究了下Linux下的時間管理,分享出來,請大家指正。

從我們大白話的角度想,時間管理其實分成兩部分,就像我們小時候?qū)W習物理的時候物理老師不斷強調(diào)時間和時刻的區(qū)別。一個是時刻,比如現(xiàn)在是20:44:37秒,指的是時刻,我們手機上看時間,指的也是時刻。另一塊是時間,比如說,我每天工作八小時,再比如說,半小時之后,我要出門了,結(jié)束時間指向的是未來,但是仍然是一段時間。OK。無論是時刻還是時望間,都是需要硬件支持的,你手里只有一塊最小刻度只有1秒的手表,就不要指用這塊手表給百米大賽度量成績了,何哉,硬件太挫。Linux也是如此,之所以Linux啟動之后,可以精確的計時,那是因為Linux的下面有相應的硬件為依托。  

RTC


RTC,real time clock,實時時鐘和其他的硬件是不同的,RTC吐出來的是時刻,而其他硬件時鐘吐出來的是時間。也就是說,RTC能告訴我們,當前是2013年9月12日,21:49:38,但是其他的硬件如TSC,PIT,HPET只能告訴我們,我應該走過了XX個cycle,按照我的頻率,已經(jīng)過去了10分鐘了。

為啥RTC這么牛X,可以告訴我們當前時刻,哪怕用戶關(guān)了機?以X86為例,RTC是主板上的一塊CMOS芯片,哪怕你的Linux關(guān)了機,她也可以依賴主板上的電池維持時鐘的準確。當然了,在Linux下,RTC存儲的是UTC時間,而不會考慮timezone。

所以,Linux啟動的時候,一定會拜訪RTC來獲得當前的時刻值,盡管精度不高(精確到秒)。When and How?

首先回答When。Linux啟動的時候,start_kernel有四大time相關(guān)的函數(shù)調(diào)用:

  1. init_timers();
  2. hrtimers_init();
  3. timekeeping_init()
  4. time_init();

從RTC中讀取當前的UTC時間是timekeeping_init中做的事情,調(diào)用路徑如下:

 timekeeping_init

|___________read_persistent_clock    (arch/x86/kernel/rtc.c)

            |_____x86_platform.get_wallclock()

            |_____mach_get_cmos_time  (arch/x86/kernel/x86_init.c)

 

  1. /************arch/x86/kernel/rtc.c*****************/ 
  2. void read_persistent_clock(struct timespec *ts) 
  3.     unsigned long retval; 
  4.   
  5.     retval = x86_platform.get_wallclock(); 
  6.   
  7.     ts->tv_sec = retval
  8.     ts->tv_nsec = 0
  9.   
  10. /*****************arch/x86/kernel/x86_init.c ****************/ 
  11.   
  12. struct x86_platform_ops x86_platform = { 
  13.     .calibrate_tsc = native_calibrate_tsc
  14.     .wallclock_init = wallclock_init_noop
  15.     .get_wallclock = mach_get_cmos_time
  16.     .set_wallclock = mach_set_rtc_mmss
  17.     .iommu_shutdown = iommu_shutdown_noop
  18.     .is_untracked_pat_range = is_ISA_range
  19.     .nmi_init = default_nmi_init
  20.     .get_nmi_reason = default_get_nmi_reason
  21.     .i8042_detect = default_i8042_detect
  22.     .save_sched_clock_state = tsc_save_sched_clock_state
  23.     .restore_sched_clock_state = tsc_restore_sched_clock_state

對于我們而言,我們要讀的function是mach_get_cmos_time

  1. unsigned long mach_get_cmos_time(void) 
  2.     unsigned int status, year, mon, day, hour, min, sec, century = 0
  3.     unsigned long flags; 
  4.   
  5.     spin_lock_irqsave(&rtc_lock, flags); 
  6.   
  7.     /* 
  8.      * If UIP is clear, then we have >= 244 microseconds before 
  9.      * RTC registers will be updated. Spec sheet says that this 
  10.      * is the reliable way to read RTC - registers. If UIP is set 
  11.      * then the register access might be invalid. 
  12.      */ 
  13.     while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) 
  14.         cpu_relax(); 
  15.   
  16.     sec = CMOS_READ(RTC_SECONDS); 
  17.     min = CMOS_READ(RTC_MINUTES); 
  18.     hour = CMOS_READ(RTC_HOURS); 
  19.     day = CMOS_READ(RTC_DAY_OF_MONTH); 
  20.     mon = CMOS_READ(RTC_MONTH); 
  21.     year = CMOS_READ(RTC_YEAR); 
  22.   
  23. #ifdef CONFIG_ACPI 
  24.     if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && 
  25.      acpi_gbl_FADT.century) 
  26.         century = CMOS_READ(acpi_gbl_FADT.century); 
  27. #endif 
  28.   
  29.     status = CMOS_READ(RTC_CONTROL); 
  30.     WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY)); 
  31.   
  32.     spin_unlock_irqrestore(&rtc_lock, flags); 
  33.   
  34.     if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) { 
  35.         sec = bcd2bin(sec); 
  36.         min = bcd2bin(min); 
  37.         hour = bcd2bin(hour); 
  38.         day = bcd2bin(day); 
  39.         mon = bcd2bin(mon); 
  40.         year = bcd2bin(year); 
  41.     } 
  42.   
  43.     if (century) { 
  44.         century = bcd2bin(century); 
  45.         year += century * 100; 
  46.         printk(KERN_INFO "Extended CMOS year: %d\n", century * 100); 
  47.     } else 
  48.         year += CMOS_YEARS_OFFS; 
  49.   
  50.     return mktime(year, mon, day, hour, min, sec); 

這一段代碼已經(jīng)牽扯到了硬件相關(guān)的編程,我們其實并不關(guān)心驅(qū)動,對于硬件比較感興趣的筒子,可以訪問http://xenyinzen.wikidot.com/reship:080225-2

獲得更多更詳細的信息。mktime是將年月日時分秒組裝成1970年1月1日00:00:00這個UNIX基準時間以來的秒數(shù)。我們在Linux下可以通過一下方式獲得這個值:

  1. root@manu:/sys/class/rtc/rtc0# date +%s ;cat /sys/class/rtc/rtc0/since_epoch  
  2. 1379081060 
  3. 1379081060 

既然Linux上電的時候,可以從RTC中讀出當前的時間,我們也可以設置時間,并且寫入到RTC。用戶層也可以操作RTC硬件時鐘,通過ioctl。下面給出一個樣例: 

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <linux/rtc.h>  
  4. #include <fcntl.h> 
  5. #include <sys/ioctl.h> 
  6.   
  7. int main(int argc,char *argv[]) 
  8.     int retval,fd; 
  9.     struct rtc_time rtc_tm; 
  10.   
  11.     fd=open("/dev/rtc",O_RDONLY);  
  12.     if(fd==-1) 
  13.     { 
  14.         perror("error open /dev/rtc"); 
  15.         return -1; 
  16.     } 
  17.   
  18.     retval=ioctl(fd,RTC_RD_TIME,&rtc_tm); 
  19.     if(retval==-1) 
  20.     { 
  21.         perror("error exec RTC_RD_TIME ioctl"); 
  22.         return -2; 
  23.     } 
  24.     printf("RTC time is %d-%d-%d %d:%d:%d \n", 
  25.            rtc_tm.tm_year+1900,rtc_tm.tm_mon,rtc_tm.tm_mday, 
  26.            rtc_tm.tm_hour,rtc_tm.tm_min,rtc_tm.tm_sec); 
  27.   
  28.     close(fd); 
  29.   
  30.     return 0; 

輸出如下:   

  1. root@manu:~/code/c/self/rtc# ./rtc_test  
  2. RTC time is 2013-8-14 15:46:2 

對于set RTC 也可以通過ioctl RTC_SET_TIME參數(shù)來實現(xiàn),我就不多說了,對這部分感興趣的,可以自行man rtc,或者Kernel的Documentation/rtc.txt有一個示例代碼,比較詳細。

已經(jīng)說過了,RTC在時間相關(guān)的硬件中是個獨樹一幟的奇葩,作用和其他的硬件不同。而其他的硬件只是以一定的頻率產(chǎn)生時鐘中斷,幫助OS完成計時。前面我也提到過,你手里拿著個手表,就不要指望給百米大賽計時,原因就是精度太低。硬件也是如此,有精度高的有精度低的。Linux操作系統(tǒng)抽象出了clocksource(時鐘源)來管理這些硬件。Linux會在所有的硬件時鐘中選擇出精度最高作為當前在用的時鐘源。

如何查看當前所有的可用的時鐘源已經(jīng)當前在用的時鐘源呢? 

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

我們分別介紹hpet,acpi_pm,tsc,不過在這之前,先介紹一個PIT

#p#

PIT


PIT全稱Programmable Interval Timer,是出現(xiàn)比較早的,比較菜的硬件。這種設備有8253/8254,對底層感興趣的可以讀drivers/clocksource/i8253.c,這種硬件的頻率是1MHZ左右:

  1. #define PIT_TICK_RATE 1193182ul 

PIT為啥沒有落在available_clocksource中呢,因為后起之秀HPET的存在。Kernel中發(fā)現(xiàn)可以使用HPET,就不會用PIT作為始終源了。后面我們會分析到。

HPET


PIT 的精度較低,HPET 被設計來替代 PIT 提供高精度時鐘中斷(至少 10MHz)。它是由微軟和 Intel 聯(lián)合開發(fā)的。一個 HPET 包括了一個固定頻率的數(shù)值增加的計數(shù)器以及 3 到 32 個獨立的計時器,這每一個計時器有包涵了一個比較器和一個寄存器(保存一個數(shù)值,表示觸發(fā)中斷的時機)。每一個比較器都比較計數(shù)器中的數(shù)值和寄存器的數(shù)值,相等就會產(chǎn)生中斷。

HPET這個時鐘源的檢測和注冊是在前文提到的四大初始化中的最后一個:time_init   

 time_init

|________________x86_late_time_init

                 |_________x86_init.timers.timer_init (arch/x86/kernel/x86_init.c)

                           |________hpet_time_init

                                    |_____hpet_enable

                                          |____hpet_clocksource_register

                                    |_____set_default_time_irq 

|________________tsc_init

                 |________x86_platform.calibrate_tsc (x86_init.c)

                        |______native_calibrate_tsc

                               |___quit_pit_calibrate

在這個時候我們看到,time_init主要是兩個部分,TSC是一個,HPET(PIT)是一個。

  1. static struct irqaction irq0 = { 
  2.     .handler = timer_interrupt
  3.     .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_IRQPOLL | IRQF_TIMER, 
  4.     .name = "timer" 
  5. }; 
  6.   
  7. void __init setup_default_timer_irq(void) 
  8.     setup_irq(0, &irq0); 
  9.   
  10. /* Default timer init function */ 
  11. void __init hpet_time_init(void) 
  12.     if (!hpet_enable()
  13.         setup_pit_timer()
  14.     setup_default_timer_irq(); 
  15.   
  16. static __init void x86_late_time_init(void) 
  17.     x86_init.timers.timer_init();  
  18.     tsc_init(); //TSC part 
  19.   
  20. /* 
  21.  * Initialize TSC and delay the periodic timer init to 
  22.  * late x86_late_time_init() so ioremap works. 
  23.  */ 
  24. void __init time_init(void) 
  25.     late_time_init = x86_late_time_init

從上面加粗的部分可以看到,當HPET可以enable的時候,我們就不用PIT作為時鐘源了,原因是HPET頻率高,精度高。

從我的筆記本Linux的dmesg可看到:

 [ 0.664201] hpet0: 8 comparators, 64-bit 14.318180 MHz counter

我的筆記本的HPET,頻率是14.318180MHz。

講到這里,就不得不講clocksource。Linux將真實的時鐘做了抽象,用數(shù)據(jù)結(jié)構(gòu)clocksource來管理這些硬件時鐘源。

  1. /** 
  2.  * struct clocksource - hardware abstraction for a free running counter 
  3.  *    Provides mostly state-free accessors to the underlying hardware. 
  4.  *    This is the structure used for system time. 
  5.  * 
  6.  * @name:        ptr to clocksource name 
  7.  * @list:        list head for registration 
  8.  * @rating:        rating value for selection (higher is better) 
  9.  *            To avoid rating inflation the following 
  10.  *            list should give you a guide as to how 
  11.  *            to assign your clocksource a rating 
  12.  *            1-99: Unfit for real use 
  13.  *                Only available for bootup and testing purposes. 
  14.  *            100-199: Base level usability. 
  15.  *                Functional for real use, but not desired. 
  16.  *            200-299: Good. 
  17.  *                A correct and usable clocksource. 
  18.  *            300-399: Desired. 
  19.  *                A reasonably fast and accurate clocksource. 
  20.  *            400-499: Perfect 
  21.  *                The ideal clocksource. A must-use where 
  22.  *                available. 
  23.  * @read:        returns a cycle value, passes clocksource as argument 
  24.  * @enable:        optional function to enable the clocksource 
  25.  * @disable:        optional function to disable the clocksource 
  26.  * @mask:        bitmask for two's complement 
  27.  *            subtraction of non 64 bit counters 
  28.  * @mult:        cycle to nanosecond multiplier 
  29.  * @shift:        cycle to nanosecond pisor (power of two) 
  30.  * @max_idle_ns:    max idle time permitted by the clocksource (nsecs) 
  31.  * @maxadj:        maximum adjustment value to mult (~11%) 
  32.  * @flags:        flags describing special properties 
  33.  * @archdata:        arch-specific data 
  34.  * @suspend:        suspend function for the clocksource, if necessary 
  35.  * @resume:        resume function for the clocksource, if necessary 
  36.  * @cycle_last:        most recent cycle counter value seen by ::read() 
  37.  */ 
  38. struct clocksource { 
  39.     /* 
  40.      * Hotpath data, fits in a single cache line when the 
  41.      * clocksource itself is cacheline aligned. 
  42.      */ 
  43.     cycle_t (*read)(struct clocksource *cs); 
  44.     cycle_t cycle_last; 
  45.     cycle_t mask; 
  46.     u32 mult; 
  47.     u32 shift; 
  48.     u64 max_idle_ns; 
  49.     u32 maxadj; 
  50. #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA 
  51.     struct arch_clocksource_data archdata; 
  52. #endif 
  53.   
  54.     const char *name; 
  55.     struct list_head list; 
  56.     int rating; 
  57.     int (*enable)(struct clocksource *cs); 
  58.     void (*disable)(struct clocksource *cs); 
  59.     unsigned long flags; 
  60.     void (*suspend)(struct clocksource *cs); 
  61.     void (*resume)(struct clocksource *cs); 
  62.   
  63.     /* private: */ 
  64. #ifdef CONFIG_CLOCKSOURCE_WATCHDOG 
  65.     /* Watchdog related data, used by the framework */ 
  66.     struct list_head wd_list; 
  67.     cycle_t cs_last; 
  68.     cycle_t wd_last; 
  69. #endif 
  70. } ____cacheline_aligned;     

很重要的一個參數(shù)是rating時鐘源分優(yōu)劣,精度越高的時鐘源的,rating值越大。從注釋中我們可以看到:

  • 1--99: 不適合于用作實際的時鐘源,只用于啟動過程或用于測試;
  • 100--199:基本可用,可用作真實的時鐘源,但不推薦;
  • 200--299:精度較好,可用作真實的時鐘源;
  • 300--399:很好,精確的時鐘源;
  • 400--499:理想的時鐘源,如有可能就必須選擇它作為時鐘源;
  1. static struct clocksource clocksource_hpet = { 
  2.     .name = "hpet"
  3.     .rating = 250
  4.     .read = read_hpet
  5.     .mask = HPET_MASK
  6.     .flags = CLOCK_SOURCE_IS_CONTINUOUS
  7.     .resume = hpet_resume_counter
  8. #ifdef CONFIG_X86_64 
  9.     .archdata = { .vclock_mode = VCLOCK_HPET }, 
  10. #endif 
  11. }; 

從rating上HPET的rating是250,已經(jīng)很不錯了,為什么最終選擇了TSC,available_clocksource中的acpi_pm又是什么?    

#p#

ACPI_PM


這個是傳說中的ACPI Power Management Time,這個其實我也知之不詳,對這個感興趣的可以去找下CU的彭東,這小子寫OS,應該會經(jīng)常和這種硬件糾纏不清。到了硬件驅(qū)動層,我水平基本溫飽線以下。

  1. #define PMTMR_TICKS_PER_SEC 3579545 
  2.   
  3.  66 static struct clocksource clocksource_acpi_pm = { 
  4.           .name = "acpi_pm"
  5.           .rating = 200
  6.           .read = acpi_pm_read
  7.           .mask = (cycle_t)ACPI_PM_MASK, 
  8.           .mult = 0, /*to be calculated*/ 
  9.           .shift = 22
  10.           .flags = CLOCK_SOURCE_IS_CONTINUOUS
  11.   
  12.  }; 
  13. fs_initcall(init_acpi_pm_clocksource) 

我們看到了頻率在3Mhz這個級別,rating是200,低于HPET。至于初始化在fs_initcall這一步做。

TSC


TSC是Time Stamp Counter。CPU 執(zhí)行指令需要一個外部振蕩器產(chǎn)生時鐘信號,從 CLK 管腳輸入。x86 提供了一個 TSC 寄存器,該寄存器的值在每次收到一個時鐘信號時加一。比如 CPU 的主頻為 1GHZ,則每一秒時間內(nèi),TSC 寄存器的值將增加 1G 次,或者說每一個納秒加一次。x86 還提供了 rtdsc 指令來讀取該值,因此 TSC 也可以作為時鐘設備。TSC 提供了比 RTC 更高精度的時間,即納秒級的時間精度。這個很牛X,看時鐘頻率是和CPU的頻率一個水平線的。遠遠超過HPET,PIT這些小魚小蝦米??聪挛业墓P記本的TSC 頻率:

  1. manu@manu:~/code/c/classical/linux-3.4.61$ dmesg |grep Detected 
  2. [ 0.004000] Detected 2127.727 MHz processor. 
  3.   
  4. manu@manu:~$ cat /proc/cpuinfo  
  5. processor   : 0 
  6. vendor_id   : GenuineIntel 
  7. cpu family  : 6 
  8. model    : 37 
  9. model name  : Intel(R) Core(TM) i3 CPU       M 330  @ 2.13GHz 
  10.   
  11. 。。。。。 

看這時鐘頻率,相當?shù)膰樔?,看下clocksource的rating:

  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. }; 

TSC的init和register分別在tsc_init和init_tsc_clocksource中進行

 time_init

|________________x86_late_time_init

                 |_________x86_init.timers.timer_init (arch/x86/kernel/x86_init.c)

                           |________hpet_time_init

                                    |_____hpet_enable

                                          |____hpet_clocksource_register

                                    |_____set_default_time_irq 

|________________tsc_init

                 |________x86_platform.calibrate_tsc (x86_init.c)

                        |______native_calibrate_tsc

                               |___quit_pit_calibrate

 

 

  1. static int __init init_tsc_clocksource(void) 
  2.     if (!cpu_has_tsc || tsc_disabled > 0 || !tsc_khz) 
  3.         return 0; 
  4.  
  5.     if (tsc_clocksource_reliable) 
  6.         clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY; 
  7.     /* lower the rating if we already know its unstable: */ 
  8.     if (check_tsc_unstable()) { 
  9.         clocksource_tsc.rating = 0
  10.         clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS; 
  11.     } 
  12.  
  13.     /* 
  14.      * Trust the results of the earlier calibration on systems 
  15.      * exporting a reliable TSC. 
  16.      */ 
  17.     if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE)) { 
  18.         clocksource_register_khz(&clocksource_tsc, tsc_khz); 
  19.         return 0; 
  20.     } 
  21.   
  22.     schedule_delayed_work(&tsc_irqwork, 0); 
  23.     return 0; 
  24. /* 
  25.  * We use device_initcall here, to ensure we run after the hpet 
  26.  * is fully initialized, which may occur at fs_initcall time. 
  27.  */ 
  28. device_initcall(init_tsc_clocksource); 

所以,clocksource 的PK之戰(zhàn)中,TSC技壓群雄,完爆HPET/PIT/ACPI_PM,成為current_clocksource。clocksource之戰(zhàn)如何進行,如何選擇當前時鐘源,新的時鐘源注冊會帶來什么影響。Linux如何根據(jù)時鐘源完成計時,這個就在下一篇clocksource中介紹。

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

2013-10-11 14:18:54

2020-07-07 11:01:04

Linux工具命令

2023-02-23 09:02:40

CIO領(lǐng)域管理

2018-12-12 09:44:07

Linux命令行時間管理

2023-07-03 22:31:40

2013-03-14 17:17:34

2021-03-18 13:00:51

JupyterPython編程語言

2012-07-31 09:55:50

時間管理管理

2020-02-24 11:11:10

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

2022-01-13 13:24:16

工具底層邏輯

2020-05-06 11:10:28

Python代碼開發(fā)

2012-04-12 14:49:31

程序員

2012-09-26 09:52:57

項目項目回顧時間管理

2012-07-27 10:17:05

開發(fā)

2009-12-04 10:00:31

無線路由器功能介紹

2012-05-14 17:42:46

ibmdw

2021-05-08 15:14:50

鴻蒙HarmonyOS應用

2009-10-27 11:30:00

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

2023-01-17 16:05:50

程序員時間管理日程表

2019-02-27 11:10:29

時間管理軟件應用
點贊
收藏

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