ºìÁªLinuxÃÅ»§
Linux°ïÖú

LinuxÄں˵ÄʱÖÓÖжϻúÖÆ

·¢²¼Ê±¼ä:2006-08-21 00:24:15À´Ô´:ºìÁª×÷Õß:hfh08
ÕªÒª£º±¾ÎÄÖ÷Òª´ÓÄÚºËʵÏֵĽǶȷÖÎöÁËLinux 2.4.0Äں˵ÄʱÖÓÖжϡ¢Äں˶Ôʱ¼äµÄ±íʾµÈ¡£±¾ÎÄÊÇΪÄÇЩÏëÒªÁ˽âLinux I/O×ÓϵͳµÄ¶ÁÕߺÍLinuxÇý¶¯³ÌÐò¿ª·¢ÈËÔ±¶øдµÄ¡£
¹Ø¼ü´Ê£ºLinux¡¢Ê±ÖÓ¡¢¶¨Ê±Æ÷

ÉêÃ÷£ºÕâ·ÝÎĵµÊÇ°´ÕÕ×ÔÓÉÈí¼þ¿ª·ÅÔ´´úÂëµÄ¾«Éñ·¢²¼µÄ£¬ÈκÎÈË¿ÉÒÔÃâ·Ñ»ñµÃ¡¢Ê¹ÓúÍÖØз¢²¼£¬µ«ÊÇÄãûÓÐÏÞÖƱðÈËÖØз¢²¼Äã·¢²¼ÄÚÈݵÄȨÀû¡£·¢²¼±¾ÎĵÄÄ¿µÄÊÇÏ£ÍûËüÄܶԶÁÕßÓÐÓ㬵«Ã»ÓÐÈκε£±££¬ÉõÖÁûÓÐÊʺÏÌض¨Ä¿µÄµÄÒþº¬µÄµ£±£¡£¸üÏêϸµÄÇé¿öÇë²ÎÔÄGNUͨÓù«¹²Ðí¿ÉÖ¤(GPL)£¬ÒÔ¼°GNU×ÔÓÉÎĵµÐ­Òé(GFDL)¡£

ÄãÓ¦¸ÃÒѾ­ºÍÎĵµÒ»ÆðÊÕµ½Ò»·ÝGNUͨÓù«¹²Ðí¿ÉÖ¤(GPL)µÄ¸±±¾¡£Èç¹û»¹Ã»ÓУ¬Ð´ÐŸø£º
The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA

»¶Ó­¸÷λָ³öÎĵµÖеĴíÎóÓëÒÉÎÊ¡£
Ç°ÑÔ
ʱ¼äÔÚÒ»¸ö²Ù×÷ϵͳÄÚºËÖÐÕ¼¾Ý×ÅÖØÒªµÄµØ룬ËüÊÇÇý¶¯Ò»¸öOSÄÚºËÔËÐеġ°Æð²©Æ÷¡±¡£Ò»°ã˵À´£¬ÄÚºËÖ÷ÒªÐèÒªÁ½ÖÖÀàÐ͵Äʱ¼ä£º
1. ÔÚÄÚºËÔËÐÐÆÚ¼ä³ÖÐø¼Ç¼µ±Ç°µÄʱ¼äÓëÈÕÆÚ£¬ÒÔ±ãÄں˶ÔijЩ¶ÔÏóºÍʼþ×÷ʱ¼ä±ê¼Ç£¨timestamp£¬Ò²³ÆΪ¡°Ê±¼ä´Á¡±£©£¬»ò¹©Óû§Í¨¹ýʱ¼äsyscall½øÐмìË÷¡£
2. ά³ÖÒ»¸ö¹Ì¶¨ÖÜÆڵĶ¨Ê±Æ÷£¬ÒÔÌáÐÑÄں˻òÓû§Ò»¶Îʱ¼äÒѾ­¹ýÈ¥ÁË¡£
PC»úÖеÄʱ¼äÊÇÓÐÈýÖÖʱÖÓÓ²¼þÌṩµÄ£¬¶øÕâЩʱÖÓÓ²¼þÓÖ¶¼»ùÓڹ̶¨ÆµÂʵľ§ÌåÕñµ´Æ÷À´ÌṩʱÖÓ·½²¨ÐźÅÊäÈë¡£ÕâÈýÖÖʱÖÓÓ²¼þÊÇ£º£¨1£©ÊµÊ±Ê±ÖÓ£¨Real Time Clock£¬RTC£©£»£¨2£©¿É±à³Ì¼ä¸ô¶¨Ê±Æ÷£¨Programmable Interval Timer£¬PIT£©£»£¨3£©Ê±¼ä´Á¼ÆÊýÆ÷£¨Time Stamp Counter£¬TSC£©¡£

7£®1 ʱÖÓÓ²¼þ
7£®1£®1 ʵʱʱÖÓRTC
×Ô´ÓIBM PC ATÆð£¬ËùÓеÄPC»ú¾Í¶¼°üº¬ÁËÒ»¸ö½Ð×öʵʱʱÖÓ£¨RTC£©µÄʱÖÓоƬ£¬ÒÔ±ãÔÚPC»ú¶ÏµçºóÈÔÈ»Äܹ»¼ÌÐø±£³Öʱ¼ä¡£ÏÔÈ»£¬RTCÊÇͨ¹ýÖ÷°åÉϵĵç³ØÀ´¹©µçµÄ£¬¶ø²»ÊÇͨ¹ýPC»úµçÔ´À´¹©µçµÄ£¬Òò´Ëµ±PC»ú¹ØµôµçÔ´ºó£¬RTCÈÔÈ»»á¼ÌÐø¹¤×÷¡£Í¨³££¬CMOS RAMºÍRTC±»¼¯³Éµ½Ò»¿éоƬÉÏ£¬Òò´ËRTCÒ²³Æ×÷¡°CMOS Timer¡±¡£×î³£¼ûµÄRTCоƬÊÇMC146818£¨Motorola£©ºÍDS12887£¨maxim£©£¬DS12887ÍêÈ«¼æÈÝÓÚMC146818£¬²¢ÓÐÒ»¶¨µÄÀ©Õ¹¡£±¾½ÚÄÚÈÝÖ÷Òª»ùÓÚMC146818ÕâÒ»±ê×¼µÄRTCоƬ¡£¾ßÌåÄÚÈÝ¿ÉÒԲο¼MC146818µÄDatasheet¡£

7£®1£®1£®1 RTC¼Ä´æÆ÷
MC146818 RTCоƬһ¹²ÓÐ64¸ö¼Ä´æÆ÷¡£ËüÃǵÄоƬÄÚ²¿µØÖ·±àºÅΪ0x00¡«0x3F£¨²»ÊÇI/O¶Ë¿ÚµØÖ·£©£¬ÕâЩ¼Ä´æÆ÷Ò»¹²¿ÉÒÔ·ÖΪÈý×飺
£¨1£©Ê±ÖÓÓëÈÕÀú¼Ä´æÆ÷×飺¹²ÓÐ10¸ö£¨0x00~0x09£©£¬±íʾʱ¼ä¡¢ÈÕÀúµÄ¾ßÌåÐÅÏ¢¡£ÔÚPC»úÖУ¬ÕâЩ¼Ä´æÆ÷ÖеÄÖµ¶¼ÊÇÒÔBCD¸ñʽÀ´´æ´¢µÄ£¨±ÈÈç23dec£½0x23BCD£©¡£
£¨2£©×´Ì¬ºÍ¿ØÖƼĴæÆ÷×飺¹²ÓÐ4¸ö£¨0x0A~0x0D£©£¬¿ØÖÆRTCоƬµÄ¹¤×÷·½Ê½£¬²¢±íʾµ±Ç°µÄ״̬¡£
£¨3£©CMOSÅäÖÃÊý¾Ý£ºÍ¨ÓõÄCMOS RAM£¬ËüÃÇÓëʱ¼äÎ޹أ¬Òò´ËÎÒÃDz»¹ØÐÄËü¡£
ʱÖÓÓëÈÕÀú¼Ä´æÆ÷×éµÄÏêϸ½âÊÍÈçÏ£º
Address Function
00 Current second for RTC
01 Alarm second
02 Current minute
03 Alarm minute
04 Current hour
05 Alarm hour
06 Current day of week£¨01£½Sunday£©
07 Current date of month
08 Current month
09 Current year£¨final two digits£¬eg£º93£©

״̬¼Ä´æÆ÷A£¨µØÖ·0x0A£©µÄ¸ñʽÈçÏ£º
ÆäÖУº
£¨1£©bit£Û7£Ý----UIP±êÖ¾£¨Update in Progress£©£¬Îª1±íʾRTCÕýÔÚ¸üÐÂÈÕÀú¼Ä´æÆ÷×éÖеÄÖµ£¬´ËʱÈÕÀú¼Ä´æÆ÷×éÊDz»¿É·ÃÎʵģ¨´Ëʱ·ÃÎÊËüÃǽ«µÃµ½Ò»¸öÎÞÒâÒåµÄ½¥±äÖµ£©¡£
£¨2£©bit£Û6£º4£Ý----ÕâÈýλÊÇ¡°³ý·¨Æ÷¿ØÖÆλ¡±£¨divider-control bits£©£¬ÓÃÀ´¶¨ÒåRTCµÄ²Ù×÷ƵÂÊ¡£¸÷ÖÖ¿ÉÄܵÄÖµÈçÏ£º
Divider bits Time-base frequency Divider Reset Operation Mode
DV2 DV1 DV0
0 0 0 4.194304 MHZ NO YES
0 0 1 1.048576 MHZ NO YES
0 1 0 32.769 KHZ NO YES
1 1 0/1 ÈκΠYES NO
PC»úͨ³£½«Divider bitsÉèÖóɡ°010¡±¡£
£¨3£©bit£Û3£º0£Ý----ËÙÂÊÑ¡Ôñ루Rate Selection bits£©£¬ÓÃÓÚÖÜÆÚÐÔ»ò·½²¨ÐźÅÊä³ö¡£
RS bits 4.194304»ò1.048578 MHZ 32.768 KHZ
RS3 RS2 RS1 RS0 ÖÜÆÚÐÔÖÐ¶Ï ·½²¨ ÖÜÆÚÐÔÖÐ¶Ï ·½²¨
0 0 0 0 None None None None
0 0 0 1 30.517¦Ìs 32.768 KHZ 3.90625ms 256 HZ
0 0 1 0 61.035¦Ìs 16.384 KHZ
0 0 1 1 122.070¦Ìs 8.192KHZ
0 1 0 0 244.141¦Ìs 4.096KHZ
0 1 0 1 488.281¦Ìs 2.048KHZ
0 1 1 0 976.562¦Ìs 1.024KHZ
0 1 1 1 1.953125ms 512HZ
1 0 0 0 3.90625ms 256HZ
1 0 0 1 7.8125ms 128HZ
1 0 1 0 15.625ms 64HZ
1 0 1 1 31.25ms 32HZ
1 1 0 0 62.5ms 16HZ
1 1 0 1 125ms 8HZ
1 1 1 0 250ms 4HZ
1 1 1 1 500ms 2HZ
PC»úBIOS¶ÔÆäĬÈϵÄÉèÖÃÖµÊÇ¡°0110¡±¡£

״̬¼Ä´æÆ÷BµÄ¸ñʽÈçÏÂËùʾ£º
¸÷λµÄº¬ÒåÈçÏ£º
£¨1£©bit£Û7£Ý----SET±êÖ¾¡£Îª1±íʾRTCµÄËùÓиüйý³Ì¶¼½«ÖÕÖ¹£¬Óû§³ÌÐòËæºóÂíÉ϶ÔÈÕÀú¼Ä´æÆ÷×éÖеÄÖµ½øÐгõʼ»¯ÉèÖá£Îª0±íʾ½«ÔÊÐí¸üйý³Ì¼ÌÐø¡£
£¨2£©bit£Û6£Ý----PIE±êÖ¾£¬ÖÜÆÚÐÔÖжÏʹÄܱêÖ¾¡£
£¨3£©bit£Û5£Ý----AIE±êÖ¾£¬¸æ¾¯ÖжÏʹÄܱêÖ¾¡£
£¨4£©bit£Û4£Ý----UIE±êÖ¾£¬¸üнáÊøÖжÏʹÄܱêÖ¾¡£
£¨5£©bit£Û3£Ý----SQWE±êÖ¾£¬·½²¨ÐźÅʹÄܱêÖ¾¡£
£¨6£©bit£Û2£Ý----DM±êÖ¾£¬ÓÃÀ´¿ØÖÆÈÕÀú¼Ä´æÆ÷×éµÄÊý¾Ýģʽ£¬0£½BCD£¬1£½BINARY¡£BIOS×ÜÊǽ«ËüÉèÖÃΪ0¡£
£¨7£©bit£Û1£Ý----24£¯12±êÖ¾£¬ÓÃÀ´¿ØÖÆhour¼Ä´æÆ÷£¬0±íʾ12СʱÖÆ£¬1±íʾ24СʱÖÆ¡£PC»úBIOS×ÜÊǽ«ËüÉèÖÃΪ1¡£
£¨8£©bit£Û0£Ý----DSE±êÖ¾¡£BIOS×ÜÊǽ«ËüÉèÖÃΪ0¡£

״̬¼Ä´æÆ÷CµÄ¸ñʽÈçÏ£º
£¨1£©bit£Û7£Ý----IRQF±êÖ¾£¬ÖжÏÇëÇó±êÖ¾£¬µ±¸ÃλΪ1ʱ£¬ËµÃ÷¼Ä´æÆ÷BÖжÏÇëÇó·¢Éú¡£
£¨2£©bit£Û6£Ý----PF±êÖ¾£¬ÖÜÆÚÐÔÖжϱêÖ¾£¬Îª1±íʾ·¢ÉúÖÜÆÚÐÔÖжÏÇëÇó¡£
£¨3£©bit£Û5£Ý----AF±êÖ¾£¬¸æ¾¯ÖжϱêÖ¾£¬Îª1±íʾ·¢Éú¸æ¾¯ÖжÏÇëÇó¡£
£¨4£©bit£Û4£Ý----UF±êÖ¾£¬¸üнáÊøÖжϱêÖ¾£¬Îª1±íʾ·¢Éú¸üнáÊøÖжÏÇëÇó¡£

״̬¼Ä´æÆ÷DµÄ¸ñʽÈçÏ£º
£¨1£©bit£Û7£Ý----VRT±êÖ¾£¨Valid RAM and Time£©£¬Îª1±íʾOK£¬Îª0±íʾRTCÒѾ­µôµç¡£
£¨2£©bit£Û6£º0£Ý----×ÜÊÇΪ0£¬Î´¶¨Òå¡£

7£®1£®1£®2 ͨ¹ýI/O¶Ë¿Ú·ÃÎÊRTC
ÔÚPC»úÖпÉÒÔͨ¹ýI/O¶Ë¿Ú0x70ºÍ0x71À´¶ÁдRTCоƬÖеļĴæÆ÷¡£ÆäÖУ¬¶Ë¿Ú0x70ÊÇRTCµÄ¼Ä´æÆ÷µØÖ·Ë÷Òý¶Ë¿Ú£¬0x71ÊÇÊý¾Ý¶Ë¿Ú¡£
¶ÁRTCоƬ¼Ä´æÆ÷µÄ²½ÖèÊÇ£º
mov al, addr
out 70h, al ; Select reg_addr in RTC chip
jmp $+2 ; a slight delay to settle thing
in al, 71h ;
дRTC¼Ä´æÆ÷µÄ²½ÖèÈçÏ£º
mov al, addr
out 70h, al ; Select reg_addr in RTC chip
jmp $+2 ; a slight delay to settle thing
mov al, value
out 71h, al

7£®1£®2 ¿É±à³Ì¼ä¸ô¶¨Ê±Æ÷PIT
ÿ¸öPC»úÖж¼ÓÐÒ»¸öPIT£¬ÒÔͨ¹ýIRQ0²úÉúÖÜÆÚÐÔµÄʱÖÓÖжÏÐźš£µ±Ç°Ê¹ÓÃ×îÆÕ±éµÄÊÇIntel 8254 PITоƬ£¬ËüµÄI/O¶Ë¿ÚµØÖ·ÊÇ0x40~0x43¡£
Intel 8254 PITÓÐ3¸ö¼ÆʱͨµÀ£¬Ã¿¸öͨµÀ¶¼ÓÐÆ䲻ͬµÄÓÃ;£º
£¨1£© ͨµÀ0ÓÃÀ´¸ºÔð¸üÐÂϵͳʱÖÓ¡£Ã¿µ±Ò»¸öʱÖӵδð¹ýȥʱ£¬Ëü¾Í»áͨ¹ýIRQ0Ïòϵͳ²úÉúÒ»´ÎʱÖÓÖжϡ£
£¨2£© ͨµÀ1ͨ³£ÓÃÓÚ¿ØÖÆDMAC¶ÔRAMµÄˢС£
£¨3£© ͨµÀ2±»Á¬½Óµ½PC»úµÄÑïÉùÆ÷£¬ÒÔ²úÉú·½²¨Ðźš£
ÿ¸öͨµÀ¶¼ÓÐÒ»¸öÏòϼõСµÄ¼ÆÊýÆ÷£¬8254 PITµÄÊäÈëʱÖÓÐźŵÄƵÂÊÊÇ1193181HZ£¬Ò²¼´Ò»ÃëÖÓÊäÈë1193181¸öclock-cycle¡£Ã¿ÊäÈëÒ»¸öclock-cycleÆäʱ¼äͨµÀµÄ¼ÆÊýÆ÷¾ÍÏòϼõ1£¬Ò»Ö±¼õµ½0Öµ¡£Òò´Ë¶ÔÓÚͨµÀ0¶øÑÔ£¬µ±ËûµÄ¼ÆÊýÆ÷¼õµ½0ʱ£¬PIT¾ÍÏòϵͳ²úÉúÒ»´ÎʱÖÓÖжϣ¬±íʾһ¸öʱÖӵδðÒѾ­¹ýÈ¥ÁË¡£µ±¸÷ͨµÀµÄ¼ÆÊýÆ÷¼õµ½0ʱ£¬ÎÒÃǾÍ˵¸ÃͨµÀ´¦ÓÚ¡°Terminal count¡±×´Ì¬¡£
ͨµÀ¼ÆÊýÆ÷µÄ×î´óÖµÊÇ10000h£¬Ëù¶ÔÓ¦µÄʱÖÓÖжÏƵÂÊÊÇ1193181£¯£¨65536£©£½18.2HZ£¬Ò²¾ÍÊÇ˵£¬´ËʱһÃëÖÓÖ®ÄÚ½«²úÉú18.2´ÎʱÖÓÖжϡ£

7£®1£®2£®1 PITµÄI/O¶Ë¿Ú
ÔÚi386ƽ̨ÉÏ£¬8254оƬµÄ¸÷¼Ä´æÆ÷µÄI/O¶Ë¿ÚµØÖ·ÈçÏ£º
Port Description
40h Channel 0 counter£¨read/write£©
41h Channel 1 counter£¨read/write£©
42h Channel 2 counter£¨read/write£©
43h PIT control word£¨write only£©
ÆäÖУ¬ÓÉÓÚͨµÀ0¡¢1¡¢2µÄ¼ÆÊýÆ÷ÊÇÒ»¸ö16λ¼Ä´æÆ÷£¬¶øÏàÓ¦µÄ¶Ë¿ÚÈ´¶¼ÊÇ8λµÄ£¬Òò´Ë¶ÁдͨµÀ¼ÆÊýÆ÷±ØÐë½øÐнøÐÐÁ½´ÎI/O¶Ë¿Ú¶Áд²Ù×÷£¬·Ö±ð¶ÔÓ¦ÓÚ¼ÆÊýÆ÷µÄ¸ß×ֽں͵Í×Ö½Ú£¬ÖÁÓÚÊÇÏȶÁд¸ß×Ö½ÚÔÙ¶ÁдµÍ×Ö½Ú£¬»¹ÊÇÏȶÁдµÍ×Ö½ÚÔÙ¶Áд¸ß×Ö½Ú£¬ÔòÓÉPITµÄ¿ØÖƼĴæÆ÷À´¾ö¶¨¡£8254 PITµÄ¿ØÖƼĴæÆ÷µÄ¸ñʽÈçÏ£º
£¨1£©bit£Û7£º6£Ý----Select Counter£¬Ñ¡Ôñ¶ÔÄǸö¼ÆÊýÆ÷½øÐвÙ×÷¡£¡°00¡±±íʾѡÔñCounter 0£¬¡°01¡±±íʾѡÔñCounter 1£¬¡°10¡±±íʾѡÔñCounter 2£¬¡°11¡±±íʾRead-Back Command£¨½ö¶ÔÓÚ8254£¬¶ÔÓÚ8253ÎÞЧ£©¡£
£¨2£©bit£Û5£º4£Ý----Read/Write/Latch¸ñʽλ¡£¡°00¡±±íʾËø´æ£¨Latch£©µ±Ç°¼ÆÊýÆ÷µÄÖµ£»¡°01¡±Ö»¶Áд¼ÆÊýÆ÷µÄ¸ß×Ö½Ú£¨MSB£©£»¡°10¡±Ö»¶Áд¼ÆÊýÆ÷µÄµÍ×Ö½Ú£¨LSB£©£»¡°11¡±±íʾÏȶÁд¼ÆÊýÆ÷µÄLSB£¬ÔÙ¶ÁдMSB¡£
£¨3£©bit£Û3£º1£Ý----Mode bits£¬¿ØÖƸ÷ͨµÀµÄ¹¤×÷ģʽ¡£¡°000¡±¶ÔÓ¦Mode 0£»¡°001¡±¶ÔÓ¦Mode 1£»¡°010¡±¶ÔÓ¦Mode 2£»¡°011¡±¶ÔÓ¦Mode 3£»¡°100¡±¶ÔÓ¦Mode 4£»¡°101¡±¶ÔÓ¦Mode 5¡£
£¨4£©bit£Û0£Ý----¿ØÖƼÆÊýÆ÷µÄ´æ´¢Ä£Ê½¡£0±íʾÒÔ¶þ½øÖƸñʽ´æ´¢£¬1±íʾ¼ÆÊýÆ÷ÖеÄÖµÒÔBCD¸ñʽ´æ´¢¡£

7£®1£®2£®2 PITͨµÀµÄ¹¤×÷ģʽ
PIT¸÷ͨµÀ¿ÉÒÔ¹¤×÷ÔÚÏÂÁÐ6ÖÖģʽÏ£º
1. Mode 0£ºµ±Í¨µÀ´¦ÓÚ¡°Terminal count¡±×´Ì¬Ê±²úÉúÖжÏÐźš£
2. Mode 1£ºHardware retriggerable one-shot¡£
3. Mode 2£ºRate Generator¡£ÕâÖÖģʽµäÐ͵ر»ÓÃÀ´²úÉúʵʱʱÖÓÖжϡ£´ËʱͨµÀµÄÐźÅÊä³ö¹Ü½ÅOUT³õʼʱ±»ÉèÖÃΪ¸ßµçƽ£¬²¢ÒԴ˳ÖÐøµ½¼ÆÊýÆ÷µÄÖµ¼õµ½1¡£È»ºóÔÚ½ÓÏÂÀ´µÄÕâ¸öclock-cycleÆڼ䣬OUT¹Ü½Å½«±äΪµÍµçƽ£¬Ö±µ½¼ÆÊýÆ÷µÄÖµ¼õµ½0¡£µ±¼ÆÊýÆ÷µÄÖµ±»×Ô¶¯µØÖØмÓÔغó£¬OUT¹Ü½ÅÓÖ±ä³É¸ßµçƽ£¬È»ºóÖظ´ÉÏÊö¹ý³Ì¡£Í¨µÀ0ͨ³£¹¤×÷ÔÚÕâ¸öģʽÏ¡£
4. Mode 3£º·½²¨Ðźŷ¢ÉúÆ÷¡£
5. Mode 4£ºSoftware triggered strobe¡£
6. Mode 5£ºHardware triggered strobe¡£

7£®1£®2£®3 Ëø´æ¼ÆÊýÆ÷£¨Latch Counter£©
µ±¿ØÖƼĴæÆ÷ÖеÄbit£Û5£º4£ÝÉèÖóÉ0ʱ£¬½«°Ñµ±Ç°Í¨µÀµÄ¼ÆÊýÆ÷ÖµËø´æ¡£´Ëʱͨ¹ýI/O¶Ë¿Ú¿ÉÒÔ¶Áµ½Ò»¸öÎȶ¨µÄ¼ÆÊýÆ÷Öµ£¬ÒòΪ¼ÆÊýÆ÷±íÃæÉÏÒѾ­Í£Ö¹ÏòϼÆÊý£¨PITоƬÄÚ²¿²¢Ã»ÓÐÍ£Ö¹ÏòϼÆÊý£©¡£NOTE£¡Ò»µ©·¢³öÁËËø´æÃüÁ¾ÍÒªÂíÉ϶Á¼ÆÊýÆ÷µÄÖµ¡£

7£®1£®3 ʱ¼ä´Á¼ÇÊýÆ÷TSC
´ÓPentium¿ªÊ¼£¬ËùÓеÄIntel 80x86 CPU¾Í¶¼ÓÖ°üº¬Ò»¸ö64λµÄʱ¼ä´Á¼ÇÊýÆ÷£¨TSC£©µÄ¼Ä´æÆ÷¡£¸Ã¼Ä´æÆ÷ʵ¼ÊÉÏÊÇÒ»¸ö²»¶ÏÔö¼ÓµÄ¼ÆÊýÆ÷£¬ËüÔÚCPUµÄÿ¸öʱÖÓÐźŵ½À´Ê±¼Ó1£¨Ò²¼´Ã¿Ò»¸öclock-cycleÊäÈëCPUʱ£¬¸Ã¼ÆÊýÆ÷µÄÖµ¾Í¼Ó1£©¡£
»ã±àÖ¸Áîrdtsc¿ÉÒÔÓÃÓÚ¶ÁÈ¡TSCµÄÖµ¡£ÀûÓÃCPUµÄTSC£¬²Ù×÷ϵͳͨ³£¿ÉÒԵõ½¸üΪ¾«×¼µÄʱ¼ä¶ÈÁ¿¡£¼ÙÈçclock-cycleµÄƵÂÊÊÇ400MHZ£¬ÄÇôTSC¾Í½«Ã¿2.5ÄÉÃëÔö¼ÓÒ»´Î¡£







µÚÆßÕ LinuxÄں˵ÄʱÖÓÖжÏ
£¨By Õ²ÈÙ¿ª£¬NUDT£©





Copyright © 2003 by Õ²ÈÙ¿ª
E-mail:zhanrk@sohu.com
Linux-2.4.0
Version 1.0.0£¬2003-2-14




ÕªÒª£º±¾ÎÄÖ÷Òª´ÓÄÚºËʵÏֵĽǶȷÖÎöÁËLinux 2.4.0Äں˵ÄʱÖÓÖжϡ¢Äں˶Ôʱ¼äµÄ±íʾµÈ¡£±¾ÎÄÊÇΪÄÇЩÏëÒªÁ˽âLinux I/O×ÓϵͳµÄ¶ÁÕߺÍLinuxÇý¶¯³ÌÐò¿ª·¢ÈËÔ±¶øдµÄ¡£
¹Ø¼ü´Ê£ºLinux¡¢Ê±ÖÓ¡¢¶¨Ê±Æ÷

ÉêÃ÷£ºÕâ·ÝÎĵµÊÇ°´ÕÕ×ÔÓÉÈí¼þ¿ª·ÅÔ´´úÂëµÄ¾«Éñ·¢²¼µÄ£¬ÈκÎÈË¿ÉÒÔÃâ·Ñ»ñµÃ¡¢Ê¹ÓúÍÖØз¢²¼£¬µ«ÊÇÄãûÓÐÏÞÖƱðÈËÖØз¢²¼Äã·¢²¼ÄÚÈݵÄȨÀû¡£·¢²¼±¾ÎĵÄÄ¿µÄÊÇÏ£ÍûËüÄܶԶÁÕßÓÐÓ㬵«Ã»ÓÐÈκε£±££¬ÉõÖÁûÓÐÊʺÏÌض¨Ä¿µÄµÄÒþº¬µÄµ£±£¡£¸üÏêϸµÄÇé¿öÇë²ÎÔÄGNUͨÓù«¹²Ðí¿ÉÖ¤(GPL)£¬ÒÔ¼°GNU×ÔÓÉÎĵµÐ­Òé(GFDL)¡£

ÄãÓ¦¸ÃÒѾ­ºÍÎĵµÒ»ÆðÊÕµ½Ò»·ÝGNUͨÓù«¹²Ðí¿ÉÖ¤(GPL)µÄ¸±±¾¡£Èç¹û»¹Ã»ÓУ¬Ð´ÐŸø£º
The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA

»¶Ó­¸÷λָ³öÎĵµÖеĴíÎóÓëÒÉÎÊ¡£
Ç°ÑÔ
ʱ¼äÔÚÒ»¸ö²Ù×÷ϵͳÄÚºËÖÐÕ¼¾Ý×ÅÖØÒªµÄµØ룬ËüÊÇÇý¶¯Ò»¸öOSÄÚºËÔËÐеġ°Æð²©Æ÷¡±¡£Ò»°ã˵À´£¬ÄÚºËÖ÷ÒªÐèÒªÁ½ÖÖÀàÐ͵Äʱ¼ä£º
1. ÔÚÄÚºËÔËÐÐÆÚ¼ä³ÖÐø¼Ç¼µ±Ç°µÄʱ¼äÓëÈÕÆÚ£¬ÒÔ±ãÄں˶ÔijЩ¶ÔÏóºÍʼþ×÷ʱ¼ä±ê¼Ç£¨timestamp£¬Ò²³ÆΪ¡°Ê±¼ä´Á¡±£©£¬»ò¹©Óû§Í¨¹ýʱ¼äsyscall½øÐмìË÷¡£
2. ά³ÖÒ»¸ö¹Ì¶¨ÖÜÆڵĶ¨Ê±Æ÷£¬ÒÔÌáÐÑÄں˻òÓû§Ò»¶Îʱ¼äÒѾ­¹ýÈ¥ÁË¡£
PC»úÖеÄʱ¼äÊÇÓÐÈýÖÖʱÖÓÓ²¼þÌṩµÄ£¬¶øÕâЩʱÖÓÓ²¼þÓÖ¶¼»ùÓڹ̶¨ÆµÂʵľ§ÌåÕñµ´Æ÷À´ÌṩʱÖÓ·½²¨ÐźÅÊäÈë¡£ÕâÈýÖÖʱÖÓÓ²¼þÊÇ£º£¨1£©ÊµÊ±Ê±ÖÓ£¨Real Time Clock£¬RTC£©£»£¨2£©¿É±à³Ì¼ä¸ô¶¨Ê±Æ÷£¨Programmable Interval Timer£¬PIT£©£»£¨3£©Ê±¼ä´Á¼ÆÊýÆ÷£¨Time Stamp Counter£¬TSC£©¡£

7£®1 ʱÖÓÓ²¼þ
7£®1£®1 ʵʱʱÖÓRTC
×Ô´ÓIBM PC ATÆð£¬ËùÓеÄPC»ú¾Í¶¼°üº¬ÁËÒ»¸ö½Ð×öʵʱʱÖÓ£¨RTC£©µÄʱÖÓоƬ£¬ÒÔ±ãÔÚPC»ú¶ÏµçºóÈÔÈ»Äܹ»¼ÌÐø±£³Öʱ¼ä¡£ÏÔÈ»£¬RTCÊÇͨ¹ýÖ÷°åÉϵĵç³ØÀ´¹©µçµÄ£¬¶ø²»ÊÇͨ¹ýPC»úµçÔ´À´¹©µçµÄ£¬Òò´Ëµ±PC»ú¹ØµôµçÔ´ºó£¬RTCÈÔÈ»»á¼ÌÐø¹¤×÷¡£Í¨³££¬CMOS RAMºÍRTC±»¼¯³Éµ½Ò»¿éоƬÉÏ£¬Òò´ËRTCÒ²³Æ×÷¡°CMOS Timer¡±¡£×î³£¼ûµÄRTCоƬÊÇMC146818£¨Motorola£©ºÍDS12887£¨maxim£©£¬DS12887ÍêÈ«¼æÈÝÓÚMC146818£¬²¢ÓÐÒ»¶¨µÄÀ©Õ¹¡£±¾½ÚÄÚÈÝÖ÷Òª»ùÓÚMC146818ÕâÒ»±ê×¼µÄRTCоƬ¡£¾ßÌåÄÚÈÝ¿ÉÒԲο¼MC146818µÄDatasheet¡£

7£®1£®1£®1 RTC¼Ä´æÆ÷
MC146818 RTCоƬһ¹²ÓÐ64¸ö¼Ä´æÆ÷¡£ËüÃǵÄоƬÄÚ²¿µØÖ·±àºÅΪ0x00¡«0x3F£¨²»ÊÇI/O¶Ë¿ÚµØÖ·£©£¬ÕâЩ¼Ä´æÆ÷Ò»¹²¿ÉÒÔ·ÖΪÈý×飺
£¨1£©Ê±ÖÓÓëÈÕÀú¼Ä´æÆ÷×飺¹²ÓÐ10¸ö£¨0x00~0x09£©£¬±íʾʱ¼ä¡¢ÈÕÀúµÄ¾ßÌåÐÅÏ¢¡£ÔÚPC»úÖУ¬ÕâЩ¼Ä´æÆ÷ÖеÄÖµ¶¼ÊÇÒÔBCD¸ñʽÀ´´æ´¢µÄ£¨±ÈÈç23dec£½0x23BCD£©¡£
£¨2£©×´Ì¬ºÍ¿ØÖƼĴæÆ÷×飺¹²ÓÐ4¸ö£¨0x0A~0x0D£©£¬¿ØÖÆRTCоƬµÄ¹¤×÷·½Ê½£¬²¢±íʾµ±Ç°µÄ״̬¡£
£¨3£©CMOSÅäÖÃÊý¾Ý£ºÍ¨ÓõÄCMOS RAM£¬ËüÃÇÓëʱ¼äÎ޹أ¬Òò´ËÎÒÃDz»¹ØÐÄËü¡£
ʱÖÓÓëÈÕÀú¼Ä´æÆ÷×éµÄÏêϸ½âÊÍÈçÏ£º
Address Function
00 Current second for RTC
01 Alarm second
02 Current minute
03 Alarm minute
04 Current hour
05 Alarm hour
06 Current day of week£¨01£½Sunday£©
07 Current date of month
08 Current month
09 Current year£¨final two digits£¬eg£º93£©

״̬¼Ä´æÆ÷A£¨µØÖ·0x0A£©µÄ¸ñʽÈçÏ£º
ÆäÖУº
£¨1£©bit£Û7£Ý----UIP±êÖ¾£¨Update in Progress£©£¬Îª1±íʾRTCÕýÔÚ¸üÐÂÈÕÀú¼Ä´æÆ÷×éÖеÄÖµ£¬´ËʱÈÕÀú¼Ä´æÆ÷×éÊDz»¿É·ÃÎʵģ¨´Ëʱ·ÃÎÊËüÃǽ«µÃµ½Ò»¸öÎÞÒâÒåµÄ½¥±äÖµ£©¡£
£¨2£©bit£Û6£º4£Ý----ÕâÈýλÊÇ¡°³ý·¨Æ÷¿ØÖÆλ¡±£¨divider-control bits£©£¬ÓÃÀ´¶¨ÒåRTCµÄ²Ù×÷ƵÂÊ¡£¸÷ÖÖ¿ÉÄܵÄÖµÈçÏ£º
Divider bits Time-base frequency Divider Reset Operation Mode
DV2 DV1 DV0
0 0 0 4.194304 MHZ NO YES
0 0 1 1.048576 MHZ NO YES
0 1 0 32.769 KHZ NO YES
1 1 0/1 ÈκΠYES NO
PC»úͨ³£½«Divider bitsÉèÖóɡ°010¡±¡£
£¨3£©bit£Û3£º0£Ý----ËÙÂÊÑ¡Ôñ루Rate Selection bits£©£¬ÓÃÓÚÖÜÆÚÐÔ»ò·½²¨ÐźÅÊä³ö¡£
RS bits 4.194304»ò1.048578 MHZ 32.768 KHZ
RS3 RS2 RS1 RS0 ÖÜÆÚÐÔÖÐ¶Ï ·½²¨ ÖÜÆÚÐÔÖÐ¶Ï ·½²¨
0 0 0 0 None None None None
0 0 0 1 30.517¦Ìs 32.768 KHZ 3.90625ms 256 HZ
0 0 1 0 61.035¦Ìs 16.384 KHZ
0 0 1 1 122.070¦Ìs 8.192KHZ
0 1 0 0 244.141¦Ìs 4.096KHZ
0 1 0 1 488.281¦Ìs 2.048KHZ
0 1 1 0 976.562¦Ìs 1.024KHZ
0 1 1 1 1.953125ms 512HZ
1 0 0 0 3.90625ms 256HZ
1 0 0 1 7.8125ms 128HZ
1 0 1 0 15.625ms 64HZ
1 0 1 1 31.25ms 32HZ
1 1 0 0 62.5ms 16HZ
1 1 0 1 125ms 8HZ
1 1 1 0 250ms 4HZ
1 1 1 1 500ms 2HZ
PC»úBIOS¶ÔÆäĬÈϵÄÉèÖÃÖµÊÇ¡°0110¡±¡£

״̬¼Ä´æÆ÷BµÄ¸ñʽÈçÏÂËùʾ£º
¸÷λµÄº¬ÒåÈçÏ£º
£¨1£©bit£Û7£Ý----SET±êÖ¾¡£Îª1±íʾRTCµÄËùÓиüйý³Ì¶¼½«ÖÕÖ¹£¬Óû§³ÌÐòËæºóÂíÉ϶ÔÈÕÀú¼Ä´æÆ÷×éÖеÄÖµ½øÐгõʼ»¯ÉèÖá£Îª0±íʾ½«ÔÊÐí¸üйý³Ì¼ÌÐø¡£
£¨2£©bit£Û6£Ý----PIE±êÖ¾£¬ÖÜÆÚÐÔÖжÏʹÄܱêÖ¾¡£
£¨3£©bit£Û5£Ý----AIE±êÖ¾£¬¸æ¾¯ÖжÏʹÄܱêÖ¾¡£
£¨4£©bit£Û4£Ý----UIE±êÖ¾£¬¸üнáÊøÖжÏʹÄܱêÖ¾¡£
£¨5£©bit£Û3£Ý----SQWE±êÖ¾£¬·½²¨ÐźÅʹÄܱêÖ¾¡£
£¨6£©bit£Û2£Ý----DM±êÖ¾£¬ÓÃÀ´¿ØÖÆÈÕÀú¼Ä´æÆ÷×éµÄÊý¾Ýģʽ£¬0£½BCD£¬1£½BINARY¡£BIOS×ÜÊǽ«ËüÉèÖÃΪ0¡£
£¨7£©bit£Û1£Ý----24£¯12±êÖ¾£¬ÓÃÀ´¿ØÖÆhour¼Ä´æÆ÷£¬0±íʾ12СʱÖÆ£¬1±íʾ24СʱÖÆ¡£PC»úBIOS×ÜÊǽ«ËüÉèÖÃΪ1¡£
£¨8£©bit£Û0£Ý----DSE±êÖ¾¡£BIOS×ÜÊǽ«ËüÉèÖÃΪ0¡£

״̬¼Ä´æÆ÷CµÄ¸ñʽÈçÏ£º
£¨1£©bit£Û7£Ý----IRQF±êÖ¾£¬ÖжÏÇëÇó±êÖ¾£¬µ±¸ÃλΪ1ʱ£¬ËµÃ÷¼Ä´æÆ÷BÖжÏÇëÇó·¢Éú¡£
£¨2£©bit£Û6£Ý----PF±êÖ¾£¬ÖÜÆÚÐÔÖжϱêÖ¾£¬Îª1±íʾ·¢ÉúÖÜÆÚÐÔÖжÏÇëÇó¡£
£¨3£©bit£Û5£Ý----AF±êÖ¾£¬¸æ¾¯ÖжϱêÖ¾£¬Îª1±íʾ·¢Éú¸æ¾¯ÖжÏÇëÇó¡£
£¨4£©bit£Û4£Ý----UF±êÖ¾£¬¸üнáÊøÖжϱêÖ¾£¬Îª1±íʾ·¢Éú¸üнáÊøÖжÏÇëÇó¡£

״̬¼Ä´æÆ÷DµÄ¸ñʽÈçÏ£º
£¨1£©bit£Û7£Ý----VRT±êÖ¾£¨Valid RAM and Time£©£¬Îª1±íʾOK£¬Îª0±íʾRTCÒѾ­µôµç¡£
£¨2£©bit£Û6£º0£Ý----×ÜÊÇΪ0£¬Î´¶¨Òå¡£

7£®1£®1£®2 ͨ¹ýI/O¶Ë¿Ú·ÃÎÊRTC
ÔÚPC»úÖпÉÒÔͨ¹ýI/O¶Ë¿Ú0x70ºÍ0x71À´¶ÁдRTCоƬÖеļĴæÆ÷¡£ÆäÖУ¬¶Ë¿Ú0x70ÊÇRTCµÄ¼Ä´æÆ÷µØÖ·Ë÷Òý¶Ë¿Ú£¬0x71ÊÇÊý¾Ý¶Ë¿Ú¡£
¶ÁRTCоƬ¼Ä´æÆ÷µÄ²½ÖèÊÇ£º
mov al, addr
out 70h, al ; Select reg_addr in RTC chip
jmp $+2 ; a slight delay to settle thing
in al, 71h ;
дRTC¼Ä´æÆ÷µÄ²½ÖèÈçÏ£º
mov al, addr
out 70h, al ; Select reg_addr in RTC chip
jmp $+2 ; a slight delay to settle thing
mov al, value
out 71h, al

7£®1£®2 ¿É±à³Ì¼ä¸ô¶¨Ê±Æ÷PIT
ÿ¸öPC»úÖж¼ÓÐÒ»¸öPIT£¬ÒÔͨ¹ýIRQ0²úÉúÖÜÆÚÐÔµÄʱÖÓÖжÏÐźš£µ±Ç°Ê¹ÓÃ×îÆÕ±éµÄÊÇIntel 8254 PITоƬ£¬ËüµÄI/O¶Ë¿ÚµØÖ·ÊÇ0x40~0x43¡£
Intel 8254 PITÓÐ3¸ö¼ÆʱͨµÀ£¬Ã¿¸öͨµÀ¶¼ÓÐÆ䲻ͬµÄÓÃ;£º
£¨1£© ͨµÀ0ÓÃÀ´¸ºÔð¸üÐÂϵͳʱÖÓ¡£Ã¿µ±Ò»¸öʱÖӵδð¹ýȥʱ£¬Ëü¾Í»áͨ¹ýIRQ0Ïòϵͳ²úÉúÒ»´ÎʱÖÓÖжϡ£
£¨2£© ͨµÀ1ͨ³£ÓÃÓÚ¿ØÖÆDMAC¶ÔRAMµÄˢС£
£¨3£© ͨµÀ2±»Á¬½Óµ½PC»úµÄÑïÉùÆ÷£¬ÒÔ²úÉú·½²¨Ðźš£
ÿ¸öͨµÀ¶¼ÓÐÒ»¸öÏòϼõСµÄ¼ÆÊýÆ÷£¬8254 PITµÄÊäÈëʱÖÓÐźŵÄƵÂÊÊÇ1193181HZ£¬Ò²¼´Ò»ÃëÖÓÊäÈë1193181¸öclock-cycle¡£Ã¿ÊäÈëÒ»¸öclock-cycleÆäʱ¼äͨµÀµÄ¼ÆÊýÆ÷¾ÍÏòϼõ1£¬Ò»Ö±¼õµ½0Öµ¡£Òò´Ë¶ÔÓÚͨµÀ0¶øÑÔ£¬µ±ËûµÄ¼ÆÊýÆ÷¼õµ½0ʱ£¬PIT¾ÍÏòϵͳ²úÉúÒ»´ÎʱÖÓÖжϣ¬±íʾһ¸öʱÖӵδðÒѾ­¹ýÈ¥ÁË¡£µ±¸÷ͨµÀµÄ¼ÆÊýÆ÷¼õµ½0ʱ£¬ÎÒÃǾÍ˵¸ÃͨµÀ´¦ÓÚ¡°Terminal count¡±×´Ì¬¡£
ͨµÀ¼ÆÊýÆ÷µÄ×î´óÖµÊÇ10000h£¬Ëù¶ÔÓ¦µÄʱÖÓÖжÏƵÂÊÊÇ1193181£¯£¨65536£©£½18.2HZ£¬Ò²¾ÍÊÇ˵£¬´ËʱһÃëÖÓÖ®ÄÚ½«²úÉú18.2´ÎʱÖÓÖжϡ£

7£®1£®2£®1 PITµÄI/O¶Ë¿Ú
ÔÚi386ƽ̨ÉÏ£¬8254оƬµÄ¸÷¼Ä´æÆ÷µÄI/O¶Ë¿ÚµØÖ·ÈçÏ£º
Port Description
40h Channel 0 counter£¨read/write£©
41h Channel 1 counter£¨read/write£©
42h Channel 2 counter£¨read/write£©
43h PIT control word£¨write only£©
ÆäÖУ¬ÓÉÓÚͨµÀ0¡¢1¡¢2µÄ¼ÆÊýÆ÷ÊÇÒ»¸ö16λ¼Ä´æÆ÷£¬¶øÏàÓ¦µÄ¶Ë¿ÚÈ´¶¼ÊÇ8λµÄ£¬Òò´Ë¶ÁдͨµÀ¼ÆÊýÆ÷±ØÐë½øÐнøÐÐÁ½´ÎI/O¶Ë¿Ú¶Áд²Ù×÷£¬·Ö±ð¶ÔÓ¦ÓÚ¼ÆÊýÆ÷µÄ¸ß×ֽں͵Í×Ö½Ú£¬ÖÁÓÚÊÇÏȶÁд¸ß×Ö½ÚÔÙ¶ÁдµÍ×Ö½Ú£¬»¹ÊÇÏȶÁдµÍ×Ö½ÚÔÙ¶Áд¸ß×Ö½Ú£¬ÔòÓÉPITµÄ¿ØÖƼĴæÆ÷À´¾ö¶¨¡£8254 PITµÄ¿ØÖƼĴæÆ÷µÄ¸ñʽÈçÏ£º
£¨1£©bit£Û7£º6£Ý----Select Counter£¬Ñ¡Ôñ¶ÔÄǸö¼ÆÊýÆ÷½øÐвÙ×÷¡£¡°00¡±±íʾѡÔñCounter 0£¬¡°01¡±±íʾѡÔñCounter 1£¬¡°10¡±±íʾѡÔñCounter 2£¬¡°11¡±±íʾRead-Back Command£¨½ö¶ÔÓÚ8254£¬¶ÔÓÚ8253ÎÞЧ£©¡£
£¨2£©bit£Û5£º4£Ý----Read/Write/Latch¸ñʽλ¡£¡°00¡±±íʾËø´æ£¨Latch£©µ±Ç°¼ÆÊýÆ÷µÄÖµ£»¡°01¡±Ö»¶Áд¼ÆÊýÆ÷µÄ¸ß×Ö½Ú£¨MSB£©£»¡°10¡±Ö»¶Áд¼ÆÊýÆ÷µÄµÍ×Ö½Ú£¨LSB£©£»¡°11¡±±íʾÏȶÁд¼ÆÊýÆ÷µÄLSB£¬ÔÙ¶ÁдMSB¡£
£¨3£©bit£Û3£º1£Ý----Mode bits£¬¿ØÖƸ÷ͨµÀµÄ¹¤×÷ģʽ¡£¡°000¡±¶ÔÓ¦Mode 0£»¡°001¡±¶ÔÓ¦Mode 1£»¡°010¡±¶ÔÓ¦Mode 2£»¡°011¡±¶ÔÓ¦Mode 3£»¡°100¡±¶ÔÓ¦Mode 4£»¡°101¡±¶ÔÓ¦Mode 5¡£
£¨4£©bit£Û0£Ý----¿ØÖƼÆÊýÆ÷µÄ´æ´¢Ä£Ê½¡£0±íʾÒÔ¶þ½øÖƸñʽ´æ´¢£¬1±íʾ¼ÆÊýÆ÷ÖеÄÖµÒÔBCD¸ñʽ´æ´¢¡£

7£®1£®2£®2 PITͨµÀµÄ¹¤×÷ģʽ
PIT¸÷ͨµÀ¿ÉÒÔ¹¤×÷ÔÚÏÂÁÐ6ÖÖģʽÏ£º
1. Mode 0£ºµ±Í¨µÀ´¦ÓÚ¡°Terminal count¡±×´Ì¬Ê±²úÉúÖжÏÐźš£
2. Mode 1£ºHardware retriggerable one-shot¡£
3. Mode 2£ºRate Generator¡£ÕâÖÖģʽµäÐ͵ر»ÓÃÀ´²úÉúʵʱʱÖÓÖжϡ£´ËʱͨµÀµÄÐźÅÊä³ö¹Ü½ÅOUT³õʼʱ±»ÉèÖÃΪ¸ßµçƽ£¬²¢ÒԴ˳ÖÐøµ½¼ÆÊýÆ÷µÄÖµ¼õµ½1¡£È»ºóÔÚ½ÓÏÂÀ´µÄÕâ¸öclock-cycleÆڼ䣬OUT¹Ü½Å½«±äΪµÍµçƽ£¬Ö±µ½¼ÆÊýÆ÷µÄÖµ¼õµ½0¡£µ±¼ÆÊýÆ÷µÄÖµ±»×Ô¶¯µØÖØмÓÔغó£¬OUT¹Ü½ÅÓÖ±ä³É¸ßµçƽ£¬È»ºóÖظ´ÉÏÊö¹ý³Ì¡£Í¨µÀ0ͨ³£¹¤×÷ÔÚÕâ¸öģʽÏ¡£
4. Mode 3£º·½²¨Ðźŷ¢ÉúÆ÷¡£
5. Mode 4£ºSoftware triggered strobe¡£
6. Mode 5£ºHardware triggered strobe¡£

7£®1£®2£®3 Ëø´æ¼ÆÊýÆ÷£¨Latch Counter£©
µ±¿ØÖƼĴæÆ÷ÖеÄbit£Û5£º4£ÝÉèÖóÉ0ʱ£¬½«°Ñµ±Ç°Í¨µÀµÄ¼ÆÊýÆ÷ÖµËø´æ¡£´Ëʱͨ¹ýI/O¶Ë¿Ú¿ÉÒÔ¶Áµ½Ò»¸öÎȶ¨µÄ¼ÆÊýÆ÷Öµ£¬ÒòΪ¼ÆÊýÆ÷±íÃæÉÏÒѾ­Í£Ö¹ÏòϼÆÊý£¨PITоƬÄÚ²¿²¢Ã»ÓÐÍ£Ö¹ÏòϼÆÊý£©¡£NOTE£¡Ò»µ©·¢³öÁËËø´æÃüÁ¾ÍÒªÂíÉ϶Á¼ÆÊýÆ÷µÄÖµ¡£

7£®1£®3 ʱ¼ä´Á¼ÇÊýÆ÷TSC
´ÓPentium¿ªÊ¼£¬ËùÓеÄIntel 80x86 CPU¾Í¶¼ÓÖ°üº¬Ò»¸ö64λµÄʱ¼ä´Á¼ÇÊýÆ÷£¨TSC£©µÄ¼Ä´æÆ÷¡£¸Ã¼Ä´æÆ÷ʵ¼ÊÉÏÊÇÒ»¸ö²»¶ÏÔö¼ÓµÄ¼ÆÊýÆ÷£¬ËüÔÚCPUµÄÿ¸öʱÖÓÐźŵ½À´Ê±¼Ó1£¨Ò²¼´Ã¿Ò»¸öclock-cycleÊäÈëCPUʱ£¬¸Ã¼ÆÊýÆ÷µÄÖµ¾Í¼Ó1£©¡£
»ã±àÖ¸Áîrdtsc¿ÉÒÔÓÃÓÚ¶ÁÈ¡TSCµÄÖµ¡£ÀûÓÃCPUµÄTSC£¬²Ù×÷ϵͳͨ³£¿ÉÒԵõ½¸üΪ¾«×¼µÄʱ¼ä¶ÈÁ¿¡£¼ÙÈçclock-cycleµÄƵÂÊÊÇ400MHZ£¬ÄÇôTSC¾Í½«Ã¿2.5ÄÉÃëÔö¼ÓÒ»´Î¡£
ÎÄÕÂÆÀÂÛ

¹²ÓÐ 7 ÌõÆÀÂÛ

  1. hfh08 ÓÚ 2006-08-21 00:26:56·¢±í:

    7£®8 ʱ¼äϵͳµ÷ÓõÄʵÏÖ
    ±¾½Ú½²ÊöÓëʱ¼äÏà¹ØµÄsyscall£¬ÕâЩϵͳµ÷ÓÃÖ÷ÒªÓÃÀ´¹©Óû§½ø³ÌÏòÄں˼ìË÷µ±Ç°Ê±¼äÓëÈÕÆÚ£¬Òò´ËËûÃÇÊÇÄں˵Äʱ¼ä·þÎñ½Ó¿Ú¡£Ö÷ÒªµÄʱ¼äϵͳµ÷Óù²ÓÐ5¸ö£ºtime¡¢stimeºÍgettimeofday¡¢settimeofday£¬ÒÔ¼°ÓëÍøÂçʱ¼äЭÒéNTPÏà¹ØµÄadjtimexϵͳµ÷Óá£ÕâÀïÎÒÃDz»¹ØÐÄNTP£¬Òò´Ë½ö·ÖÎöÇ°4¸öʱ¼äϵͳµ÷Óá£Ç°4¸öʱ¼äϵͳµ÷ÓÿÉÒÔ·ÖΪÁ½×飺£¨1£©timeºÍstimeÊÇÒ»×飻£¨2£©gettimeofdayºÍsettimeofdayÊÇÒ»×é¡£

    7£®8£®1 ϵͳµ÷ÓÃtimeºÍstime
    ϵͳµ÷ÓÃtime£¨£©ÓÃÓÚ»ñÈ¡ÒÔÃëÊý±íʾµÄϵͳµ±Ç°Ê±¼ä£¨¼´ÄÚºËÈ«¾Öʱ¼ä±äÁ¿xtimeÖеÄtv_sec³ÉÔ±µÄÖµ£©¡£ËüÖ»ÓÐÒ»¸ö²ÎÊý----ÕûÐÍÖ¸Õëtloc£¬Ö¸ÏòÓû§¿Õ¼äÖеÄÒ»¸öÕûÊý£¬ÓÃÀ´½ÓÊÕ·µ»ØµÄµ±Ç°Ê±¼äÖµ¡£º¯Êýsys_time£¨£©µÄÔ´ÂëÈçÏ£¨kernel/time.c£©£º
    asmlinkage long sys_time(int * tloc)
    {
    int i;

    /* SMP: This is fairly trivial. We grab CURRENT_TIME and
    stuff it to user space. No side effects */
    i = CURRENT_TIME;
    if (tloc) {
    if (put_user(i,tloc))
    i = -EFAULT;
    }
    return i;
    }
    ×¢ÊÍÈçÏ£º
    £¨1£©Ê×ÏÈ£¬º¯Êýµ÷ÓÃCURRENT_TIMEºêÀ´µÃµ½ÒÔÃëÊý±íʾµÄÄں˵±Ç°Ê±¼äÖµ£¬²¢½«¸ÃÖµ±£´æÔÚ¾Ö²¿±äÁ¿iÖС£ºêCURRENT_TIME¶¨ÒåÔÚinclude/linux/sched.hÍ·ÎļþÖУ¬Ëüʵ¼ÊÉϾÍÊÇÄÚºËÈ«¾Öʱ¼ä±äÁ¿xtimeÖеÄtv_sec³ÉÔ±¡£ÈçÏÂËùʾ£º
    #define CURRENT_TIME (xtime.tv_sec)
    £¨2£©È»ºó£¬ÔÚ²ÎÊýÖ¸Õëtloc·Ç¿ÕµÄÇé¿öϽ«iµÄֵͨ¹ýput_user()ºê´«µÝµ½ÓÐtlocËùÖ¸ÏòµÄÓû§¿Õ¼äÖÐÈ¥£¬ÒÔ×÷Ϊº¯ÊýµÄÊä³ö½á¹û¡£
    £¨3£©×îºó£¬½«¾Ö²¿±äÁ¿IµÄÖµ----Ò²¼´Ò²ÃëÊý±íʾµÄϵͳµ±Ç°Ê±¼äÖµ×÷Ϊ·µ»ØÖµ·µ»Ø¡£

    ϵͳµ÷ÓÃstime()Óëϵͳµ÷ÓÃtime()¸ÕºÃÏà·´£¬Ëü¿ÉÒÔÈÃÓû§ÉèÖÃϵͳµÄµ±Ç°Ê±¼ä£¨ÒÔÃëÊýΪµ¥Î»£©¡£ËüͬÑùÒ²Ö»ÓÐÒ»¸ö²ÎÊý----ÕûÐÍÖ¸Õëtptr£¬Ö¸ÏòÓû§¿Õ¼äÖдýÉèÖõÄʱ¼äÃëÊýÖµ¡£º¯Êýsys_stime()µÄÔ´ÂëÈçÏ£¨kernel/time.c£©£º
    asmlinkage long sys_stime(int * tptr)
    {
    int value;

    if (!capable(CAP_SYS_TIME))
    return -EPERM;
    if (get_user(value, tptr))
    return -EFAULT;
    write_lock_irq(&xtime_lock);
    xtime.tv_sec = value;

  2. hfh08 ÓÚ 2006-08-21 00:26:37·¢±í:

    7£®7 ½ø³Ì¼ä¸ô¶¨Ê±Æ÷itimer
    Ëùν¡°¼ä¸ô¶¨Ê±Æ÷£¨Interval Timer£¬¼ò³Æitimer£©¾ÍÊÇÖ¸¶¨Ê±Æ÷²ÉÓá°¼ä¸ô¡±Öµ£¨interval£©À´×÷Ϊ¼Æʱ·½Ê½£¬µ±¶¨Ê±Æ÷Æô¶¯ºó£¬¼ä¸ôÖµinterval½«²»¶Ï¼õС¡£µ±intervalÖµ¼õµ½0ʱ£¬ÎÒÃǾÍ˵¸Ã¼ä¸ô¶¨Ê±Æ÷µ½ÆÚ¡£ÓëÉÏÒ»½ÚËù˵µÄÄں˶¯Ì¬¶¨Ê±Æ÷Ïà±È£¬¶þÕß×î´óµÄÇø±ðÔÚÓÚ¶¨Ê±Æ÷µÄ¼Æʱ·½Ê½²»Í¬¡£Äں˶¨Ê±Æ÷ÊÇͨ¹ýËüµÄµ½ÆÚʱ¿ÌexpiresÖµÀ´¼ÆʱµÄ£¬µ±È«¾Ö±äÁ¿jiffiesÖµ´óÓÚ»òµÈÓÚÄں˶¯Ì¬¶¨Ê±Æ÷µÄexpiresֵʱ£¬ÎÒÃÇ˵ÄÚºËÄں˶¨Ê±Æ÷µ½ÆÚ¡£¶ø¼ä¸ô¶¨Ê±Æ÷Ôòʵ¼ÊÉÏÊÇͨ¹ýÒ»¸ö²»¶Ï¼õСµÄ¼ÆÊýÆ÷À´¼ÆʱµÄ¡£ËäÈ»ÕâÁ½ÖÖ¶¨Ê±Æ÷²¢²»Ïàͬ£¬µ«È´Ò²ÊÇÏ໥ÁªÏµµÄ¡£¼ÙÈçÎÒÃÇÿ¸öʱÖÓ½ÚÅĶ¼Ê¹¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷¼õ1£¬ÄÇôÔÚÕâÖÖÇéÐÎϼä¸ô¶¨Ê±Æ÷ʵ¼ÊÉϾÍÊÇÄں˶¯Ì¬¶¨Ê±Æ÷£¨ÏÂÃæÎÒÃǻῴµ½½ø³ÌµÄÕæʵ¼ä¸ô¶¨Ê±Æ÷¾ÍÊÇÕâÑùͨ¹ýÄں˶¨Ê±Æ÷À´ÊµÏֵģ©¡£
    ¼ä¸ô¶¨Ê±Æ÷Ö÷Òª±»Ó¦ÓÃÔÚÓû§½ø³ÌÉÏ¡£Ã¿¸öLinux½ø³Ì¶¼ÓÐÈý¸öÏ໥¹ØÁªµÄ¼ä¸ô¶¨Ê±Æ÷¡£Æä¸÷×Եļä¸ô¼ÆÊýÆ÷¶¼¶¨ÒåÔÚ½ø³ÌµÄtask_struct½á¹¹ÖУ¬ÈçÏÂËùʾ£¨include/linux/sched.h£©£º
    struct task_struct£û
    ¡­¡­
    unsigned long it_real_value, it_prof_value, it_virt_value;
    unsigned long it_real_incr, it_prof_incr, it_virt_incr;
    struct timer_list real_timer;
    ¡­¡­
    }
    £¨1£©Õæʵ¼ä¸ô¶¨Ê±Æ÷£¨ITIMER_REAL£©£ºÕâÖÖ¼ä¸ô¶¨Ê±Æ÷ÔÚÆô¶¯ºó£¬²»¹Ü½ø³ÌÊÇ·ñÔËÐУ¬Ã¿¸öʱÖӵδ𶼽«Æä¼ä¸ô¼ÆÊýÆ÷¼õ1¡£µ±¼õµ½0ֵʱ£¬ÄÚºËÏò½ø³Ì·¢ËÍSIGALRMÐźš£½á¹¹ÀàÐÍtask_structÖеijÉÔ±it_real_incrÔò±íʾÕæʵ¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄ³õʼֵ£¬¶ø³ÉÔ±it_real_valueÔò±íʾÕæʵ¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄµ±Ç°Öµ¡£ÓÉÓÚÕâÖÖ¼ä¸ô¶¨Ê±Æ÷±¾ÖÊÉÏÓëÉÏÒ»½ÚµÄÄں˶¨Ê±Æ÷ʱһÑùµÄ£¬Òò´ËLinuxʵ¼ÊÉÏÊÇͨ¹ýreal_timerÕâ¸öÄÚǶÔÚtask_struct½á¹¹ÖеÄÄں˶¯Ì¬¶¨Ê±Æ÷À´ÊµÏÖÕæʵ¼ä¸ô¶¨Ê±Æ÷ITIMER_REALµÄ¡£
    £¨2£©ÐéÄâ¼ä¸ô¶¨Ê±Æ÷ITIMER_VIRT£ºÒ²³ÆΪ½ø³ÌµÄÓû§Ì¬¼ä¸ô¶¨Ê±Æ÷¡£½á¹¹ÀàÐÍtask_structÖгÉÔ±it_virt_incrºÍit_virt_value·Ö±ð±íʾÐéÄâ¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄ³õʼֵºÍµ±Ç°Öµ£¬¶þÕß¾ùÒÔʱÖӵδð´ÎÊýλ¼ÆÊýµ¥Î»¡£µ±ÐéÄâ¼ä¸ô¶¨Ê±Æ÷Æô¶¯ºó£¬Ö»Óе±½ø³ÌÔÚÓû§Ì¬ÏÂÔËÐÐʱ£¬Ò»´ÎʱÖӵδð²ÅÄÜʹ¼ä¸ô¼ÆÊýÆ÷µ±Ç°Öµit_virt_value¼õ1¡£µ±¼õµ½0ֵʱ£¬ÄÚºËÏò½ø³Ì·¢ËÍSIGVTALRMÐźţ¨ÐéÄâÄÖÖÓÐźţ©£¬²¢½«it_virt_valueÖØÖÃΪ³õÖµit_virt_incr¡£¾ßÌåÇë¼û7.4.3½ÚÖеÄdo_it_virt()º¯ÊýµÄʵÏÖ¡£
    £¨3£©PROF¼ä¸ô¶¨Ê±Æ÷ITIMER_PROF£º½ø³ÌµÄtask_struct½á¹¹ÖеÄit_prof_valueºÍit_prof_incr³ÉÔ±·Ö±ð±íʾPROF¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄµ±Ç°ÖµºÍ³õʼֵ£¨¾ùÒÔʱÖӵδðΪµ¥Î»£©¡£µ±Ò»¸ö½ø³ÌµÄPROF¼ä¸ô¶¨Ê±Æ÷Æô¶¯ºó£¬ÔòÖ»Òª¸Ã½ø³Ì´¦ÓÚÔËÐÐÖУ¬¶ø²»¹ÜÊÇÔÚÓû§Ì¬»òºËÐÄ̬ÏÂÖ´ÐУ¬Ã¿¸öʱÖӵδð¶¼Ê¹¼ä¸ô¼ÆÊýÆ÷it_prof_valueÖµ¼õ1¡£µ±¼õµ½0ֵʱ£¬ÄÚºËÏò½ø³Ì·¢ËÍSIGPROFÐźţ¬²¢½«it_prof_valueÖØÖÃΪ³õÖµit_prof_incr¡£¾ßÌåÇë¼û7.4.3½ÚµÄdo_it_prof()º¯Êý¡£
    LinuxÔÚinclude/linux/time.hÍ·ÎļþÖÐΪÉÏÊöÈýÖÖ½ø³Ì¼ä¸ô¶¨Ê±Æ÷¶¨ÒåÁËË÷Òý±êʶ£¬ÈçÏÂËùʾ£º
    #define ITIMER_REAL 0
    #define ITIMER_VIRTUAL 1
    #define ITIMER_PROF 2

    7£®7£®1 Êý¾Ý½á¹¹itimerval
    ËäÈ»£¬ÔÚÄÚºËÖмä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷ÊÇÒÔʱÖӵδð´ÎÊýΪµ¥Î»£¬µ«ÊÇÈÃÓû§ÒÔʱÖӵδðΪµ¥Î»À´Ö¸¶¨¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄ³õÖµÏÔÈ»ÊDz»Ì«·½±ãµÄ£¬ÒòΪÓû§Ï°¹ßµÄʱ¼äµ¥Î»ÊÇÃë¡¢ºÁÃë»ò΢ÃëµÈ¡£ËùÒÔLinux¶¨ÒåÁËÊý¾Ý½á¹¹itimervalÀ´ÈÃÓû§ÒÔÃë»ò΢ÃëΪµ¥Î»Ö¸¶¨¼ä¸ô¶¨Ê±Æ÷µÄʱ¼ä¼ä¸ôÖµ¡£Æ䶨ÒåÈçÏ£¨include/linux/time.h£©£º
    struct itimerval {
    struct timeval it_interval; /* timer interval */
    struct timeval it_value; /* current value */
    };
    ÆäÖУ¬it_interval³ÉÔ±±íʾ¼ä¸ô¼ÆÊýÆ÷µÄ³õʼֵ£¬¶øit_value³ÉÔ±±íʾ¼ä¸ô¼ÆÊýÆ÷µÄµ±Ç°Öµ¡£ÕâÁ½¸ö³ÉÔ±¶¼ÊÇtimeval½á¹¹ÀàÐ͵ıäÁ¿£¬Òò´ËÆ侫¶È¿ÉÒԴﵽ΢Ã뼶¡£

    l timevalÓëjiffiesÖ®¼äµÄÏ໥ת»»
    ÓÉÓÚ¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄÄÚ²¿±íʾ·½Ê½ÓëÍⲿ±íÏÖ·½Ê½»¥²»Ïàͬ£¬Òò´ËÓбØҪʵÏÖÒÔ΢ÃëΪµ¥Î»µÄtimeval½á¹¹ºÍΪʱÖӵδð´ÎÊýµ¥Î»µÄjiffiesÖ®¼äµÄÏ໥ת»»¡£Îª´Ë£¬LinuxÔÚkernel/itimer.cÖÐʵÏÖÁËÁ½¸öº¯ÊýʵÏÖ¶þÕߵĻ¥Ïàת»»----tvtojiffies()º¯ÊýºÍjiffiestotv()º¯Êý¡£ËüÃǵÄÔ´ÂëÈçÏ£º
    static unsigned long tvtojiffies(struct timeval *value)
    {
    unsigned long sec = (unsigned) value->tv_sec;
    unsigned long usec = (unsigned) value->tv_usec;

    if (sec > (ULONG_MAX / HZ))
    return ULONG_MAX;
    usec += 1000000 / HZ - 1;
    usec /= 1000000 / HZ;
    return HZ*sec+usec;
    }

    static void jiffiestotv(unsigned long jiffies, struct timeval *value)
    {
    value->tv_usec = (jiffies % HZ) * (1000000 / HZ);
    value->tv_sec = jiffies / HZ;
    }

    7£®7£®2 Õæʵ¼ä¸ô¶¨Ê±Æ÷ITIMER_REALµÄµ×²ãÔËÐлúÖÆ
    ¼ä¸ô¶¨Ê±Æ÷ITIMER_VIRTºÍITIMER_PROFµÄµ×²ãÔËÐлúÖÆÊÇ·Ö±ðͨ¹ýº¯Êýdo_it_virt£¨£©º¯ÊýºÍdo_it_prof£¨£©º¯ÊýÀ´ÊµÏֵģ¬ÕâÀï¾Í²»ÔÙÖØÊö£¨¿ÉÒԲμû7.4.3½Ú£©¡£
    ÓÉÓÚ¼ä¸ô¶¨Ê±Æ÷ITIMER_REAL±¾ÖÊÉÏÓëÄں˶¯Ì¬¶¨Ê±Æ÷²¢ÎÞÇø±ð¡£Òò´ËÄÚºËʵ¼ÊÉÏÊÇͨ¹ýÄں˶¯Ì¬¶¨Ê±Æ÷À´ÊµÏÖ½ø³ÌµÄITIMER_REAL¼ä¸ô¶¨Ê±Æ÷µÄ¡£Îª´Ë£¬task_struct½á¹¹ÖÐרÃÅÉèÁ¢Ò»¸ötimer_list½á¹¹ÀàÐ͵ijÉÔ±±äÁ¿real_timer¡£¶¯Ì¬¶¨Ê±Æ÷real_timerµÄº¯ÊýÖ¸Õëfunction×ÜÊDZ»task_struct½á¹¹µÄ³õʼ»¯ºêINIT_TASKÉèÖÃΪָÏòº¯Êýit_real_fn()¡£ÈçÏÂËùʾ£¨include/linux/sched.h£©£º
    #define INIT_TASK(tsk) \
    ¡­¡­
    real_timer£º {
    function£º it_real_fn \
    } \
    ¡­¡­
    }
    ¶øreal_timerÁ´±íÔªËØlistºÍdata³ÉÔ±×ÜÊDZ»½ø³Ì´´½¨Ê±·Ö±ð³õʼ»¯Îª¿ÕºÍ½ø³Ìtask_struct½á¹¹µÄµØÖ·£¬ÈçÏÂËùʾ£¨kernel/fork.c£©£º
    int do_fork(¡­¡­)
    {
    ¡­¡­
    p->it_real_value = p->it_virt_value = p->it_prof_value = 0;
    p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0;
    init_timer(&p->real_timer);
    p->real_timer.data = (unsigned long)p;
    ¡­¡­
    }
    µ±Óû§Í¨¹ýsetitimer()ϵͳµ÷ÓÃÀ´ÉèÖýø³ÌµÄITIMER_REAL¼ä¸ô¶¨Ê±Æ÷ʱ£¬it_real_incr±»ÉèÖóɷÇÁãÖµ£¬ÓÚÊǸÃϵͳµ÷ÓÃÏàÓ¦µØÉèÖúÃreal_timer.expiresÖµ£¬È»ºó½ø³ÌµÄreal_timer¶¨Ê±Æ÷¾Í±»¼ÓÈëµ½Äں˶¯Ì¬¶¨Ê±Æ÷Á´±íÖУ¬ÕâÑù¸Ã½ø³ÌµÄITIMER_REAL¼ä¸ô¶¨Ê±Æ÷¾Í±»Æô¶¯ÁË¡£µ±real_timer¶¨Ê±Æ÷µ½ÆÚʱ£¬ËüµÄ¹ØÁªº¯Êýit_real_fn()½«±»Ö´ÐС£×¢Ò⣡ËùÓнø³ÌµÄreal_timer¶¨Ê±Æ÷µÄfunctionº¯ÊýÖ¸Õ붼ָÏòit_real_fn()Õâͬһ¸öº¯Êý£¬Òò´Ëit_real_fn()º¯Êý±ØÐëͨ¹ýÆä²ÎÊýÀ´Ê¶±ðÊÇÄÄÒ»¸ö½ø³Ì£¬Îª´ËËü½«unsigned longÀàÐ͵IJÎÊýp½âÊÍΪ½ø³Ìtask_struct½á¹¹µÄµØÖ·¡£¸Ãº¯ÊýµÄÔ´ÂëÈçÏ£¨kernel/itimer.c£©£º
    void it_real_fn(unsigned long __data)
    {
    struct task_struct * p = (struct task_struct *) __data;
    unsigned long interval;

    send_sig(SIGALRM, p, 1);
    interval = p->it_real_incr;
    if (interval) {
    if (interval > (unsigned long) LONG_MAX)
    interval = LONG_MAX;
    p->real_timer.expires = jiffies + interval;
    add_timer(&p->real_timer);
    }
    }
    º¯Êýit_real_fn()µÄÖ´Ðйý³Ì´óÖÂÈçÏ£º
    £¨1£©Ê×ÏȽ«²ÎÊýpͨ¹ýÇ¿ÖÆÀàÐÍת»»½âÊÍΪ½ø³ÌµÄtask_struct½á¹¹ÀàÐ͵ÄÖ¸Õë¡£
    £¨2£©Ïò½ø³Ì·¢ËÍSIGALRMÐźš£
    £¨3£©ÔÚ½ø³ÌµÄit_real_incr·Ç0µÄÇé¿öϼÌÐøÆô¶¯real_timer¶¨Ê±Æ÷¡£Ê×ÏÈ£¬¼ÆËãreal_timer¶¨Ê±Æ÷µÄexpiresֵΪ£¨jiffies£«it_real_incr£©¡£È»ºó£¬µ÷ÓÃadd_timer()º¯Êý½«real_timer¼ÓÈëµ½Äں˶¯Ì¬¶¨Ê±Æ÷Á´±íÖС£

    7£®7£®3 itimer¶¨Ê±Æ÷µÄϵͳµ÷ÓÃ
    Óëitimer¶¨Ê±Æ÷Ïà¹ØµÄsyscallÓÐÁ½¸ö£ºgetitimer()ºÍsetitimer()¡£ÆäÖУ¬getitimer()ÓÃÓÚ²éѯµ÷Óýø³ÌµÄÈý¸ö¼ä¸ô¶¨Ê±Æ÷µÄÐÅÏ¢£¬¶øsetitimer()ÔòÓÃÀ´ÉèÖõ÷Óýø³ÌµÄÈý¸ö¼ä¸ô¶¨Ê±Æ÷¡£ÕâÁ½¸ösyscall¶¼ÊÇÏÖÔÚkernel/itimer.cÎļþÖС£

    7£®7£®3£®1 getitimer()ϵͳµ÷ÓõÄʵÏÖ
    º¯Êýsys_getitimer()ÓÐÁ½¸ö²ÎÊý£º£¨1£©which£¬Ö¸¶¨²éѯµ÷Óýø³ÌµÄÄÄÒ»¸ö¼ä¸ô¶¨Ê±Æ÷£¬ÆäÈ¡Öµ¿ÉÒÔÊÇITIMER_REAL¡¢ITIMER_VIRTºÍITIMER_PROFÈýÕßÖ®Ò»¡££¨2£©valueÖ¸Õ룬ָÏòÓû§¿Õ¼äÖеÄÒ»¸öitimerval½á¹¹£¬ÓÃÓÚ½ÓÊÕ²éѯ½á¹û¡£¸Ãº¯ÊýµÄÔ´ÂëÈçÏ£º
    /* SMP: Only we modify our itimer values. */
    asmlinkage long sys_getitimer(int which, struct itimerval *value)
    {
    int error = -EFAULT;
    struct itimerval get_buffer;

    if (value) {
    error = do_getitimer(which, &get_buffer);
    if (!error &&
    copy_to_user(value, &get_buffer, sizeof(get_buffer)))
    error = -EFAULT;
    }
    return error;
    }
    ÏÔÈ»£¬sys_getitimer()º¯ÊýÖ÷Ҫͨ¹ýdo_getitimer()º¯ÊýÀ´²éѯµ±Ç°½ø³ÌµÄ¼ä¸ô¶¨Ê±Æ÷ÐÅÏ¢£¬²¢½«²éѯ½á¹û±£´æÔÚÄں˿ռäµÄ½á¹¹±äÁ¿get_bufferÖС£È»ºó£¬µ÷ÓÃcopy_to_usr()ºê½«get_bufferÖнá¹û¿½±´µ½Óû§¿Õ¼ä»º³åÇøÖС£
    º¯Êýdo_getitimer()µÄÔ´ÂëÈçÏ£¨kernel/itimer.c£©£º
    int do_getitimer(int which, struct itimerval *value)
    {
    register unsigned long val, interval;

    switch (which) {
    case ITIMER_REAL:
    interval = current->it_real_incr;
    val = 0;
    /*
    * FIXME! This needs to be atomic, in case the kernel timer happens!
    */
    if (timer_pending(¤t->real_timer)) {
    val = current->real_timer.expires - jiffies;

    /* look out for negative/zero itimer.. */
    if ((long) val <= 0)
    val = 1;
    }
    break;
    case ITIMER_VIRTUAL:
    val = current->it_virt_value;
    interval = current->it_virt_incr;
    break;
    case ITIMER_PROF:
    val = current->it_prof_value;
    interval = current->it_prof_incr;
    break;
    default:
    return(-EINVAL);
    }
    jiffiestotv(val, &value->it_value);
    jiffiestotv(interval, &value->it_interval);
    return 0;
    }
    ²éѯµÄ¹ý³ÌÈçÏ£º
    £¨1£©Ê×ÏÈ£¬Óþֲ¿±äÁ¿valºÍinterval·Ö±ð±íʾ´ý²éѯ¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄµ±Ç°ÖµºÍ³õʼֵ¡£
    £¨2£©Èç¹ûwhich£½ITIMER_REAL£¬Ôò²éѯµ±Ç°½ø³ÌµÄITIMER_REAL¼ä¸ô¶¨Ê±Æ÷¡£ÓÚÊÇ´Ócurrent->it_real_incrÖеõ½ITIMER_REAL¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄ³õʼֵ£¬²¢½«Æä±£´æµ½interval¾Ö²¿±äÁ¿ÖС£¶ø¶ÔÓÚ¼ä¸ô¼ÆÊýÆ÷µÄµ±Ç°Öµ£¬ÓÉÓÚITITMER_REAL¼ä¸ô¶¨Ê±Æ÷ÊÇͨ¹ýreal_timerÕâ¸öÄں˶¯Ì¬¶¨Ê±Æ÷À´ÊµÏֵģ¬Òò´Ë²»ÄÜͨ¹ýcurrent->it_real_valueÀ´»ñµÃITIMER_REAL¼ä¸ô¶¨Ê±Æ÷µÄ¼ä¸ô¼ÆÊýÆ÷µÄµ±Ç°Öµ£¬¶ø±ØÐëͨ¹ýreal_timerÀ´µÃµ½Õâ¸öÖµ¡£Îª´ËÏÈÓÃtimer_pending()º¯ÊýÀ´ÅжÏcurrent->real_timerÊÇ·ñÒѱ»Æ𶯡£Èç¹ûδÆô¶¯£¬Ôò˵Ã÷ITIMER_REAL¼ä¸ô¶¨Ê±Æ÷ҲδÆô¶¯£¬Òò´ËÆä¼ä¸ô¼ÆÊýÆ÷µÄµ±Ç°Öµ¿Ï¶¨ÊÇ0¡£Òò´Ë½«val±äÁ¿¼òµ¥µØÖÃ0¾Í¿ÉÒÔÁË¡£Èç¹ûÒѾ­Æô¶¯£¬Ôò¼ä¸ô¼ÆÊýÆ÷µÄµ±Ç°ÖµÓ¦¸ÃµÈÓÚ£¨timer_real.expires£­jiffies£©¡£
    £¨3£©Èç¹ûwhich£½ITIMER_VIRT£¬Ôò²éѯµ±Ç°½ø³ÌµÄITIMER_VIRT¼ä¸ô¶¨Ê±Æ÷¡£ÓÚÊǼòµ¥µØ½«¼ÆÊýÆ÷³õÖµit_virt_incrºÍµ±Ç°Öµit_virt_value·Ö±ð±£´æµ½¾Ö²¿±äÁ¿intervalºÍvalÖС£
    £¨4£©Èç¹ûwhich£½ITIMER_PROF£¬Ôò²éѯµ±Ç°½ø³ÌµÄITIMER_PROF¼ä¸ô¶¨Ê±Æ÷¡£ÓÚÊǼòµ¥µØ½«¼ÆÊýÆ÷³õÖµit_prof_incrºÍµ±Ç°Öµit_prof_value·Ö±ð±£´æµ½¾Ö²¿±äÁ¿intervalºÍvalÖС£
    £¨5£©×îºó£¬Í¨¹ýת»»º¯Êýjiffiestotv()½«valºÍintervalת»»³Étimeval¸ñʽµÄʱ¼äÖµ£¬²¢±£´æµ½value->it_valueºÍvalue->it_intervalÖУ¬×÷Ϊ²éѯ½á¹û·µ»Ø¡£

    7£®7£®3£®2 setitimer()ϵͳµ÷ÓõÄʵÏÖ
    º¯Êýsys_setitimer()²»½öÉèÖõ÷Óýø³ÌµÄÖ¸¶¨¼ä¸ô¶¨Ê±Æ÷£¬¶øÇÒ»¹·µ»Ø¸Ã¼ä¸ô¶¨Ê±Æ÷µÄÔ­ÓÐÐÅÏ¢¡£ËüÓÐÈý¸ö²ÎÊý£º£¨1£©which£¬º¬ÒåÓësys_getitimer()ÖеIJÎÊýÏàͬ¡££¨2£©ÊäÈë²ÎÊývalue£¬Ö¸ÏòÓû§¿Õ¼äÖеÄÒ»¸öitimerval½á¹¹£¬º¬ÓдýÉèÖõÄÐÂÖµ¡££¨3£©Êä³ö²ÎÊýovalue£¬Ö¸ÏòÓû§¿Õ¼äÖеÄÒ»¸öitimerval½á¹¹£¬ÓÃÓÚ½ÓÊÕ¼ä¸ô¶¨Ê±Æ÷µÄÔ­ÓÐÐÅÏ¢¡£
    ¸Ãº¯ÊýµÄÔ´ÂëÈçÏ£¨kernel/itimer.c£©£º
    /* SMP: Again, only we play with our itimers, and signals are SMP safe
    * now so that is not an issue at all anymore.
    */
    asmlinkage long sys_setitimer(int which, struct itimerval *value,
    struct itimerval *ovalue)
    {
    struct itimerval set_buffer, get_buffer;
    int error;

    if (value) {
    if(copy_from_user(&set_buffer, value, sizeof(set_buffer)))
    return -EFAULT;
    } else
    memset((char *) &set_buffer, 0, sizeof(set_buffer));

    error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : 0);
    if (error || !ovalue)
    return error;

    if (copy_to_user(ovalue, &get_buffer, sizeof(get_buffer)))
    return -EFAULT;
    return 0;
    }
    ¶Ô¸Ãº¯ÊýµÄ×¢ÊÍÈçÏ£º
    £¨1£©ÔÚÊäÈë²ÎÊýÖ¸Õëvalue·Ç¿ÕµÄÇé¿öÏ£¬µ÷ÓÃcopy_from_user()ºê½«Óû§¿Õ¼äÖеĴýÉèÖÃÐÅÏ¢¿½±´µ½Äں˿ռäÖеÄset_buffer½á¹¹±äÁ¿ÖС£Èç¹ûvalueÖ¸ÕëΪ¿Õ£¬Ôò¼òµ¥µØ½«set_buffer½á¹¹±äÁ¿È«²¿ÖÃ0¡£
    £¨2£©µ÷ÓÃdo_setitimer()º¯ÊýÍê³Éʵ¼ÊµÄÉèÖòÙ×÷¡£Èç¹ûÊä³ö²ÎÊýovalueÖ¸ÕëÓÐЧ£¬ÔòÒÔÄں˱äÁ¿get_bufferµÄµØÖ·×÷Ϊdo_setitimer()º¯ÊýµÄµÚÈýÄǸöµ÷ÓòÎÊý£¬ÕâÑùµ±do_setitimer()º¯Êý·µ»Øʱ£¬get_buffer½á¹¹±äÁ¿Öоͽ«º¬Óе±Ç°½ø³ÌµÄÖ¸¶¨¼ä¸ô¶¨Ê±Æ÷µÄÔ­À´ÐÅÏ¢¡£Do_setitimer()º¯Êý·µ»Ø0Öµ±íʾ³É¹¦£¬·Ç0Öµ±íʾʧ°Ü¡£
    £¨3£©ÔÚdo_setitimer()º¯Êý·µ»Ø·Ç0ÖµµÄÇé¿öÏ£¬»òÕßovalueÖ¸ÕëΪ¿ÕµÄÇé¿öÏ£¨²»ÐèÒªÊä³ö¼ä¸ô¶¨Ê±Æ÷µÄÔ­ÓÐÐÅÏ¢£©£¬º¯Êý¾Í¿ÉÒÔÖ±½Ó·µ»ØÁË¡£
    £¨4£©Èç¹ûovalueÖ¸Õë·Ç¿Õ£¬µ÷ÓÃcopy_to_user()ºê½«get_buffer()½á¹¹±äÁ¿ÖÐÖµ¿½±´µ½ovalueËùÖ¸ÏòµÄÓû§¿Õ¼äÖÐÈ¥£¬ÒÔ±ãÈÃÓû§µÃµ½Ö¸¶¨¼ä¸ô¶¨Ê±Æ÷µÄÔ­ÓÐÐÅÏ¢Öµ¡£

    º¯Êýdo_setitimer()µÄÔ´ÂëÈçÏ£¨kernel/itimer.c£©£º
    int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
    {
    register unsigned long i, j;
    int k;

    i = tvtojiffies(&value->it_interval);
    j = tvtojiffies(&value->it_value);
    if (ovalue && (k = do_getitimer(which, ovalue)) < 0)
    return k;
    switch (which) {
    case ITIMER_REAL:
    del_timer_sync(¤t->real_timer);
    current->it_real_value = j;
    current->it_real_incr = i;
    if (!j)
    break;
    if (j > (unsigned long) LONG_MAX)
    j = LONG_MAX;
    i = j + jiffies;
    current->real_timer.expires = i;
    add_timer(¤t->real_timer);
    break;
    case ITIMER_VIRTUAL:
    if (j)
    j++;
    current->it_virt_value = j;
    current->it_virt_incr = i;
    break;
    case ITIMER_PROF:
    if (j)
    j++;
    current->it_prof_value = j;
    current->it_prof_incr = i;
    break;
    default:
    return -EINVAL;
    }
    return 0;
    }
    ¶Ô¸Ãº¯ÊýµÄ×¢ÊÍÈçÏ£º
    £¨1£©Ê×Ïȵ÷ÓÃtvtojiffies£¨£©º¯Êý½«timeval¸ñʽµÄ³õʼֵºÍµ±Ç°Öµ×ª»»³ÉÒÔʱÖӵδðΪµ¥Î»µÄʱ¼äÖµ¡£²¢·Ö±ð±£´æÔÚ¾Ö²¿±äÁ¿iºÍjÖС£
    £¨2£©Èç¹ûovalueÖ¸Õë·Ç¿Õ£¬Ôòµ÷ÓÃdo_getitimer()º¯Êý²éѯָ¶¨¼ä¸ô¶¨Ê±Æ÷µÄÔ­À´ÐÅÏ¢¡£Èç¹ûdo_getitimer()º¯Êý·µ»Ø¸ºÖµ£¬ËµÃ÷³ö´í¡£Òò´Ë¾ÍÒªÖ±½Ó·µ»Ø´íÎóÖµ¡£·ñÔò¼ÌÐøÏòÏÂÖ´ÐпªÊ¼ÕæÕýµØÉèÖÃÖ¸¶¨µÄ¼ä¸ô¶¨Ê±Æ÷¡£
    £¨3£©Èç¹ûwhich=ITITMER_REAL£¬±íʾÉèÖÃITIMER_REAL¼ä¸ô¶¨Ê±Æ÷¡££¨a£©µ÷ÓÃdel_timer_sync()º¯Êý£¨¸Ãº¯ÊýÔÚµ¥CPUϵͳÖоÍÊÇdel_timer()º¯Êý£©½«µ±Ç°½ø³ÌµÄreal_timer¶¨Ê±Æ÷´ÓÄں˶¯Ì¬¶¨Ê±Æ÷Á´±íÖÐɾ³ý¡££¨b£©½«it_real_incrºÍit_real_value·Ö±ðÉèÖÃΪ¾Ö²¿±äÁ¿iºÍj¡££¨c£©Èç¹ûj=0£¬ËµÃ÷²»±ØÆô¶¯real_timer¶¨Ê±Æ÷£¬Òò´ËÖ´ÐÐbreakÓï¾äÍ˳öswitch¡­case¿ØÖƽṹ£¬¶øÖ±½Ó·µ»Ø¡££¨d£©½«real_timerµÄexpires³ÉÔ±ÉèÖóɣ¨jiffies£«µ±Ç°Öµj£©£¬È»ºóµ÷ÓÃadd_timer()º¯Êý½«µ±Ç°½ø³ÌµÄreal_timer¶¨Ê±Æ÷¼ÓÈëµ½Äں˶¯Ì¬¶¨Ê±Æ÷Á´±íÖУ¬´Ó¶øÆô¶¯¸Ã¶¨Ê±Æ÷¡£
    £¨4£©Èç¹ûwhich=ITIMER_VIRT£¬Ôò¼òµ¥µØÓþֲ¿±äÁ¿iºÍjµÄÖµ·Ö±ð¸üÐÂit_virt_incrºÍit_virt_value¾Í¿ÉÒÔÁË¡£
    £¨5£©Èç¹ûwhich=ITIMER_PROF£¬Ôò¼òµ¥µØÓþֲ¿±äÁ¿iºÍjµÄÖµ·Ö±ð¸üÐÂit_prof_incrºÍit_prof_value¾Í¿ÉÒÔÁË¡£
    £¨6£©×îºó£¬·µ»Ø0Öµ±íʾ³É¹¦¡£

    7£®7£®3£®3 alarmϵͳµ÷ÓÃ
    ϵͳµ÷ÓÃalarm¿ÉÒÔÈõ÷Óýø³ÌÔÚÖ¸¶¨µÄÃëÊý¼ä¸ôºóÊÕµ½Ò»¸öSIGALRMÐźš£ËüÖ»ÓÐÒ»¸ö²ÎÊýseconds£¬Ö¸¶¨ÒÔÃëÊý¼ÆµÄ¶¨Ê±¼ä¸ô¡£º¯Êýsys_alarm()µÄÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    /*
    * For backwards compatibility? This can be done in libc so Alpha
    * and all newer ports shouldn't need it.
    */
    asmlinkage unsigned long sys_alarm(unsigned int seconds)
    {
    struct itimerval it_new, it_old;
    unsigned int oldalarm;

    it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
    it_new.it_value.tv_sec = seconds;
    it_new.it_value.tv_usec = 0;
    do_setitimer(ITIMER_REAL, &it_new, &it_old);
    oldalarm = it_old.it_value.tv_sec;
    /* ehhh.. We can't return 0 if we have an alarm pending.. */
    /* And we'd better return too much than too little anyway */
    if (it_old.it_value.tv_usec)
    oldalarm++;
    return oldalarm;
    }
    Õâ¸öϵͳµ÷ÓÃʵ¼ÊÉϾÍÊÇÆô¶¯½ø³ÌµÄITIMER_REAL¼ä¸ô¶¨Ê±Æ÷¡£Òò´ËËüÍêÈ«¿É·Åµ½Óû§¿Õ¼äµÄCº¯Êý¿â£¨±ÈÈçlibcºÍglibc£©ÖÐÀ´ÊµÏÖ¡£µ«ÊÇΪÁ˱£´ËÄں˵ÄÏòºó¼æÈÝÐÔ£¬2.4.0°æµÄÄÚºËÈÔÈ»½«Õâ¸ösyscall·ÅÔÚÄں˿ռäÖÐÀ´ÊµÏÖ¡£º¯Êýsys_alarm£¨£©µÄʵÏÖ¹ý³ÌÈçÏ£º
    £¨1£©¸ù¾Ý²ÎÊýsecondsµÄÖµ¹¹ÔìÒ»¸öitimerval½á¹¹±äÁ¿it_new¡£×¢Ò⣡ÓÉÓÚalarmÆô¶¯µÄITIMER_REAL¼ä¸ô¶¨Ê±Æ÷ÊÇÒ»´ÎÐÔ¶ø²»ÊÇÑ­»·Öظ´µÄ£¬Òò´Ëit_new±äÁ¿ÖеÄit_interval³ÉÔ±Ò»¶¨ÒªÉèÖÃΪ0¡£
    £¨2£©µ÷Óú¯Êýdo_setitimer()º¯ÊýÒÔй¹ÔìµÄ¶¨Ê±Æ÷it_newÀ´Æô¶¯µ±Ç°½ø³ÌµÄITIMER_REAL¶¨Ê±Æ÷£¬Í¬Ê±½«¸Ã¼ä¸ô¶¨Ê±Æ÷µÄÔ­¶¨Ê±¼ä¸ô±£´æµ½¾Ö²¿±äÁ¿it_oldÖС£
    £¨3£©·µ»ØÖµoldalarm±íʾÒÔÃëÊý¼ÆµÄITIMER_REAL¼ä¸ô¶¨Ê±Æ÷µÄÔ­¶¨Ê±¼ä¸ôÖµ¡£Òò´ËÏÈ°Ñit_old.it_value.tv_sec¸³¸øoldalarm£¬²¢ÇÒÔÚit_old.it_value.tv_usec·Ç0µÄÇé¿öÏ£¬½«oldalarmµÄÖµ¼Ó1£¨Ò²¼´²»×ã1Ãë²¹×ã1Ã룩¡£

  3. hfh08 ÓÚ 2006-08-21 00:26:10·¢±í:

    7£®6 Äں˶¨Ê±Æ÷»úÖÆ
    LinuxÄÚºË2.4°æÖÐÈ¥µôÁËÀÏ°æ±¾ÄÚºËÖеľ²Ì¬¶¨Ê±Æ÷»úÖÆ£¬¶øÖ»Áô϶¯Ì¬¶¨Ê±Æ÷¡£ÏàÓ¦µØÔÚtimer_bh()º¯ÊýÖÐÒ²²»ÔÙͨ¹ýrun_old_timers()º¯ÊýÀ´ÔËÐÐÀÏʽµÄ¾²Ì¬¶¨Ê±Æ÷¡£¶¯Ì¬¶¨Ê±Æ÷Ó뾲̬¶¨Ê±Æ÷Õâ¶þ¸ö¸ÅÄîÊÇÏà¶ÔÓÚLinuxÄں˶¨Ê±Æ÷»úÖƵĿÉÀ©Õ¹¹¦ÄܶøÑԵģ¬¶¯Ì¬¶¨Ê±Æ÷ÊÇÖ¸Äں˵Ķ¨Ê±Æ÷¶ÓÁÐÊÇ¿ÉÒÔ¶¯Ì¬±ä»¯µÄ£¬È»¶ø¾Í¶¨Ê±Æ÷±¾Éí¶øÑÔ£¬¶þÕß²¢ÎÞ±¾ÖʵÄÇø±ð¡£¿¼Âǵ½¾²Ì¬¶¨Ê±Æ÷»úÖƵÄÄÜÁ¦ÓÐÏÞ£¬Òò´ËLinuxÄÚºË2.4°æÖÐÍêÈ«È¥µôÁËÒÔÇ°µÄ¾²Ì¬¶¨Ê±Æ÷»úÖÆ¡£

    7£®6£®1 LinuxÄں˶Զ¨Ê±Æ÷µÄÃèÊö
    LinuxÔÚinclude/linux/timer.hÍ·ÎļþÖж¨ÒåÁËÊý¾Ý½á¹¹timer_listÀ´ÃèÊöÒ»¸öÄں˶¨Ê±Æ÷£º
    struct timer_list {
    struct list_head list;
    unsigned long expires;
    unsigned long data;
    void (*function)(unsigned long);
    };
    ¸÷Êý¾Ý³ÉÔ±µÄº¬ÒåÈçÏ£º
    £¨1£©Ë«ÏòÁ´±íÔªËØlist£ºÓÃÀ´½«¶à¸ö¶¨Ê±Æ÷Á¬½Ó³ÉÒ»ÌõË«ÏòÑ­»·¶ÓÁС£
    £¨2£©expires£ºÖ¸¶¨¶¨Ê±Æ÷µ½ÆÚµÄʱ¼ä£¬Õâ¸öʱ¼ä±»±íʾ³É×ÔϵͳÆô¶¯ÒÔÀ´µÄʱÖӵδð¼ÆÊý£¨Ò²¼´Ê±ÖÓ½ÚÅÄÊý£©¡£µ±Ò»¸ö¶¨Ê±Æ÷µÄexpiresֵСÓÚ»òµÈÓÚjiffies±äÁ¿Ê±£¬ÎÒÃǾÍ˵Õâ¸ö¶¨Ê±Æ÷ÒѾ­³¬Ê±»òµ½ÆÚÁË¡£ÔÚ³õʼ»¯Ò»¸ö¶¨Ê±Æ÷ºó£¬Í¨³£°ÑËüµÄexpiresÓòÉèÖóɵ±Ç°expires±äÁ¿µÄµ±Ç°Öµ¼ÓÉÏij¸öʱ¼ä¼ä¸ôÖµ£¨ÒÔʱÖӵδð´ÎÊý¼Æ£©¡£
    £¨3£©º¯ÊýÖ¸Õëfunction£ºÖ¸ÏòÒ»¸ö¿ÉÖ´Ðк¯Êý¡£µ±¶¨Ê±Æ÷µ½ÆÚʱ£¬Äں˾ÍÖ´ÐÐfunctionËùÖ¸¶¨µÄº¯Êý¡£¶ødataÓòÔò±»ÄÚºËÓÃ×÷functionº¯ÊýµÄµ÷ÓòÎÊý¡£

    Äں˺¯Êýinit_timer()ÓÃÀ´³õʼ»¯Ò»¸ö¶¨Ê±Æ÷¡£Êµ¼ÊÉÏ£¬Õâ¸ö³õʼ»¯º¯Êý½ö½ö½«½á¹¹ÖеÄlist³ÉÔ±³õʼ»¯Îª¿Õ¡£ÈçÏÂËùʾ£¨include/linux/timer.h£©£º
    static inline void init_timer(struct timer_list * timer)
    {
    timer->list.next = timer->list.prev = NULL;
    }
    ÓÉÓÚ¶¨Ê±Æ÷ͨ³£±»Á¬½ÓÔÚÒ»¸öË«ÏòÑ­»·¶ÓÁÐÖеȴýÖ´ÐУ¨´ËʱÎÒÃÇ˵¶¨Ê±Æ÷´¦ÓÚpending״̬£©¡£Òò´Ëº¯Êýtime_pending()¾Í¿ÉÒÔÓÃlist³ÉÔ±ÊÇ·ñΪ¿ÕÀ´ÅжÏÒ»¸ö¶¨Ê±Æ÷ÊÇ·ñ´¦ÓÚpending״̬¡£ÈçÏÂËùʾ£¨include/linux/timer.h£©£º
    static inline int timer_pending (const struct timer_list * timer)
    {
    return timer->list.next != NULL;
    }

    l ʱ¼ä±È½Ï²Ù×÷
    ÔÚ¶¨Ê±Æ÷Ó¦ÓÃÖо­³£ÐèÒª±È½ÏÁ½¸öʱ¼äÖµ£¬ÒÔÈ·¶¨timerÊÇ·ñ³¬Ê±£¬ËùÒÔLinuxÄÚºËÔÚtimer.hÍ·ÎļþÖж¨ÒåÁË4¸öʱ¼ä¹Øϵ±È½Ï²Ù×÷ºê¡£ÕâÀïÎÒÃÇ˵ʱ¿ÌaÔÚʱ¿ÌbÖ®ºó£¬¾ÍÒâζ×Åʱ¼äÖµa¡Ýb¡£LinuxÇ¿ÁÒÍƼöÓû§Ê¹ÓÃËüËù¶¨ÒåµÄÏÂÁÐ4¸öʱ¼ä±È½Ï²Ù×÷ºê£¨include/linux/timer.h£©£º
    #define time_after(a,b) ((long)(b) - (long)(a) < 0)
    #define time_before(a,b) time_after(b,a)

    #define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)
    #define time_before_eq(a,b) time_after_eq(b,a)

    7£®6£®2 ¶¯Ì¬Äں˶¨Ê±Æ÷»úÖƵÄÔ­Àí
    LinuxÊÇÔõÑùΪÆäÄں˶¨Ê±Æ÷»úÖÆÌṩ¶¯Ì¬À©Õ¹ÄÜÁ¦µÄÄØ£¿Æä¹Ø¼ü¾ÍÔÚÓÚ¡°¶¨Ê±Æ÷ÏòÁ¿¡±µÄ¸ÅÄî¡£Ëùν¡°¶¨Ê±Æ÷ÏòÁ¿¡±¾ÍÊÇÖ¸ÕâÑùÒ»ÌõË«ÏòÑ­»·¶¨Ê±Æ÷¶ÓÁУ¨¶ÔÁÐÖеÄÿһ¸öÔªËض¼ÊÇÒ»¸ötimer_list½á¹¹£©£º¶ÔÁÐÖеÄËùÓж¨Ê±Æ÷¶¼ÔÚͬһ¸öʱ¿Ìµ½ÆÚ£¬Ò²¼´¶ÔÁÐÖеÄÿһ¸ötimer_list½á¹¹¶¼¾ßÓÐÏàͬµÄexpiresÖµ¡£ÏÔÈ»£¬¿ÉÒÔÓÃÒ»¸ötimer_list½á¹¹ÀàÐ͵ÄÖ¸ÕëÀ´±íʾһ¸ö¶¨Ê±Æ÷ÏòÁ¿¡£
    ÏÔÈ»£¬¶¨Ê±Æ÷expires³ÉÔ±µÄÖµÓëjiffies±äÁ¿µÄ²îÖµ¾ö¶¨ÁËÒ»¸ö¶¨Ê±Æ÷½«Ôڶ೤ʱ¼äºóµ½ÆÚ¡£ÔÚ32λϵͳÖУ¬Õâ¸öʱ¼ä²îÖµµÄ×î´óÖµÓ¦¸ÃÊÇ0xffffffff¡£Òò´ËÈç¹ûÊÇ»ùÓÚ¡°¶¨Ê±Æ÷ÏòÁ¿¡±»ù±¾¶¨Ò壬Äں˽«ÖÁÉÙҪά»¤0xffffffff¸ötimer_list½á¹¹ÀàÐ͵ÄÖ¸Õ룬ÕâÏÔÈ»ÊDz»ÏÖʵµÄ¡£
    ÁíÒ»·½Ã棬´ÓÄں˱¾ÉíÕâ¸ö½Ç¶È¿´£¬ËüËù¹ØÐĵĶ¨Ê±Æ÷ÏÔÈ»²»ÊÇÄÇЩÒѾ­¹ýÆÚ¶ø±»Ö´ÐйýµÄ¶¨Ê±Æ÷£¨ÕâЩ¶¨Ê±Æ÷ÍêÈ«¿ÉÒÔ±»¶ªÆú£©£¬Ò²²»ÊÇÄÇЩҪ¾­¹ýºÜ³¤Ê±¼ä²Å»áµ½ÆڵĶ¨Ê±Æ÷£¬¶øÊÇÄÇЩµ±Ç°ÒѾ­µ½ÆÚ»òÕßÂíÉϾÍÒªµ½ÆڵĶ¨Ê±Æ÷£¨×¢Ò⣡ʱ¼ä¼ä¸ôÊÇÒԵδð´ÎÊýΪ¼ÆÊýµ¥Î»µÄ£©¡£
    »ùÓÚÉÏÊö¿¼ÂÇ£¬²¢¼Ù¶¨Ò»¸ö¶¨Ê±Æ÷Òª¾­¹ýinterval¸öʱÖӵδðºó²Åµ½ÆÚ£¨interval£½expires£­jiffies£©£¬ÔòLinux²ÉÓÃÁËÏÂÁÐ˼ÏëÀ´ÊµÏÖÆ䶯̬Äں˶¨Ê±Æ÷»úÖÆ£º¶ÔÓÚÄÇЩ0¡Üinterval¡Ü255µÄ¶¨Ê±Æ÷£¬LinuxÑϸñ°´ÕÕ¶¨Ê±Æ÷ÏòÁ¿µÄ»ù±¾ÓïÒåÀ´×éÖ¯ÕâЩ¶¨Ê±Æ÷£¬Ò²¼´LinuxÄÚºË×î¹ØÐÄÄÇЩÔÚ½ÓÏÂÀ´µÄ255¸öʱÖÓ½ÚÅÄÄÚ¾ÍÒªµ½ÆڵĶ¨Ê±Æ÷£¬Òò´Ë½«ËüÃÇ°´ÕÕ¸÷×Ô²»Í¬µÄexpiresÖµ×éÖ¯³É256¸ö¶¨Ê±Æ÷ÏòÁ¿¡£¶ø¶ÔÓÚÄÇЩ256¡Üinterval¡Ü0xffffffffµÄ¶¨Ê±Æ÷£¬ÓÉÓÚËûÃÇÀëµ½ÆÚ»¹ÓÐÒ»¶Îʱ¼ä£¬Òò´ËÄں˲¢²»¹ØÐÄËûÃÇ£¬¶øÊǽ«ËüÃÇÒÔÒ»ÖÖÀ©Õ¹µÄ¶¨Ê±Æ÷ÏòÁ¿ÓïÒ壨»ò³ÆΪ¡°ËÉÉ¢µÄ¶¨Ê±Æ÷ÏòÁ¿ÓïÒ塱£©½øÐÐ×éÖ¯¡£Ëùν¡°ËÉÉ¢µÄ¶¨Ê±Æ÷ÏòÁ¿ÓïÒ塱¾ÍÊÇÖ¸£º¸÷¶¨Ê±Æ÷µÄexpiresÖµ¿ÉÒÔ»¥²»ÏàͬµÄÒ»¸ö¶¨Ê±Æ÷¶ÓÁС£
    ¾ßÌåµÄ×éÖ¯·½°¸¿ÉÒÔ·ÖΪÁ½´ó²¿·Ö£º
    £¨1£©¶ÔÓÚÄÚºË×î¹ØÐĵġ¢intervalÖµÔÚ£Û0£¬255£ÝÖ®¼äµÄÇ°256¸ö¶¨Ê±Æ÷ÏòÁ¿£¬ÄÚºËÊÇÕâÑù×éÖ¯ËüÃǵģºÕâ256¸ö¶¨Ê±Æ÷ÏòÁ¿±»×éÖ¯ÔÚÒ»Æð×é³ÉÒ»¸ö¶¨Ê±Æ÷ÏòÁ¿Êý×飬²¢×÷ΪÊý¾Ý½á¹¹timer_vec_rootµÄÒ»²¿·Ö£¬¸ÃÊý¾Ý½á¹¹¶¨ÒåÔÚkernel/timer.cÎļþÖУ¬ÈçÏÂÊö´úÂë¶ÎËùʾ£º
    /*
    * Event timer code
    */
    #define TVN_BITS 6
    #define TVR_BITS 8
    #define TVN_SIZE (1 << TVN_BITS)
    #define TVR_SIZE (1 << TVR_BITS)
    #define TVN_MASK (TVN_SIZE - 1)
    #define TVR_MASK (TVR_SIZE - 1)

    struct timer_vec {
    int index;
    struct list_head vec[TVN_SIZE];
    };

    struct timer_vec_root {
    int index;
    struct list_head vec[TVR_SIZE];
    };

    static struct timer_vec tv5;
    static struct timer_vec tv4;
    static struct timer_vec tv3;
    static struct timer_vec tv2;
    static struct timer_vec_root tv1;

    static struct timer_vec * const tvecs[] = {
    (struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5
    };

    #define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0]))
    »ùÓÚÊý¾Ý½á¹¹timer_vec_root£¬Linux¶¨ÒåÁËÒ»¸öÈ«¾Ö±äÁ¿tv1£¬ÒÔ±íʾÄÚºËËù¹ØÐĵÄÇ°256¸ö¶¨Ê±Æ÷ÏòÁ¿¡£ÕâÑùÄÚºËÔÚ´¦ÀíÊÇ·ñÓе½ÆÚ¶¨Ê±Æ÷ʱ£¬Ëü¾ÍÖ»´Ó¶¨Ê±Æ÷ÏòÁ¿Êý×étv1.vec£Û256£ÝÖеÄij¸ö¶¨Ê±Æ÷ÏòÁ¿ÄÚ½øÐÐɨÃè¡£¶øtv1µÄindex×Ö¶ÎÔòÖ¸¶¨µ±Ç°ÕýÔÚɨÃ趨ʱÆ÷ÏòÁ¿Êý×étv1.vec£Û256£ÝÖеÄÄÄÒ»¸ö¶¨Ê±Æ÷ÏòÁ¿£¬Ò²¼´¸ÃÊý×éµÄË÷Òý£¬Æä³õֵΪ0£¬×î´óֵΪ255£¨ÒÔ256Ϊģ£©¡£Ã¿¸öʱÖÓ½ÚÅÄʱindex×ֶζ¼»á¼Ó1¡£ÏÔÈ»£¬index×Ö¶ÎËùÖ¸¶¨µÄ¶¨Ê±Æ÷ÏòÁ¿tv1.vec£Ûindex£ÝÖаüº¬Á˵±Ç°Ê±ÖÓ½ÚÅÄÄÚÒѾ­µ½ÆÚµÄËùÓж¯Ì¬¶¨Ê±Æ÷¡£¶ø¶¨Ê±Æ÷ÏòÁ¿tv1.vec£Ûindex£«k£ÝÔò°üº¬Á˽ÓÏÂÀ´µÚk¸öʱÖÓ½ÚÅÄʱ¿Ì½«µ½ÆÚµÄËùÓж¯Ì¬¶¨Ê±Æ÷¡£µ±indexÖµÓÖÖØбäΪ0ʱ£¬¾ÍÒâζ×ÅÄÚºËÒѾ­É¨ÃèÁËtv1±äÁ¿ÖеÄËùÓÐ256¸ö¶¨Ê±Æ÷ÏòÁ¿¡£ÔÚÕâÖÖÇé¿öϾͱØÐ뽫ÄÇЩÒÔËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿ÓïÒåÀ´×éÖ¯µÄ¶¨Ê±Æ÷ÏòÁ¿²¹³äµ½tv1ÖÐÀ´¡£
    £¨2£©¶ø¶ÔÓÚÄں˲»¹ØÐĵġ¢intervalÖµÔÚ£Û0xff£¬0xffffffff£ÝÖ®¼äµÄ¶¨Ê±Æ÷£¬ËüÃǵĵ½ÆÚ½ôÆȳ̶ÈÒ²ËæÆäintervalÖµµÄ²»Í¬¶ø²»Í¬¡£ÏÔÈ»intervalֵԽС£¬¶¨Ê±Æ÷½ôÆȳ̶ÈÒ²Ô½¸ß¡£Òò´ËÔÚ½«ËüÃÇÒÔËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿½øÐÐ×é֯ʱҲӦ¸ÃÇø±ð¶Ô´ý¡£Í¨³££¬¶¨Ê±Æ÷µÄintervalֵԽС£¬ËüËù´¦µÄ¶¨Ê±Æ÷ÏòÁ¿µÄËÉÉ¢¶ÈÒ²¾ÍÔ½µÍ£¨Ò²¼´ÏòÁ¿Öеĸ÷¶¨Ê±Æ÷µÄexpiresÖµÏà²îԽС£©£»¶øintervalÖµÔ½´ó£¬ËüËù´¦µÄ¶¨Ê±Æ÷ÏòÁ¿µÄËÉÉ¢¶ÈÒ²¾ÍÔ½´ó£¨Ò²¼´ÏòÁ¿Öеĸ÷¶¨Ê±Æ÷µÄexpiresÖµÏà²îÔ½´ó£©¡£
    Äں˹涨£¬¶ÔÓÚÄÇЩÂú×ãÌõ¼þ£º0x100¡Üinterval¡Ü0x3fffµÄ¶¨Ê±Æ÷£¬Ö»Òª±í´ïʽ£¨interval>>8£©¾ßÓÐÏàֵͬµÄ¶¨Ê±Æ÷¶¼½«±»×éÖ¯ÔÚͬһ¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿ÖС£Òò´Ë£¬Îª×éÖ¯ËùÓÐÂú×ãÌõ¼þ0x100¡Üinterval¡Ü0x3fffµÄ¶¨Ê±Æ÷£¬¾ÍÐèÒª26£½64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿¡£Í¬ÑùµØ£¬Îª·½±ãÆð¼û£¬Õâ64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿Ò²·ÅÔÚÒ»ÆðÐγÉÊý×飬²¢×÷ΪÊý¾Ý½á¹¹timer_vecµÄÒ»²¿·Ö¡£»ùÓÚÊý¾Ý½á¹¹timer_vec£¬Linux¶¨ÒåÁËÈ«¾Ö±äÁ¿tv2£¬À´±íʾÕâ64ÌõËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿¡£ÈçÉÏÊö´úÂë¶ÎËùʾ¡£
    ¶ÔÓÚÄÇЩÂú×ãÌõ¼þ0x4000¡Üinterval¡Ü0xfffffµÄ¶¨Ê±Æ÷£¬Ö»Òª±í´ïʽ£¨interval>>8£«6£©µÄÖµÏàͬµÄ¶¨Ê±Æ÷¶¼½«±»·ÅÔÚͬһ¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿ÖС£Í¬Ñù£¬Òª×éÖ¯ËùÓÐÂú×ãÌõ¼þ0x4000¡Üinterval¡Ü0xfffffµÄ¶¨Ê±Æ÷£¬Ò²ÐèÒª26£½64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿¡£ÀàËƵأ¬Õâ64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿Ò²¿ÉÒÔÓÃÒ»¸ötimer_vec½á¹¹À´ÃèÊö£¬ÏàÓ¦µØLinux¶¨ÒåÁËtv3È«¾Ö±äÁ¿À´±íʾÕâ64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿¡£
    ¶ÔÓÚÄÇЩÂú×ãÌõ¼þ0x100000¡Üinterval¡Ü0x3ffffffµÄ¶¨Ê±Æ÷£¬Ö»Òª±í´ïʽ£¨interval>>8£«6£«6£©µÄÖµÏàͬµÄ¶¨Ê±Æ÷¶¼½«±»·ÅÔÚͬһ¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿ÖС£Í¬Ñù£¬Òª×éÖ¯ËùÓÐÂú×ãÌõ¼þ0x100000¡Üinterval¡Ü0x3ffffffµÄ¶¨Ê±Æ÷£¬Ò²ÐèÒª26£½64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿¡£ÀàËƵأ¬Õâ64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿Ò²¿ÉÒÔÓÃÒ»¸ötimer_vec½á¹¹À´ÃèÊö£¬ÏàÓ¦µØLinux¶¨ÒåÁËtv4È«¾Ö±äÁ¿À´±íʾÕâ64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿¡£
    ¶ÔÓÚÄÇЩÂú×ãÌõ¼þ0x4000000¡Üinterval¡Ü0xffffffffµÄ¶¨Ê±Æ÷£¬Ö»Òª±í´ïʽ£¨interval>>8£«6£«6£«6£©µÄÖµÏàͬµÄ¶¨Ê±Æ÷¶¼½«±»·ÅÔÚͬһ¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿ÖС£Í¬Ñù£¬Òª×éÖ¯ËùÓÐÂú×ãÌõ¼þ0x4000000¡Üinterval¡Ü0xffffffffµÄ¶¨Ê±Æ÷£¬Ò²ÐèÒª26£½64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿¡£ÀàËƵأ¬Õâ64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿Ò²¿ÉÒÔÓÃÒ»¸ötimer_vec½á¹¹À´ÃèÊö£¬ÏàÓ¦µØLinux¶¨ÒåÁËtv5È«¾Ö±äÁ¿À´±íʾÕâ64¸öËÉÉ¢¶¨Ê±Æ÷ÏòÁ¿¡£

    ×îºó£¬ÎªÁËÒýÓ÷½±ã£¬Linux¶¨ÒåÁËÒ»¸öÖ¸ÕëÊý×étvecs£Û£Ý£¬À´·Ö±ðÖ¸Ïòtv1¡¢tv2¡¢¡­¡¢tv5½á¹¹±äÁ¿¡£ÈçÉÏÊö´úÂëËùʾ¡£
    Õû¸öÄں˶¨Ê±Æ÷»úÖƵÄ×ÜÌå½á¹¹ÈçÏÂͼ7£­8Ëùʾ£º

    7£®6£®3 Äں˶¯Ì¬¶¨Ê±Æ÷»úÖƵÄʵÏÖ
    ÔÚÄں˶¯Ì¬¶¨Ê±Æ÷»úÖƵÄʵÏÖÖУ¬ÓÐÈý¸ö²Ù×÷ʱ·Ç³£ÖØÒªµÄ£º£¨1£©½«Ò»¸ö¶¨Ê±Æ÷²åÈëµ½ËüÓ¦¸ÃËù´¦µÄ¶¨Ê±Æ÷ÏòÁ¿ÖС££¨2£©¶¨Ê±Æ÷µÄǨÒÆ£¬Ò²¼´½«Ò»¸ö¶¨Ê±Æ÷´ÓËüÔ­À´Ëù´¦µÄ¶¨Ê±Æ÷ÏòÁ¿Ç¨ÒƵ½ÁíÒ»¸ö¶¨Ê±Æ÷ÏòÁ¿ÖС££¨3£©É¨Ãè²¢Ö´Ðе±Ç°ÒѾ­µ½ÆڵĶ¨Ê±Æ÷¡£

    7£®6£®3£®1 ¶¯Ì¬¶¨Ê±Æ÷»úÖƵijõʼ»¯
    º¯Êýinit_timervecs()ʵÏÖ¶Ô¶¯Ì¬¶¨Ê±Æ÷»úÖƵijõʼ»¯¡£¸Ãº¯Êý½ö±»sched_init()³õʼ»¯Àý³ÌËùµ÷Ó᣶¯Ì¬¶¨Ê±Æ÷»úÖƳõʼ»¯¹ý³ÌµÄÖ÷ÒªÈÎÎñ¾ÍÊǽ«tv1¡¢tv2¡¢¡­¡¢tv5Õâ5¸ö½á¹¹±äÁ¿ÖеĶ¨Ê±Æ÷ÏòÁ¿Ö¸ÕëÊý×évec£Û£Ý³õʼ»¯ÎªNULL¡£ÈçÏÂËùʾ£¨kernel/timer.c£©£º
    void init_timervecs (void)
    {
    int i;

    for (i = 0; i < TVN_SIZE; i++) {
    INIT_LIST_HEAD(tv5.vec + i);
    INIT_LIST_HEAD(tv4.vec + i);
    INIT_LIST_HEAD(tv3.vec + i);
    INIT_LIST_HEAD(tv2.vec + i);
    }
    for (i = 0; i < TVR_SIZE; i++)
    INIT_LIST_HEAD(tv1.vec + i);
    }
    ÉÏÊöº¯ÊýÖеĺêTVN_SIZEÊÇÖ¸timer_vec½á¹¹ÀàÐÍÖеĶ¨Ê±Æ÷ÏòÁ¿Ö¸ÕëÊý×évec£Û£ÝµÄ´óС£¬ÖµÎª64¡£ºêTVR_SIZEÊÇÖ¸timer_vec_root½á¹¹ÀàÐÍÖеĶ¨Ê±Æ÷ÏòÁ¿Êý×évec£Û£ÝµÄ´óС£¬ÖµÎª256¡£

    7£®6£®3£®2 ¶¯Ì¬¶¨Ê±Æ÷µÄʱÖӵδð»ù×¼timer_jiffies
    ÓÉÓÚ¶¯Ì¬¶¨Ê±Æ÷ÊÇÔÚʱÖÓÖжϵÄBottom HalfÖб»Ö´Ðеģ¬¶ø´ÓTIMER_BHÏòÁ¿±»¼¤»îµ½Æätimer_bh()º¯ÊýÕæÕýÖ´ÐÐÕâ¶Îʱ¼äÄÚ¿ÉÄÜ»áÓм¸´ÎʱÖÓÖжϷ¢Éú¡£Òò´ËÄں˱ØÐë¼ÇסÉÏÒ»´ÎÔËÐж¨Ê±Æ÷»úÖÆÊÇʲôʱºò£¬Ò²¼´Äں˱ØÐë±£´æÉÏÒ»´ÎÔËÐж¨Ê±Æ÷»úÖÆʱµÄjiffiesÖµ¡£Îª´Ë£¬LinuxÔÚkernel/timer.cÎļþÖж¨ÒåÁËÈ«¾Ö±äÁ¿timer_jiffiesÀ´±íʾÉÏÒ»´ÎÔËÐж¨Ê±Æ÷»úÖÆʱµÄjiffiesÖµ¡£¸Ã±äÁ¿µÄ¶¨ÒåÈçÏÂËùʾ£º
    static unsigned long timer_jiffies;

    7£®6£®3£®3 ¶ÔÄں˶¯Ì¬¶¨Ê±Æ÷Á´±íµÄ±£»¤
    ÓÉÓÚÄں˶¯Ì¬¶¨Ê±Æ÷Á´±íÊÇÒ»ÖÖϵͳȫ¾Ö¹²Ïí×ÊÔ´£¬ÎªÁËʵÏÖ¶ÔËüµÄ»¥³â·ÃÎÊ£¬Linux¶¨ÒåÁËרÃŵÄ×ÔÐýËøtimerlist_lockÀ´±£»¤¡£ÈκÎÏëÒª·ÃÎʶ¯Ì¬¶¨Ê±Æ÷Á´±íµÄ´úÂë¶Î¶¼Ê×ÏȱØÐëÏȳÖÓиÃ×ÔÐýËø£¬²¢ÇÒÔÚ·ÃÎʽáÊøºóÊͷŸÃ×ÔÐýËø¡£Æ䶨ÒåÈçÏ£¨kernel/timer.c£©£º
    /* Initialize both explicitly - let's try to have them in the same cache line */
    spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED;

    7£®6£®3£®4 ½«Ò»¸ö¶¨Ê±Æ÷²åÈëµ½Á´±íÖÐ
    º¯Êýadd_timer()ÓÃÀ´½«²ÎÊýtimerÖ¸ÕëËùÖ¸ÏòµÄ¶¨Ê±Æ÷²åÈëµ½Ò»¸öºÏÊʵĶ¨Ê±Æ÷Á´±íÖС£ËüÊ×Ïȵ÷ÓÃtimer_pending()º¯ÊýÅжÏËùÖ¸¶¨µÄ¶¨Ê±Æ÷ÊÇ·ñÒѾ­Î»ÓÚÔÚij¸ö¶¨Ê±Æ÷ÏòÁ¿ÖеȴýÖ´ÐС£Èç¹ûÊÇ£¬Ôò²»½øÐÐÈκβÙ×÷£¬Ö»ÊÇ´òÓ¡Ò»ÌõÄں˸澯ÐÅÏ¢¾Í·µ»ØÁË£»Èç¹û²»ÊÇ£¬Ôòµ÷ÓÃinternal_add_timer()º¯ÊýÍê³Éʵ¼ÊµÄ²åÈë²Ù×÷¡£ÆäÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    void add_timer(struct timer_list *timer)
    {
    unsigned long flags;

    spin_lock_irqsave(&timerlist_lock, flags);
    if (timer_pending(timer))
    goto bug;
    internal_add_timer(timer);
    spin_unlock_irqrestore(&timerlist_lock, flags);
    return;
    bug:
    spin_unlock_irqrestore(&timerlist_lock, flags);
    printk("bug: kernel timer added twice at %p.\n",
    __builtin_return_address(0));
    }

    º¯Êýinternal_add_timer()ÓÃÓÚ½«Ò»¸ö²»´¦ÓÚÈκζ¨Ê±Æ÷ÏòÁ¿ÖеĶ¨Ê±Æ÷²åÈëµ½ËüÓ¦¸ÃËù´¦µÄ¶¨Ê±Æ÷ÏòÁ¿ÖÐÈ¥£¨¸ù¾Ý¶¨Ê±Æ÷µÄexpiresÖµÀ´¾ö¶¨£©¡£ÈçÏÂËùʾ£¨kernel/timer.c£©£º
    static inline void internal_add_timer(struct timer_list *timer)
    {
    /*
    * must be cli-ed when calling this
    */
    unsigned long expires = timer->expires;
    unsigned long idx = expires - timer_jiffies;
    struct list_head * vec;

    if (idx < TVR_SIZE) {
    int i = expires & TVR_MASK;
    vec = tv1.vec + i;
    } else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
    int i = (expires >> TVR_BITS) & TVN_MASK;
    vec = tv2.vec + i;
    } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
    int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
    vec = tv3.vec + i;
    } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
    int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
    vec = tv4.vec + i;
    } else if ((signed long) idx < 0) {
    /* can happen if you add a timer with expires == jiffies,
    * or you set a timer to go off in the past
    */
    vec = tv1.vec + tv1.index;
    } else if (idx <= 0xffffffffUL) {
    int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
    vec = tv5.vec + i;
    } else {
    /* Can only get here on architectures with 64-bit jiffies */
    INIT_LIST_HEAD(&timer->list);
    return;
    }
    /*
    * Timers are FIFO!
    */
    list_add(&timer->list, vec->prev);
    }
    ¶Ô¸Ãº¯ÊýµÄ×¢ÊÍÈçÏ£º
    £¨1£©Ê×ÏÈ£¬¼ÆË㶨ʱÆ÷µÄexpiresÖµÓëtimer_jiffiesµÄ²åÖµ£¨×¢Ò⣡ÕâÀïÓ¦¸ÃʹÓö¯Ì¬¶¨Ê±Æ÷×Ô¼ºµÄʱ¼ä»ù×¼£©£¬Õâ¸ö²îÖµ¾Í±íʾÕâ¸ö¶¨Ê±Æ÷Ïà¶ÔÓÚÉÏÒ»´ÎÔËÐж¨Ê±Æ÷»úÖƵÄÄǸöʱ¿Ì»¹ÐèÒª¶à³¤Ê±¼ä¼ä¸ô²Åµ½ÆÚ¡£¾Ö²¿±äÁ¿idx±£´æÕâ¸ö²îÖµ¡£
    £¨2£©¸ù¾ÝidxµÄֵȷ¶¨Õâ¸ö¶¨Ê±Æ÷Ó¦±»²åÈëµ½ÄÄÒ»¸ö¶¨Ê±Æ÷ÏòÁ¿ÖС£Æä¾ßÌåµÄÈ·¶¨·½·¨ÎÒÃÇÔÚ7.6.2½ÚÒѾ­Ëµ¹ýÁË£¬ÕâÀï²»ÔÙÏêÊö¡£×îºó£¬¶¨Ê±Æ÷ÏòÁ¿µÄÍ·²¿Ö¸Õëvec±íʾÕâ¸ö¶¨Ê±Æ÷Ó¦¸ÃËù´¦µÄ¶¨Ê±Æ÷ÏòÁ¿Á´±íÍ·²¿¡£
    £¨3£©×îºó£¬µ÷ÓÃlist_add()º¯Êý½«¶¨Ê±Æ÷²åÈëµ½vecÖ¸ÕëËùÖ¸ÏòµÄ¶¨Ê±Æ÷¶ÓÁеÄβ²¿¡£

    7£®6£®3£®5 ÐÞ¸ÄÒ»¸ö¶¨Ê±Æ÷µÄexpiresÖµ
    µ±Ò»¸ö¶¨Ê±Æ÷ÒѾ­±»²åÈëµ½Äں˶¯Ì¬¶¨Ê±Æ÷Á´±íÖкó£¬ÎÒÃÇ»¹¿ÉÒÔÐ޸ĸö¨Ê±Æ÷µÄexpiresÖµ¡£º¯Êýmod_timer()ʵÏÖÕâÒ»µã¡£ÈçÏÂËùʾ£¨kernel/timer.c£©£º
    int mod_timer(struct timer_list *timer, unsigned long expires)
    {
    int ret;
    unsigned long flags;

    spin_lock_irqsave(&timerlist_lock, flags);
    timer->expires = expires;
    ret = detach_timer(timer);
    internal_add_timer(timer);
    spin_unlock_irqrestore(&timerlist_lock, flags);
    return ret;
    }
    ¸Ãº¯ÊýÊ×Ïȸù¾Ý²ÎÊýexpiresÖµ¸üж¨Ê±Æ÷µÄexpires³ÉÔ±¡£È»ºóµ÷ÓÃdetach_timer()º¯Êý½«¸Ã¶¨Ê±Æ÷´ÓËüÔ­À´ËùÊôµÄÁ´±íÖÐɾ³ý¡£×îºóµ÷ÓÃinternal_add_timer()º¯Êý½«¸Ã¶¨Ê±Æ÷¸ù¾ÝËüеÄexpiresÖµÖØвåÈëµ½ÏàÓ¦µÄÁ´±íÖС£

    º¯Êýdetach_timer()Ê×Ïȵ÷ÓÃtimer_pending()À´ÅжÏÖ¸¶¨µÄ¶¨Ê±Æ÷ÊÇ·ñÒѾ­´¦ÓÚij¸öÁ´±íÖУ¬Èç¹û¶¨Ê±Æ÷Ô­À´¾Í²»´¦ÓÚÈκÎÁ´±íÖУ¬Ôòdetach_timer()º¯ÊýʲôҲ²»×ö£¬Ö±½Ó·µ»Ø0Öµ£¬±íʾʧ°Ü¡£·ñÔò£¬¾Íµ÷ÓÃlist_del()º¯Êý½«¶¨Ê±Æ÷´ÓËüÔ­À´Ëù´¦µÄÁ´±íÖÐÕª³ý¡£ÈçÏÂËùʾ£¨kernel/timer.c£©£º
    static inline int detach_timer (struct timer_list *timer)
    {
    if (!timer_pending(timer))
    return 0;
    list_del(&timer->list);
    return 1;
    }

    7£®6£®3£®6 ɾ³ýÒ»¸ö¶¨Ê±Æ÷
    º¯Êýdel_timer()ÓÃÀ´½«Ò»¸ö¶¨Ê±Æ÷´ÓÏàÓ¦µÄÄں˶¨Ê±Æ÷¶ÓÁÐÖÐɾ³ý¡£¸Ãº¯Êýʵ¼ÊÉÏÊǶÔdetach_timer()º¯ÊýµÄ¸ß²ã·â×°¡£ÈçÏÂËùʾ£¨kernel/timer.c£©£º
    int del_timer(struct timer_list * timer)
    {
    int ret;
    unsigned long flags;

    spin_lock_irqsave(&timerlist_lock, flags);
    ret = detach_timer(timer);
    timer->list.next = timer->list.prev = NULL;
    spin_unlock_irqrestore(&timerlist_lock, flags);
    return ret;
    }

    7£®6£®3£®7 ¶¨Ê±Æ÷ǨÒƲÙ×÷
    ÓÉÓÚÒ»¸ö¶¨Ê±Æ÷µÄintervalÖµ»áËæ×Åʱ¼äµÄ²»¶ÏÁ÷ÊÅ£¨¼´jiffiesÖµµÄ²»¶ÏÔö´ó£©¶ø²»¶Ï±äС£¬Òò´ËÄÇЩԭ±¾µ½ÆÚ½ôÆȳ̶Ƚϵ͵Ķ¨Ê±Æ÷»áËæ×ÅjiffiesÖµµÄ²»¶ÏÔö´ó¶ø³ÉΪ¼È½«ÂíÉϵ½ÆڵĶ¨Ê±Æ÷¡£±ÈÈ綨ʱÆ÷ÏòÁ¿tv2.vec[0]ÖеĶ¨Ê±Æ÷ÔÚ¾­¹ý256¸öʱÖӵδðºó»á³ÉΪδÀ´256¸öʱÖӵδðÄڻᵽÆڵĶ¨Ê±Æ÷¡£Òò´Ë£¬¶¨Ê±Æ÷ÔÚÄں˶¯Ì¬¶¨Ê±Æ÷Á´±íÖеÄλÖÃÒ²Ó¦ÏàÓ¦µØËæןı䡣¸Ä±äµÄ¹æÔòÊÇ£ºµ±tv1.indexÖØбäΪ0ʱ£¨Òâζ×Åtv1ÖеÄ256¸ö¶¨Ê±Æ÷ÏòÁ¿¶¼Òѱ»ÄÚºËɨÃèÒ»±éÁË£¬´Ó¶øʹtv1ÖеÄ256¸ö¶¨Ê±Æ÷ÏòÁ¿±äΪ¿Õ£©£¬ÔòÓÃtv2.vec£Ûindex£Ý¶¨Ê±Æ÷ÏòÁ¿ÖеĶ¨Ê±Æ÷È¥Ìî³ätv1£¬Í¬Ê±Ê¹tv2.index¼Ó1£¨ËüÒÔ64Ϊģ£©¡£µ±tv2.indexÖØбäΪ0£¨Òâζ×Åtv2ÖеÄ64¸ö¶¨Ê±Æ÷ÏòÁ¿¶¼ÒѾ­±»È«²¿Ìî³äµ½tv1ÖÐÈ¥ÁË£¬´Ó¶øʹµÃtv2±äΪ¿Õ£©£¬ÔòÓÃtv3.vec£Ûindex£Ý¶¨Ê±Æ÷ÏòÁ¿ÖеĶ¨Ê±Æ÷È¥Ìî³ätv2¡£Èç´ËÒ»Ö±ÀàÍÆÏÂÈ¥£¬Ö±µ½tv5¡£
    º¯Êýcascade_timers()Íê³ÉÕâÖÖ¶¨Ê±Æ÷ǨÒƲÙ×÷£¬¸Ãº¯ÊýÖ»ÓÐÒ»¸ötimer_vec½á¹¹ÀàÐÍÖ¸ÕëµÄ²ÎÊýtv¡£Õâ¸öº¯Êý½«°Ñ¶¨Ê±Æ÷ÏòÁ¿tv->vec£Ûtv->index£ÝÖеÄËùÓж¨Ê±Æ÷ÖØÐÂÌî³äµ½ÉÏÒ»²ã¶¨Ê±Æ÷ÏòÁ¿ÖÐÈ¥¡£ÈçÏÂËùʾ£¨kernel/timer.c£©£º
    static inline void cascade_timers(struct timer_vec *tv)
    {
    /* cascade all the timers from tv up one level */
    struct list_head *head, *curr, *next;

    head = tv->vec + tv->index;
    curr = head->next;
    /*
    * We are removing _all_ timers from the list, so we don't have to
    * detach them individually, just clear the list afterwards.
    */
    while (curr != head) {
    struct timer_list *tmp;

    tmp = list_entry(curr, struct timer_list, list);
    next = curr->next;
    list_del(curr); // not needed
    internal_add_timer(tmp);
    curr = next;
    }
    INIT_LIST_HEAD(head);
    tv->index = (tv->index + 1) & TVN_MASK;
    }
    ¶Ô¸Ãº¯ÊýµÄ×¢ÊÍÈçÏ£º
    £¨1£©Ê×ÏÈ£¬ÓÃÖ¸ÕëheadÖ¸Ïò¶¨Ê±Æ÷Í·²¿ÏòÁ¿Í·²¿µÄlist_head½á¹¹¡£Ö¸ÕëcurrÖ¸Ïò¶¨Ê±Æ÷ÏòÁ¿ÖеĵÚÒ»¸ö¶¨Ê±Æ÷¡£
    £¨2£©È»ºó£¬ÓÃÒ»¸öwhile{}Ñ­»·À´±éÀú¶¨Ê±Æ÷ÏòÁ¿tv->vec£Ûtv->index£Ý¡£ÓÉÓÚ¶¨Ê±Æ÷ÏòÁ¿ÊÇÒ»¸öË«ÏòÑ­»·¶ÓÁУ¬Òò´ËÑ­»·µÄÖÕÖ¹Ìõ¼þÊÇcurr=head¡£¶ÔÓÚÿһ¸ö±»É¨ÃèµÄ¶¨Ê±Æ÷£¬Ñ­»·Ì嶼Ïȵ÷ÓÃlist_del()º¯Êý½«µ±Ç°¶¨Ê±Æ÷´ÓÁ´±íÖÐÕª³ý£¬È»ºóµ÷ÓÃinternal_add_timer()º¯ÊýÖØÐÂÈ·¶¨¸Ã¶¨Ê±Æ÷Ó¦¸Ã±»·Åµ½Äĸö¶¨Ê±Æ÷ÏòÁ¿ÖÐÈ¥¡£
    £¨3£©µ±´Ówhile{}Ñ­»·Í˳öºó£¬¶¨Ê±Æ÷ÏòÁ¿tv->vec£Ûtv->index£ÝÖÐËùÓеĶ¨Ê±Æ÷¶¼Òѱ»Ç¨ÒƵ½ÆäËüµØ·½£¨µ½ËüÃǸôôµÄµØ·½£º£­£©£¬Òò´ËËü±¾Éí¾Í³ÉΪһ¸ö¿Õ¶ÓÁС£ÕâÀïÎÒÃÇÏÔʾµØµ÷ÓÃINIT_LIST_HEAD()ºêÀ´½«¶¨Ê±Æ÷ÏòÁ¿µÄ±íÍ·½á¹¹³õʼ»¯Îª¿Õ¡£
    £¨4£©×îºó£¬½«tv->indexÖµ¼Ó1£¬µ±È»ËüÊÇÒÔ64Ϊģ¡£

    7£®6£®4£®8 ɨÃè²¢Ö´Ðе±Ç°ÒѾ­µ½ÆڵĶ¨Ê±Æ÷
    º¯Êýrun_timer_list()Íê³ÉÕâ¸ö¹¦ÄÜ¡£ÈçÇ°ËùÊö£¬¸Ãº¯ÊýÊDZ»timer_bh()º¯ÊýËùµ÷Óõģ¬Òò´ËÄں˶¨Ê±Æ÷ÊÇÔÚʱÖÓÖжϵÄBottom HalfÖб»Ö´Ðеġ£¼ÇסÕâÒ»µã·Ç³£ÖØÒª¡£È«¾Ö±äÁ¿timer_jiffies±íʾÁËÄÚºËÉÏÒ»´ÎÖ´ÐÐrun_timer_list()º¯ÊýµÄʱ¼ä£¬Òò´ËjiffiesÓëtimer_jiffiesµÄ²îÖµ¾Í±íʾÁË×Ô´ÓÉÏÒ»´Î´¦Àí¶¨Ê±Æ÷ÒÔÀ´£¬ÆÚ¼äÒ»¹²·¢ÉúÁ˶àÉÙ´ÎʱÖÓÖжϣ¬ÏÔÈ»run_timer_list()º¯Êý±ØÐëΪÆÚ¼äËù·¢ÉúµÄÿһ´ÎʱÖÓÖжϲ¹É϶¨Ê±Æ÷·þÎñ¡£¸Ãº¯ÊýµÄÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    static inline void run_timer_list(void)
    {
    spin_lock_irq(&timerlist_lock);
    while ((long)(jiffies - timer_jiffies) >= 0) {
    struct list_head *head, *curr;
    if (!tv1.index) {
    int n = 1;
    do {
    cascade_timers(tvecs[n]);
    } while (tvecs[n]->index == 1 && ++n < NOOF_TVECS);
    }
    repeat:
    head = tv1.vec + tv1.index;
    curr = head->next;
    if (curr != head) {
    struct timer_list *timer;
    void (*fn)(unsigned long);
    unsigned long data;

    timer = list_entry(curr, struct timer_list, list);
    fn = timer->function;
    data= timer->data;

    detach_timer(timer);
    timer->list.next = timer->list.prev = NULL;
    timer_enter(timer);
    spin_unlock_irq(&timerlist_lock);
    fn(data);
    spin_lock_irq(&timerlist_lock);
    timer_exit();
    goto repeat;
    }
    ++timer_jiffies;
    tv1.index = (tv1.index + 1) & TVR_MASK;
    }
    spin_unlock_irq(&timerlist_lock);
    }
    º¯Êýrun_timer_list()µÄÖ´Ðйý³ÌÖ÷Òª¾ÍÊÇÓÃÒ»¸ö´ówhile{}Ñ­»·À´ÎªÊ±ÖÓÖжÏÖ´Ðж¨Ê±Æ÷·þÎñ£¬Ã¿Ò»´ÎÑ­»··þÎñÒ»´ÎʱÖÓÖжϡ£Òò´ËÒ»¹²ÒªÖ´ÐУ¨jiffies£­timer_jiffies£«1£©´ÎÑ­»·¡£Ñ­»·ÌåËùÖ´ÐеķþÎñ²½ÖèÈçÏ£º
    £¨1£©Ê×ÏÈ£¬ÅжÏtv1.indexÊÇ·ñΪ0£¬Èç¹ûΪ0ÔòÐèÒª´Ótv2Öв¹³ä¶¨Ê±Æ÷µ½tv1ÖÐÀ´¡£µ«tv2Ò²¿ÉÄÜΪ¿Õ¶øÐèÒª´Ótv3Öв¹³ä¶¨Ê±Æ÷£¬Òò´ËÓÃÒ»¸ödo{}whileÑ­»·À´µ÷ÓÃcascade_timer()º¯ÊýÀ´ÒÀ´ÎÊÓÐèÒª´Ótv2Öв¹³ätv1£¬´Ótv3Öв¹³ätv2¡¢¡­¡¢´Ótv5Öв¹³ätv4¡£ÏÔÈ»Èç¹ûtvi.index=0£¨2¡Üi¡Ü5£©£¬Ôò¶ÔÓÚtviÖ´ÐÐcascade_timers()º¯Êýºó£¬tvi.index¿Ï¶¨Îª1¡£·´¹ýÀ´½²£¬Èç¹û¶ÔtviÖ´Ðйýcascade_timers()º¯Êýºótvi.index²»µÈÓÚ1£¬ÄÇô¿ÉÒԿ϶¨ÔÚδ¶ÔtviÖ´ÐÐcascade_timers()º¯Êý֮ǰ£¬tvi.indexÖµ¿Ï¶¨²»Îª0£¬Òò´ËÕâʱtvi²»ÐèÒª´Ótv(i+1)Öв¹³ä¶¨Ê±Æ÷£¬Õâʱ¾Í¿ÉÒÔÖÕÖ¹do{}whileÑ­»·¡£
    £¨2£©½ÓÏÂÀ´£¬¾ÍÒªÖ´Ðж¨Ê±Æ÷ÏòÁ¿tv1.vec£Ûtv1.index£ÝÖеÄËùÓе½ÆÚ¶¨Ê±Æ÷¡£Òò´ËÕâÀïÓÃÒ»¸ögoto repeatÑ­»·´ÓÍ·µ½Î²ÒÀ´ÎɨÃèÕû¸ö¶¨Ê±Æ÷¶ÔÁС£ÓÉÓÚÔÚÖ´Ðж¨Ê±Æ÷µÄ¹ØÁªº¯Êýʱ²¢²»ÐèÒª¹ØCPUÖжϣ¬ËùÒÔÔÚÓÃdetach_timer()º¯Êý½«µ±Ç°¶¨Ê±Æ÷´Ó¶ÔÁÐÖÐÕª³ýºó£¬¾Í¿ÉÒÔµ÷ÓÃspin_unlock_irq()º¯Êý½øÐнâËøºÍ¿ªÖжϣ¬È»ºóÔÚÖ´ÐÐÍ굱ǰ¶¨Ê±Æ÷µÄ¹ØÁªº¯ÊýºóÖØÐÂÓÃspin_lock_irq£¨£©º¯Êý¼ÓËøºÍ¹ØÖжϡ£
    £¨3£©µ±Ö´ÐÐÍ궨ʱÆ÷ÏòÁ¿tv1.vec[tv1.index]ÖеÄËùÓе½ÆÚ¶¨Ê±Æ÷ºó£¬tv1.vec£Ûtv1.index£ÝÓ¦¸ÃÊǸö¿Õ¶ÓÁС£ÖÁ´ËÕâÒ»´Î¶¨Ê±Æ÷·þÎñÒ²¾ÍÐû¸æ½áÊø¡£
    £¨4£©×îºó£¬½«timer_jiffiesÖµ¼Ó1£¬½«tv1.indexÖµ¼Ó1£¬µ±È»ËüµÄÄ£ÊÇ256¡£È»ºó£¬»Øµ½whileÑ­»·¿ªÊ¼ÏÂÒ»´Î¶¨Ê±Æ÷·þÎñ¡£

  4. hfh08 ÓÚ 2006-08-21 00:25:48·¢±í:

    7£®5 ʱÖÓÖжϵÄBottom Half
    ÓëʱÖÓÖжÏÏà¹ØµÄBottom HalfÏòÁ½Ö÷ÒªÓÐÁ½¸ö£ºTIMER_BHºÍTQUEUE_BH¡£ÓëTIMER_BHÏà¶ÔÓ¦µÄBHº¯ÊýÊÇtimer_bh()£¬ÓëTQUEUE_BH¶ÔÓ¦µÄº¯ÊýÊÇtqueue_bh()¡£ËüÃǾùʵÏÖÔÚkernel/timer.cÎļþÖС£

    7£®5£®1 TQUEUE_BHÏòÁ¿
    TQUEUE_BHµÄ×÷ÓÃÊÇÓÃÀ´ÔËÐÐtq_timerÕâ¸öÈÎÎñ¶ÓÁÐÖеÄÈÎÎñ¡£Òò´Ëdo_timer()º¯Êý½ö½öÔÚtq_timerÈÎÎñ¶ÓÁв»Îª¿ÕµÄÇé¿ö²Å¼¤»îTQUEUE_BHÏòÁ¿¡£º¯Êýtqueue_bh()µÄʵÏַdz£¼òµ¥£¬ËüÖ»ÊǼòµ¥µØµ÷ÓÃrun_task_queue()º¯ÊýÀ´ÔËÐÐÈÎÎñ¶ÓÁÐtq_timer¡£ÈçÏÂËùʾ£º
    void tqueue_bh(void)
    {
    run_task_queue(&tq_timer);
    }
    ÈÎÎñ¶ÔÁÐtq_timerÒ²ÊǶ¨ÒåÔÚkernel/timer.cÎļþÖУ¬ÈçÏÂËùʾ£º
    DECLARE_TASK_QUEUE(tq_timer);

    7£®5£®2 TIMER_BHÏòÁ¿
    TIMER_BHÕâ¸öBottom HalfÏòÁ¿ÊÇLinuxÄÚºËʱÖÓÖжÏÇý¶¯µÄÒ»¸öÖØÒª¸¨Öú²¿·Ö¡£ÄÚºËÔÚÿһ´Î¶ÔʱÖÓÖжϵķþÎñ¿ìÒª½áÊøʱ£¬¶¼»áÎÞÌõ¼þµØ¼¤»îÒ»¸öTIMER_BHÏòÁ¿£¬ÒÔʹµÃÄÚºËÔÚÉÔºóÒ»¶ÎÑÓ³ÙºóÖ´ÐÐÏàÓ¦µÄBHº¯Êý----timer_bh()¡£¸ÃÈÎÎñµÄÔ´ÂëÈçÏ£º
    void timer_bh(void)
    {
    update_times();
    run_timer_list();
    }
    ´ÓÉÏÊöÔ´Âë¿ÉÒÔ¿´³ö£¬ÄÚºËÔÚʱÖÓÖжÏÇý¶¯µÄµ×°ë²¿·ÖÖ÷ÒªÓÐÁ½¸öÈÎÎñ£º£¨1£©µ÷ÓÃupdate_times()º¯ÊýÀ´¸üÐÂϵͳȫ¾Öʱ¼äxtime£»£¨2£©µ÷ÓÃrun_timer_list()º¯ÊýÀ´Ö´Ðж¨Ê±Æ÷¡£¹ØÓÚ¶¨Ê±Æ÷ÎÒÃǽ«ÔÚÏÂÒ»½ÚÌÖÂÛ¡£±¾½ÚÎÒÃÇÖ÷ÒªÌÖÂÛTIMER_BHµÄµÚÒ»¸öÈÎÎñ----¶ÔÄÚºËʱ¼äxtimeµÄ¸üС£
    ÎÒÃǶ¼ÖªµÀ£¬Äں˾ֲ¿Ê±¼äxtimeÊÇÓÃÀ´¹©Óû§³ÌÐòͨ¹ýʱ¼äsyscallÀ´¼ìË÷»òÉèÖõ±Ç°ÏµÍ³Ê±¼äµÄ£¬¶øÄں˴úÂëÔÚ´ó¶àÊýÇé¿ö϶¼ÒýÓÃjiffies±äÁ¿£¬¶øºÜÉÙʹÓÃxtime£¨Å¼¶ûÒ²»áÓÐÒýÓÃxtimeµÄÇé¿ö£¬±ÈÈç¸üÐÂinodeµÄʱ¼ä±ê¼Ç£©¡£Òò´Ë£¬¶ÔÓÚʱÖÓÖжϷþÎñ³ÌÐòtimer_interrupt£¨£©¶øÑÔ£¬jiffies±äÁ¿µÄ¸üÐÂÊÇ×î½ôÆȵģ¬¶øxtimeµÄ¸üÐÂÔò¿ÉÒÔÑÓ³Ùµ½ÖжϷþÎñµÄµ×°ë²¿·ÖÀ´½øÐС£
    ÓÉÓÚBottom Half»úÖÆÔÚÖ´ÐÐʱ¼ä¾ßÓÐijЩ²»È·¶¨ÐÔ£¬Òò´ËÔÚtimer_bh()º¯ÊýµÃµ½ÕæÕýÖ´ÐÐ֮ǰ£¬ÆÚ¼ä¿ÉÄÜÓÖ»áÓм¸´ÎʱÖÓÖжϷ¢Éú¡£ÕâÑù¾Í»áÔì³ÉʱÖӵδðµÄ¶ªÊ§ÏÖÏó¡£ÎªÁË´¦ÀíÕâÖÖÇé¿ö£¬LinuxÄÚºËʹÓÃÁËÒ»¸ö¸¨ÖúÈ«¾Ö±äÁ¿wall_jiffies£¬À´±íʾÉÏÒ»´Î¸üÐÂxtimeʱµÄjiffiesÖµ¡£Æ䶨ÒåÈçÏ£¨kernel/timer.c£©£º
    /* jiffies at the most recent update of wall time */
    unsigned long wall_jiffies;
    ¶øtimer_bh()º¯ÊýÕæÕýÖ´ÐÐʱµÄjiffiesÖµÓëwall_jiffiesµÄ²î¾ÍÊÇÔÚtimer_bh()ÕæÕýÖ´ÐÐ֮ǰËù·¢ÉúµÄʱÖÓÖжϴÎÊý¡£
    º¯Êýupdate_times()µÄÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    static inline void update_times(void)
    {
    unsigned long ticks;

    /*
    * update_times() is run from the raw timer_bh handler so we
    * just know that the irqs are locally enabled and so we don't
    * need to save/restore the flags of the local CPU here. -arca
    */
    write_lock_irq(&xtime_lock);

    ticks = jiffies - wall_jiffies;
    if (ticks) {
    wall_jiffies += ticks;
    update_wall_time(ticks);
    }
    write_unlock_irq(&xtime_lock);
    calc_load(ticks);
    }
    £¨1£©Ê×ÏÈ£¬¸ù¾ÝjiffiesºÍwall_jiffiesµÄ²îÖµ¼ÆËãÔÚ´Ë֮ǰһ¹²·¢ÉúÁ˼¸´ÎʱÖӵδ𣬲¢½«Õâ¸öÖµ±£´æµ½¾Ö²¿±äÁ¿ticksÖС£²¢ÔÚticksÖµ´óÓÚ0µÄÇé¿öÏ£¨ticks´óÓÚµÈÓÚ1£¬Ò»°ãÇé¿öÏÂΪ1£©£º¢Ù¸üÐÂwall_jiffiesΪjiffies±äÁ¿µÄµ±Ç°Öµ£¨wall_jiffies£«£½ticksµÈ¼ÛÓÚwall_jiffies£½jiffies£©¡£¢ÚÒÔ²ÎÊýticksµ÷ÓÃupdate_wall_time()º¯ÊýÈ¥ÕæÕýµØ¸üÐÂÈ«¾Öʱ¼äxtime¡£
    £¨2£©µ÷ÓÃcalc_load()º¯ÊýÈ¥¼ÆËãϵͳ¸ºÔØÇé¿ö¡£ÕâÀïÎÒÃDz»È¥ÉËü¡£

    º¯Êýupdate_wall_time()º¯Êý¸ù¾Ý²ÎÊýticksËùÖ¸¶¨µÄʱÖӵδð´ÎÊýÏàÓ¦µØ¸üÐÂÄÚºËÈ«¾Öʱ¼ä±äÁ¿xtime¡£ÆäÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    /*
    * Using a loop looks inefficient, but "ticks" is
    * usually just one (we shouldn't be losing ticks,
    * we're doing this this way mainly for interrupt
    * latency reasons, not because we think we'll
    * have lots of lost timer ticks
    */
    static void update_wall_time(unsigned long ticks)
    {
    do {
    ticks--;
    update_wall_time_one_tick();
    } while (ticks);

    if (xtime.tv_usec >= 1000000) {
    xtime.tv_usec -= 1000000;
    xtime.tv_sec++;
    second_overflow();
    }
    }
    ¶Ô¸Ãº¯ÊýµÄ×¢ÊÍÈçÏ£º
    £¨1£©Ê×ÏÈ£¬ÓÃÒ»¸ödo{}Ñ­»·À´¸ù¾Ý²ÎÊýticksµÄÖµÒ»´ÎÒ»´Îµ÷ÓÃupdate_wall_time_one_tick()º¯ÊýÀ´ÎªÒ»´ÎʱÖӵδð¸üÐÂxtimeÖеÄtv_usec³ÉÔ±¡£
    £¨2£©¸ù¾ÝÐèÒªµ÷ÕûxtimeÖеÄÃëÊý³ÉÔ±tv_usecºÍ΢ÃëÊý³ÉÔ±tv_usec¡£Èç¹û΢ÃëÊý³ÉÔ±tv_usecµÄÖµ³¬¹ý106£¬Ôò˵Ã÷ÒѾ­¹ýÁËÒ»ÃëÖÓ¡£Òò´Ë½«tv_usecµÄÖµ¼õÈ¥1000000£¬²¢½«ÃëÊý³ÉÔ±tv_secµÄÖµ¼Ó1£¬È»ºóµ÷ÓÃsecond_overflow£¨£©º¯ÊýÀ´´¦Àí΢ÃëÊý³ÉÔ±Òç³öµÄÇé¿ö¡£

    º¯Êýupdate_wall_time_one_tick£¨£©ÓÃÀ´¸üÐÂÒ»´ÎʱÖӵδð¶Ôϵͳȫ¾Öʱ¼äxtimeµÄÓ°Ïì¡£ÓÉÓÚtickÈ«¾Ö±äÁ¿±íʾÁËÒ»´ÎʱÖӵδðµÄʱ¼ä¼ä¸ô³¤¶È£¨ÒÔusΪµ¥Î»£©£¬Òò´Ë¸Ãº¯ÊýµÄʵÏÖÖÐ×îºËÐĵĴúÂë¾ÍÊǽ«xtimeµÄtv_usec³ÉÔ±Ôö¼Ótick΢Ãë¡£ÕâÀïÎÒÃDz»È¥¹ØÐĺ¯ÊýʵÏÖÖÐÓëNTP£¨Network Time Protocol£©ºÍϵͳµ÷ÓÃadjtimex£¨£©µÄÏà¹Ø²¿·Ö¡£ÆäÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    /* in the NTP reference this is called "hardclock()" */
    static void update_wall_time_one_tick(void)
    {
    if ( (time_adjust_step = time_adjust) != 0 ) {
    /* We are doing an adjtime thing.
    *
    * Prepare time_adjust_step to be within bounds.
    * Note that a positive time_adjust means we want the clock
    * to run faster.
    *
    * Limit the amount of the step to be in the range
    * -tickadj .. +tickadj
    */
    if (time_adjust > tickadj)
    time_adjust_step = tickadj;
    else if (time_adjust < -tickadj)
    time_adjust_step = -tickadj;

    /* Reduce by this step the amount of time left */
    time_adjust -= time_adjust_step;
    }
    xtime.tv_usec += tick + time_adjust_step;
    /*
    * Advance the phase, once it gets to one microsecond, then
    * advance the tick more.
    */
    time_phase += time_adj;
    if (time_phase <= -FINEUSEC) {
    long ltemp = -time_phase >> SHIFT_SCALE;
    time_phase += ltemp << SHIFT_SCALE;
    xtime.tv_usec -= ltemp;
    }
    else if (time_phase >= FINEUSEC) {
    long ltemp = time_phase >> SHIFT_SCALE;
    time_phase -= ltemp << SHIFT_SCALE;
    xtime.tv_usec += ltemp;
    }
    }

  5. hfh08 ÓÚ 2006-08-21 00:25:25·¢±í:

    7£®4 ʱÖÓÖжϵÄÇý¶¯
    ÈçÇ°ËùÊö£¬8253£¯8254 PITµÄͨµÀ0ͨ³£±»ÓÃÀ´ÔÚIRQ0ÉϲúÉúÖÜÆÚÐÔµÄʱÖÓÖжϡ£¶ÔʱÖÓÖжϵÄÇý¶¯ÊǾø´óÊý²Ù×÷ϵͳÄÚºËʵÏÖtime-keepingµÄ¹Ø¼üËùÔÚ¡£²»Í¬µÄOS¶ÔʱÖÓÇý¶¯µÄÒªÇóÒ²²»Í¬£¬µ«ÊÇÒ»°ã¶¼°üº¬ÏÂÁÐÒªÇóÄÚÈÝ£º
    1. ά»¤ÏµÍ³µÄµ±Ç°Ê±¼äÓëÈÕÆÚ¡£
    2. ·ÀÖ¹½ø³ÌÔËÐÐʱ¼ä³¬³öÆäÔÊÐíµÄʱ¼ä¡£
    3. ¶ÔCPUµÄʹÓÃÇé¿ö½øÐмÇÕÊͳ¼Æ¡£
    4. ´¦ÀíÓû§½ø³Ì·¢³öµÄʱ¼äϵͳµ÷Óá£
    5. ¶ÔϵͳijЩ²¿·ÖÌṩ¼àÊÓ¶¨Ê±Æ÷¡£
    ÆäÖУ¬µÚÒ»ÏÄÜÊÇËùÓÐOS¶¼±ØÐëʵÏֵĻù´¡¹¦ÄÜ£¬ËüÊÇOSÄں˵ÄÔËÐлù´¡¡£Í¨³£ÓÐÈýÖÖ·½·¨¿ÉÓÃÀ´Î¬»¤ÏµÍ³µÄʱ¼äÓëÈÕÆÚ£º£¨1£©×î¼òµ¥µÄÒ»ÖÖ·½·¨¾ÍÊÇÓÃÒ»¸ö64λµÄ¼ÆÊýÆ÷À´¶ÔʱÖӵδð½øÐмÆÊý¡££¨2£©µÚ¶þÖÖ·½·¨¾ÍÊÇÓÃÒ»¸ö32λ¼ÆÊýÆ÷À´¶ÔÃë½øÐмÆÊý¡£ÓÃÒ»¸ö32λµÄ¸¨Öú¼ÆÊýÆ÷À´¶ÔʱÖӵδð¼ÆÊýÖ±ÖÁÀÛ¼ÆÒ»ÃëΪֹ¡£ÒòΪ232³¬¹ý136Ä꣬Òò´ËÕâÖÖ·½·¨Ö±ÖÁ22ÊÀ¼Í¶¼¿ÉÒÔ¹¤×÷µÃºÜºÃ¡££¨3£©µÚÈýÖÖ·½·¨Ò²ÊÇ°´µÎ´ð½øÐмÆÊý£¬µ«È´ÊÇÏà¶ÔÓÚϵͳÆô¶¯ÒÔÀ´µÄµÎ´ð´ÎÊý£¬¶ø²»ÊÇÏà¶ÔÓÚÒ»¸öÈ·¶¨µÄÍⲿʱ¿Ì¡£µ±¶Áºó±¸Ê±ÖÓ£¨ÈçRTC£©»òÓû§ÊäÈëʵ¼Êʱ¼äʱ£¬¸ù¾Ýµ±Ç°µÄµÎ´ð´ÎÊý¼ÆËãϵͳµ±Ç°Ê±¼ä¡£
    UNIXÀàµÄOSͨ³£¶¼²ÉÓõÚÈýÖÖ·½·¨À´Î¬»¤ÏµÍ³µÄʱ¼äÓëÈÕÆÚ¡£

    7£®4£®1 Linux¶ÔʱÖÓÖжϵijõʼ»¯
    Linux¶ÔʱÖÓÖжϵijõʼ»¯ÊÇ·ÖΪ¼¸¸ö²½ÖèÀ´½øÐеģº£¨1£©Ê×ÏÈ£¬ÓÉinit_IRQ()º¯Êýͨ¹ýµ÷ÓÃinit_ISA_IRQ()º¯Êý¶ÔÖжÏÏòÁ¿32¡«256Ëù¶ÔÓ¦µÄÖжÏÏòÁ¿ÃèÊö·û½øÐгõʼ»¯ÉèÖá£ÏÔÈ»£¬ÕâÆäÖÐÒ²¾Í°ÑIRQ0£¨Ò²¼´ÖжÏÏòÁ¿32£©µÄÖжÏÏòÁ¿ÃèÊö·û³õʼ»¯ÁË¡££¨2£©È»ºó£¬init_IRQ()º¯ÊýÉèÖÃÖжÏÏòÁ¿32¡«256Ïà¶ÔÓ¦µÄÖжÏÃÅ¡££¨3£©init_IRQ()º¯Êý¶ÔPIT½øÐгõʼ»¯±à³Ì£»£¨4£©sched_init()º¯Êý¶Ô¼ÆÊýÆ÷¡¢Ê±¼äÖжϵÄBottom Half½øÐгõʼ»¯¡££¨5£©×îºó£¬ÓÉtime_init()º¯Êý¶ÔLinuxÄں˵ÄʱÖÓÖжϻúÖƽøÐгõʼ»¯¡£ÕâÈý¸ö³õʼ»¯º¯Êý¶¼ÊÇÓÉinit/main.cÎļþÖеÄstart_kernel()º¯Êýµ÷Óõģ¬ÈçÏ£º
    asmlinkage void __init start_kernel()
    {
    ¡­
    trap_init();
    init_IRQ();
    sched_init();
    time_init();
    softirq_init();
    ¡­
    }

    (1)init_IRQ()º¯Êý¶Ô8254 PITµÄ³õʼ»¯±à³Ì
    º¯Êýinit_IRQ()º¯ÊýÔÚÍê³ÉÖжÏÃŵijõʼ»¯ºó£¬¾Í¶Ô8254 PIT½øÐгõʼ»¯±à³ÌÉèÖã¬ÉèÖõIJ½ÖèÈçÏ£º£¨1£©ÉèÖÃ8254 PITµÄ¿ØÖƼĴæÆ÷£¨¶Ë¿Ú0x43£©µÄֵΪ¡°01100100¡±£¬Ò²¼´Ñ¡ÔñͨµÀ0¡¢ÏȶÁдLSBÔÙ¶ÁдMSB¡¢¹¤×÷ģʽ2¡¢¶þ½øÖÆ´æ´¢¸ñʽ¡££¨2£©½«ºêLATCHµÄֵдÈëͨµÀ0µÄ¼ÆÊýÆ÷ÖУ¨¶Ë¿Ú0x40£©£¬×¢ÒâÒªÏÈдLATCHµÄLSB£¬ÔÙдLATCHµÄ¸ß×Ö½Ú¡£ÆäÔ´ÂëÈçÏÂËùʾ£¨arch/i386/kernel/i8259.c£©£º
    void __init init_IRQ(void)
    {
    ¡­¡­
    /*
    * Set the clock to HZ Hz, we already have a valid
    * vector now:
    */
    outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
    outb_p(LATCH & 0xff , 0x40); /* LSB */
    outb(LATCH >> 8 , 0x40); /* MSB */
    ¡­¡­
    }

    £¨2£©sched_init()¶Ô¶¨Ê±Æ÷»úÖƺÍʱÖÓÖжϵÄBottom HalfµÄ³õʼ»¯
    º¯Êýsched_init()ÖÐÓëʱ¼äÏà¹ØµÄ³õʼ»¯¹ý³ÌÖ÷ÒªÓÐÁ½²½£º£¨1£©µ÷ÓÃinit_timervecs()º¯Êý³õʼ»¯Äں˶¨Ê±Æ÷»úÖÆ£»£¨2£©µ÷ÓÃinit_bh()º¯Êý½«BHÏòÁ¿TIMER_BH¡¢TQUEUE_BHºÍIMMEDIATE_BHËù¶ÔÓ¦µÄBHº¯Êý·Ö±ðÉèÖóÉtimer_bh()¡¢tqueue_bh()ºÍimmediate_bh()º¯Êý¡£ÈçÏÂËùʾ£¨kernel/sched.c£©£º
    void __init sched_init(void)
    {
    ¡­¡­
    init_timervecs();

    init_bh(TIMER_BH, timer_bh);
    init_bh(TQUEUE_BH, tqueue_bh);
    init_bh(IMMEDIATE_BH, immediate_bh);
    ¡­¡­
    }

    £¨3£©time_init()º¯Êý¶ÔÄÚºËʱÖÓÖжϻúÖƵijõʼ»¯
    Ç°ÃæÁ½¸öº¯ÊýËù½øÐеijõʼ»¯²½Ö趼ÊÇΪʱ¼äÖжϻúÖÆ×öºÃ×¼±¸¶øÒÑ¡£ÔÚÖ´ÐÐÍêinit_IRQ()º¯ÊýºÍsched_init()º¯Êýºó£¬CPUÒѾ­¿ÉÒÔΪIRQ0ÉϵÄʱÖÓÖжϽøÐзþÎñÁË£¬ÒòΪIRQ0Ëù¶ÔÓ¦µÄÖжÏÃÅÒѾ­±»ÉèÖúÃÖ¸ÏòÖжϷþÎñº¯ÊýIRQ0x20_interrupt()¡£µ«ÊÇÓÉÓÚ´ËʱÖжÏÏòÁ¿0x20µÄÖжÏÏòÁ¿ÃèÊö·ûirq_desc£Û0£Ý»¹ÊÇ´¦ÓÚ³õʼ״̬£¨Æästatus³ÉÔ±µÄֵΪIRQ_DISABLED£©£¬²¢Î´¹Ò½ÓÈκξßÌåµÄÖжϷþÎñÃèÊö·û£¬Òò´ËÕâʱCPU¶ÔIRQ0µÄÖжϷþÎñ²¢Ã»ÓÐÈκξßÌåÒâÒ壬¶øÖ»ÊÇ°´Õչ涨µÄÁ÷³Ì¿ÕÅÜÒ»ÌË¡£µ«Êǵ±CPUÖ´ÐÐÍêtime_init()º¯Êýºó£¬ÇéÐξʹó²»Ò»ÑùÁË¡£
    º¯Êýtime_init()Ö÷Òª×öÈý¼þÊ£º£¨1£©´ÓRTCÖлñÈ¡ÄÚºËÆô¶¯Ê±µÄʱ¼äÓëÈÕÆÚ£»£¨2£©ÔÚCPUÓÐTSCµÄÇé¿öÏÂУ׼TSC£¬ÒÔ±ãΪºóÃæʹÓÃTSC×öºÃ×¼±¸£»£¨3£©ÔÚIRQ0µÄÖжÏÇëÇóÃèÊö·ûÖйҽӾßÌåµÄÖжϷþÎñÃèÊö·û¡£ÆäÔ´ÂëÈçÏÂËùʾ£¨arch/i386/kernel/time.c£©£º
    void __init time_init(void)
    {
    extern int x86_udelay_tsc;

    xtime.tv_sec = get_cmos_time();
    xtime.tv_usec = 0;

    /*
    * If we have APM enabled or the CPU clock speed is variable
    * (CPU stops clock on HLT or slows clock to save power)
    * then the TSC timestamps may diverge by up to 1 jiffy from
    * 'real time' but nothing will break.
    * The most frequent case is that the CPU is "woken" from a halt
    * state by the timer interrupt itself, so we get 0 error. In the
    * rare cases where a driver would "wake" the CPU and request a
    * timestamp, the maximum error is < 1 jiffy. But timestamps are
    * still perfectly ordered.
    * Note that the TSC counter will be reset if APM suspends
    * to disk; this won't break the kernel, though, 'cuz we're
    * smart. See arch/i386/kernel/apm.c.
    */
    /*
    * Firstly we have to do a CPU check for chips with
    * a potentially buggy TSC. At this point we haven't run
    * the ident/bugs checks so we must run this hook as it
    * may turn off the TSC flag.
    *
    * NOTE: this doesnt yet handle SMP 486 machines where only
    * some CPU's have a TSC. Thats never worked and nobody has
    * moaned if you have the only one in the world - you fix it!
    */

    dodgy_tsc();

    if (cpu_has_tsc) {
    unsigned long tsc_quotient = calibrate_tsc();
    if (tsc_quotient) {
    fast_gettimeoffset_quotient = tsc_quotient;
    use_tsc = 1;
    /*
    * We could be more selective here I suspect
    * and just enable this for the next intel chips ?
    */
    x86_udelay_tsc = 1;
    #ifndef do_gettimeoffset
    do_gettimeoffset = do_fast_gettimeoffset;
    #endif
    do_get_fast_time = do_gettimeofday;

    /* report CPU clock rate in Hz.
    * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
    * clock/second. Our precision is about 100 ppm.
    */
    { unsigned long eax=0, edx=1000;
    __asm__("divl %2"
    :"=a" (cpu_khz), "=d" (edx)
    :"r" (tsc_quotient),
    "0" (eax), "1" (edx));
    printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000);
    }
    }
    }

    #ifdef CONFIG_VISWS
    printk("Starting Cobalt Timer system clock\n");

    /* Set the countdown value */
    co_cpu_write(CO_CPU_TIMEVAL, CO_TIME_HZ/HZ);

    /* Start the timer */
    co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) | CO_CTRL_TIMERUN);

    /* Enable (unmask) the timer interrupt */
    co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) & ~CO_CTRL_TIMEMASK);

    /* Wire cpu IDT entry to s/w handler (and Cobalt APIC to IDT) */
    setup_irq(CO_IRQ_TIMER, &irq0);
    #else
    setup_irq(0, &irq0);
    #endif
    }
    ¶Ô¸Ãº¯ÊýµÄ×¢½âÈçÏ£º
    £¨1£©µ÷Óú¯Êýget_cmos_time()´ÓRTCÖеõ½ÏµÍ³Æô¶¯Ê±µÄʱ¼äÓëÈÕÆÚ£¬Ëü·µ»ØµÄÊǵ±Ç°Ê±¼äÏà¶ÔÓÚ1970£­01£­01 00£º00£º00Õâ¸öUNIXʱ¼ä»ù×¼µÄÃëÊýÖµ¡£Òò´ËÕâ¸öÃëÊýÖµ¾Í±»±£´æÔÚϵͳȫ¾Ö±äÁ¿xtimeµÄtv_sec³ÉÔ±ÖС£¶øxtimeµÄÁíÒ»¸ö³ÉÔ±tv_usecÔò±»³õʼ»¯Îª0¡£
    £¨2£©Í¨¹ýdodgy_tsc()º¯Êý¼ì²âCPUÊÇ·ñ´æÔÚʱ¼ä´Á¼ÇÊýÆ÷BUG£¨I know nothing about it£º£­£©
    £¨3£©Í¨¹ýºêcpu_has_tscÀ´È·¶¨ÏµÍ³ÖÐCPUÊÇ·ñ´æÔÚTSC¼ÆÊýÆ÷¡£Èç¹û´æÔÚTSC£¬ÄÇôÄں˾ͿÉÒÔÓÃTSCÀ´»ñµÃ¸üΪ¾«È·µÄʱ¼ä¡£ÎªÁËÄܹ»ÓÃTSCÀ´ÐÞÕýÄÚºËʱ¼ä¡£ÕâÀï±ØÐë×÷һЩ³õʼ»¯¹¤×÷£º¢Ùµ÷ÓÃcalibrate_tsc()À´È·¶¨TSCµÄÿһ´Î¼ÆÊýÕæÕý´ú±í¶à³¤µÄʱ¼ä¼ä¸ô£¨µ¥Î»Îªus£©£¬Ò²¼´Ò»¸öʱÖÓÖÜÆÚµÄÕæÕýʱ¼ä¼ä¸ô³¤¶È¡£¢Ú½«calibrate_tsc()º¯ÊýËù·µ»ØµÄÖµ±£´æÔÚÈ«¾Ö±äÁ¿fast_gettimeoffset_quotientÖУ¬¸Ã±äÁ¿±»ÓÃÀ´¿ìËٵؼÆËãʱ¼äÆ«²î£»Í¬Ê±»¹½«ÁíÒ»¸öÈ«¾Ö±äÁ¿use_tscÉèÖÃΪ1£¬±íʾÄں˿ÉÒÔʹÓÃTSC¡£ÕâÁ½¸ö±äÁ¿¶¼¶¨ÒåÔÚarch/i386/kernel/time.cÎļþÖУ¬ÈçÏ£º
    /* Cached *multiplier* to convert TSC counts to microseconds.
    * (see the equation below).
    * Equal to 2^32 * (1 / (clocks per usec) ).
    * Initialized in time_init.
    */
    unsigned long fast_gettimeoffset_quotient;
    ¡­¡­
    static int use_tsc;
    ¢Û½ÓÏÂÀ´£¬½«ÏµÍ³È«¾Ö±äÁ¿x86_udelay_tscÉèÖÃΪ1£¬±íʾ¿ÉÒÔͨ¹ýTSCÀ´ÊµÏÖ΢ÃµÄ¾«È·ÑÓʱ¡£¸Ã±äÁ¿¶¨ÒåÔÚarch/i386/lib/delay.cÎļþÖС£¢Ü½«º¯ÊýÖ¸Õëdo_gettimeoffsetÇ¿ÖÆÐÔµØÖ¸Ïòº¯Êýdo_fast_gettimeoffset()£¨ÓëÖ®¶ÔÓ¦µÄÊÇdo_slow_gettimeoffset()º¯Êý£©£¬´Ó¶øʹÄÚºËÔÚ¼ÆËãʱ¼äÆ«²îʱ¿ÉÒÔÓÃTSCÕâÖÖ¿ìËٵķ½·¨À´½øÐС£¢Ý½«º¯ÊýÖ¸Õëdo_get_fast_timeÖ¸Ïòº¯Êýdo_gettimeofday()£¬´Ó¶ø¿ÉÒÔÈÃÆäËûÄÚºËÄ£¿éͨ¹ýdo_gettimeofday()º¯ÊýÀ´»ñµÃ¸ü¾«×¼µÄµ±Ç°Ê±¼ä¡£¢Þ¼ÆËã²¢±¨¸æ¸ù¾ÝTSCËùËãµÃµÄCPUʱÖÓƵÂÊ¡£
    £¨4£©²»¿¼ÂÇCONFIG_VISWSµÄÇé¿ö£¬Òò´Ëtime_init()µÄ×îºóÒ»¸ö²½Öè¾ÍÊǵ÷ÓÃsetup_irq()º¯ÊýÀ´ÎªIRQ0¹Ò½Ó¾ßÌåµÄÖжϷþÎñÃèÊö·ûirq0¡£È«¾Ö±äÁ¿irq0ÊÇʱÖÓÖжÏÇëÇóµÄÖжϷþÎñÃèÊö·û£¬Æ䶨ÒåÈçÏ£¨arch/i386/kernel/time.c£©£º
    static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
    ÏÔÈ»£¬º¯Êýtimer_interrupt()½«³ÉΪʱÖÓÖжϵķþÎñ³ÌÐò£¨ISR£©£¬¶øSA_INTERRUPT±êÖ¾Ò²Ö¸¶¨ÁËtimer_interrupt()º¯Êý½«ÊÇÔÚCPU¹ØÖжϵÄÌõ¼þÏÂÖ´Ðеġ£½á¹¹irq0ÖеÄnextÖ¸Õë±»ÉèÖÃΪNULL£¬Òò´ËIRQ0Ëù¶ÔÓ¦µÄÖжϷþÎñ¶ÓÁÐÖÐÖ»ÓÐirq0ÕâΨһµÄÒ»¸öÔªËØ£¬ÇÒIRQ0²»ÔÊÐíÖжϹ²Ïí¡£

    7£®4£®2 ʱÖÓÖжϷþÎñÀý³Ìtimer_interrupt()
    ÖжϷþÎñÃèÊö·ûirq0Ò»µ©±»¹³¹Òµ½IRQ0µÄÖжϷþÎñ¶ÓÁÐÖÐÈ¥ºó£¬LinuxÄں˾ͿÉÒÔͨ¹ýirq0->handlerº¯ÊýÖ¸ÕëËùÖ¸ÏòµÄtimer_interrupt()º¯Êý¶ÔʱÖÓÖжÏÇëÇó½øÐÐÕæÕýµÄ·þÎñ£¬¶ø²»ÊÇÏòÇ°ÃæËù˵µÄÄÇÑùÖ»ÊÇÈÃCPU¡°¿ÕÅÜ¡±Ò»ÌË¡£´Ëʱ£¬LinuxÄں˿ÉÒÔ˵ÊÇÕæÕýµÄ¡°Ìø¶¯¡±ÆðÀ´ÁË¡£
    ÔÚ±¾½ÚÒ»¿ªÊ¼ËùÊöµÄ¶ÔʱÖÓÖжÏÇý¶¯µÄ5ÏîÒªÇóÖУ¬Í¨³£Ö»ÓеÚÒ»Ï¼´timekeeping£©ÊÇ×îΪÆÈÇеģ¬Òò´Ë±ØÐëÔÚʱÖÓÖжϷþÎñÀý³ÌÖÐÍê³É¡£¶øÆäÓàµÄ¼¸¸öÒªÇó¿ÉÒÔÉÔ»º£¬Òò´Ë¿ÉÒÔ·ÅÔÚʱÖÓÖжϵÄBottom HalfÖÐÈ¥Ö´ÐС£ÕâÑù£¬LinuxÄں˾ÍÊÇtimer_interrupt()º¯ÊýµÄÖ´ÐÐʱ¼ä¾¡¿ÉÄܵĶ̣¬ÒòΪËüÊÇÔÚCPU¹ØÖжϵÄÌõ¼þÏÂÖ´Ðеġ£
    º¯Êýtimer_interrupt()µÄÔ´ÂëÈçÏ£¨arch/i386/kernel/time.c£©£º
    /*
    * This is the same as the above, except we _also_ save the current
    * Time Stamp Counter value at the time of the timer interrupt, so that
    * we later on can estimate the time of day more exactly.
    */
    static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    {
    int count;

    /*
    * Here we are in the timer irq handler. We just have irqs locally
    * disabled but we don't know if the timer_bh is running on the other
    * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
    * the irq version of write_lock because as just said we have irq
    * locally disabled. -arca
    */
    write_lock(&xtime_lock);

    if (use_tsc)
    {
    /*
    * It is important that these two operations happen almost at
    * the same time. We do the RDTSC stuff first, since it's
    * faster. To avoid any inconsistencies, we need interrupts
    * disabled locally.
    */

    /*
    * Interrupts are just disabled locally since the timer irq
    * has the SA_INTERRUPT flag set. -arca
    */

    /* read Pentium cycle counter */

    rdtscl(last_tsc_low);

    spin_lock(&i8253_lock);
    outb_p(0x00, 0x43); /* latch the count ASAP */

    count = inb_p(0x40); /* read the latched count */
    count |= inb(0x40) << 8;
    spin_unlock(&i8253_lock);

    count = ((LATCH-1) - count) * TICK_SIZE;
    delay_at_last_interrupt = (count + LATCH/2) / LATCH;
    }

    do_timer_interrupt(irq, NULL, regs);

    write_unlock(&xtime_lock);

    }
    ¶Ô¸Ãº¯ÊýµÄ×¢ÊÍÈçÏ£º
    £¨1£©ÓÉÓÚº¯ÊýÖ´ÐÐÆÚ¼äÒª·ÃÎÊÈ«¾Öʱ¼ä±äÁ¿xtime£¬Òò´ËÒ»¿ª¾Í¶Ô×ÔÐýËøxtime_lock½øÐмÓËø¡£
    £¨2£©Èç¹ûÄÚºËʹÓÃCPUµÄTSC¼Ä´æÆ÷£¨use_tsc±äÁ¿·Ç0£©£¬ÄÇôͨ¹ýTSC¼Ä´æÆ÷À´¼ÆËã´Óʱ¼äÖжϵIJúÉúµ½timer_interrupt£¨£©º¯ÊýÕæÕýÔÚCPUÉÏÖ´ÐÐÕâÖ®¼äµÄʱ¼äÑÓ³Ù£º
    l µ÷Óúêrdtscl()½«64λµÄTSC¼Ä´æÆ÷ÖµÖеĵÍ32루LSB£©¶Áµ½±äÁ¿last_tsc_lowÖУ¬ÒÔ¹©do_fast_gettimeoffset()º¯Êý¼ÆËãʱ¼äÆ«²îÖ®Óá£ÕâÒ»²½µÄʵÖʾÍÊǽ«CPU TSC¼Ä´æÆ÷µÄÖµ¸üе½Äں˶ÔTSCµÄ»º´æ±äÁ¿last_tsc_lowÖС£
    l ͨ¹ý¶Á8254 PITµÄͨµÀ0µÄ¼ÆÊýÆ÷µÄµ±Ç°ÖµÀ´¼ÆËãʱ¼äÑÓ³Ù£¬Îª´Ë£ºÊ×ÏÈ£¬¶Ô×ÔÐýËøi8253_lock½øÐмÓËø¡£×ÔÐýËøi8253_lockµÄ×÷ÓþÍÊÇÓÃÀ´´®Ðл¯¶Ô8254 PITµÄ¶Áд·ÃÎÊ¡£Æä´Î£¬Ïò8254µÄ¿ØÖƼĴæÆ÷£¨¶Ë¿Ú0x43£©ÖÐдÈëÖµ0x00£¬ÒÔ±ã¶ÔͨµÀ0µÄ¼ÆÊýÆ÷½øÐÐËø´æ¡£×îºó£¬Í¨¹ý¶Ë¿Ú0x40½«Í¨µÀ0µÄ¼ÆÊýÆ÷µÄµ±Ç°Öµ¶Áµ½¾Ö²¿±äÁ¿countÖУ¬²¢½âËøi8253_lock¡£
    l ÏÔÈ»£¬´Óʱ¼äÖжϵIJúÉúµ½timer_interrupt()º¯ÊýÕæÕýÖ´ÐÐÕâ¶Îʱ¼äÄÚ£¬ÒÔÒ»¹²Á÷ÊÅÁË£¨£¨LATCH-1£©-count£©¸öʱÖÓÖÜÆÚ£¬Òò´ËÕâ¸öÑÓʱ³¤¶È¿ÉÒÔÓÃÈçϹ«Ê½¼ÆË㣺
    delay_at_last_interrupt£½£¨£¨£¨LATCH-1£©£­count£©¡ÂLATCH£©*TICK_SIZE
    ÏÔÈ»£¬ÉÏÊö¹«Ê½µÄ½á¹ûÊǸöСÊý£¬Ó¦¶ÔÆä½øÐÐËÄÉáÎåÈ룬Ϊ´Ë£¬LinuxÓÃÏÂÊö±í´ïʽÀ´¼ÆËãdelay_at_last_interrupt±äÁ¿µÄÖµ£º
    £¨£¨£¨LATCH-1£©-count£©£ªTICK_SIZE£«LATCH/2£©£¯LATCH
    ÉÏÊö±»³ýÊý±í´ïʽÖеÄLATCH£¯2¾ÍÊÇÓÃÀ´½«½á¹ûÏòÉÏÔ²Õû³ÉÕûÊýµÄ¡£
    £¨3£©ÔÚ¼ÆËã³öʱ¼äÑÓ³Ùºó£¬×îºóµ÷Óú¯Êýdo_timer_interrupt()Ö´ÐÐÕæÕýµÄʱÖÓ·þÎñ¡£

    º¯Êýdo_timer_interrupt()µÄÔ´ÂëÈçÏ£¨arch/i386/kernel/time.c£©£º
    /*
    * timer_interrupt() needs to keep up the real-time clock,
    * as well as call the "do_timer()" routine every clocktick
    */
    static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    {
    ¡£¡£¡£¡£¡£¡£
    do_timer(regs);
    ¡£¡£¡£¡£¡£¡£¡£
    /*
    * If we have an externally synchronized Linux clock, then update
    * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
    * called as close as possible to 500 ms before the new second starts.
    */
    if ((time_status & STA_UNSYNC) == 0 &&
    xtime.tv_sec > last_rtc_update + 660 &&
    xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 &&
    xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) {
    if (set_rtc_mmss(xtime.tv_sec) == 0)
    last_rtc_update = xtime.tv_sec;
    else
    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
    }
    ¡­¡­
    }
    ÉÏÊö´úÂëÖÐÊ¡ÂÔÁËÐí¶àÓëSMPÏà¹ØµÄ´úÂ룬ÒòΪÎÒÃDz»¹ØÐÄSMP¡£´ÓÉÏÊö´úÂëÎÒÃÇ¿ÉÒÔ¿´³ö£¬do_timer_interrupt()º¯ÊýÖ÷Òª×÷Á½¼þÊ£º
    £¨1£©µ÷ÓÃdo_timer()º¯Êý¡£
    £¨2£©ÅжÏÊÇ·ñÐèÒª¸üÐÂCMOSʱÖÓ£¨¼´RTC£©ÖеÄʱ¼ä¡£Linux½öÔÚÏÂÁÐÈý¸öÌõ¼þͬʱ³ÉÁ¢Ê±²Å¸üÐÂCMOSʱÖÓ£º¢Ùϵͳȫ¾Öʱ¼ä״̬±äÁ¿time_statusÖÐûÓÐÉèÖÃSTA_UNSYNC±êÖ¾£¬Ò²¼´ËµÃ÷LinuxÓÐÒ»¸öÍⲿͬ²½Ê±ÖÓ¡£Êµ¼ÊÉÏÈ«¾Öʱ¼ä״̬±äÁ¿time_status½öÔÚÒ»ÖÖÇé¿öÏ»ᱻÇå³ýSTA_SYNC±êÖ¾£¬ÄǾÍÊÇÖ´ÐÐadjtimex()ϵͳµ÷ÓÃʱ£¨Õâ¸ösyscallÓëNTPÓйأ©¡£¢Ú×Ô´ÓÉÏ´ÎCMOSʱÖÓ¸üÐÂÒѾ­¹ýÈ¥ÁË11·ÖÖÓ¡£È«¾Ö±äÁ¿last_rtc_update±£´æ×ÅÉϴθüÐÂCMOSʱÖÓµÄʱ¼ä¡£¢ÛÓÉÓÚRTC´æÔÚUpdate Cycle£¬Òò´Ë×îºÃÔÚÒ»Ãëʱ¼ä¼ä¸ôµÄÖмäλÖÃ500ms×óÓÒµ÷ÓÃset_rtc_mmss()º¯ÊýÀ´¸üÐÂCMOSʱÖÓ¡£Òò´ËLinux¹æ¶¨½öµ±È«¾Ö±äÁ¿xtimeµÄ΢ÃëÊýtv_usecÔÚ500000¡À£¨tick/2£©Î¢Ã뷶Χ·¶Î§Ö®ÄÚʱ£¬²Åµ÷ÓÃset_rtc_mmss()º¯Êý¡£Èç¹ûÉÏÊöÌõ¼þ¾ù³ÉÁ¢£¬ÄǾ͵÷ÓÃset_rtc_mmss()½«µ±Ç°Ê±¼äxtime.tv_sec¸üлØдµ½RTCÖС£
    Èç¹ûÉÏÃæÊǵÄset_rtc_mmss()º¯Êý·µ»Ø0Öµ£¬Ôò±íÃ÷¸üгɹ¦¡£ÓÚÊǾͽ«¡°×î½üÒ»´ÎRTC¸üÐÂʱ¼ä¡±±äÁ¿last_rtc_update¸üÐÂΪµ±Ç°Ê±¼äxtime.tv_sec¡£Èç¹û·µ»Ø·Ç0Öµ£¬ËµÃ÷¸üÐÂʧ°Ü£¬ÓÚÊǾÍÈÃlast_rtc_update£½xtime.tv_sec-600£¨Ï൱ÓÚlast_rtc_update+=60£©£¬ÒÔ±ãÔÚÔÚ60ÃëÖ®ºóÔٴζÔRTC½øÐиüС£

    º¯Êýdo_timer()ʵÏÖÔÚkernel/timer.cÎļþÖУ¬ÆäÔ´ÂëÈçÏ£º
    void do_timer(struct pt_regs *regs)
    {
    (*(unsigned long *)&jiffies)++;
    #ifndef CONFIG_SMP
    /* SMP process accounting uses the local APIC timer */

    update_process_times(user_mode(regs));
    #endif
    mark_bh(TIMER_BH);
    if (TQ_ACTIVE(tq_timer))
    mark_bh(TQUEUE_BH);
    }
    ¸Ãº¯ÊýµÄºËÐÄÊÇÍê³ÉÈý¸öÈÎÎñ£º
    £¨1£©½«±íʾ×ÔϵͳÆô¶¯ÒÔÀ´µÄʱÖӵδð¼ÆÊý±äÁ¿jiffies¼Ó1¡£
    £¨2£©µ÷ÓÃupdate_process_times()º¯Êý¸üе±Ç°½ø³ÌµÄʱ¼äͳ¼ÆÐÅÏ¢¡£×¢Ò⣬¸Ãº¯ÊýµÄ²ÎÊýÔ­ÐÍÊÇ¡°int user_tick¡±£¬Èç¹û±¾´ÎʱÖÓÖжϣ¨¼´Ê±Öӵδ𣩷¢ÉúʱCPUÕý´¦ÓÚÓû§Ì¬ÏÂÖ´ÐУ¬Ôòuser_tick²ÎÊýÓ¦¸ÃΪ1£»·ñÔòÈç¹û±¾´ÎʱÖÓÖжϷ¢ÉúʱCPUÕý´¦ÓÚºËÐÄ̬ÏÂÖ´ÐÐʱ£¬Ôòuser_tick²ÎÊýÓ¦¸ÄΪ0¡£ËùÒÔÕâÀïÎÒÃÇÒÔºêuser_mode(regs)À´×÷Ϊupdate_process_times()º¯ÊýµÄµ÷ÓòÎÊý¡£¸Ãºê¶¨ÒåÔÚinclude/asm-i386/ptrace.hÍ·ÎļþÖУ¬Ëü¸ù¾ÝregsÖ¸ÕëËùÖ¸ÏòµÄºËÐĶÑÕ»¼Ä´æÆ÷½á¹¹À´ÅжÏCPU½øÈëÖжϷþÎñ֮ǰÊÇ´¦ÓÚÓû§Ì¬Ï»¹ÊÇ´¦ÓÚºËÐÄ̬Ï¡£ÈçÏÂËùʾ£º
    #ifdef __KERNEL__
    #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->xcs))
    ¡­¡­
    #endif
    £¨3£©µ÷ÓÃmark_bh()º¯Êý¼¤»îʱÖÓÖжϵÄBottom HalfÏòÁ¿TIMER_BHºÍTQUEUE_BH£¨×¢Ò⣬TQUEUE_BH½öÔÚÈÎÎñ¶ÓÁÐtq_timer²»Îª¿ÕµÄÇé¿öϲŻᱻ¼¤»î£©¡£

    ÖÁ´Ë£¬Äں˶ÔʱÖÓÖжϵķþÎñÁ÷³ÌÐû¸æ½áÊø£¬ÏÂÃæÎÒÃÇÏêϸ·ÖÎöÒ»ÏÂupdate_process_times()º¯ÊýµÄʵÏÖ¡£

    7£®4£®3 ¸üÐÂʱ¼ä¼ÇÕÊÐÅÏ¢----CPU·ÖʱµÄʵÏÖ
    º¯Êýupdate_process_times()±»ÓÃÀ´ÔÚ·¢ÉúʱÖÓÖжÏʱ¸üе±Ç°½ø³ÌÒÔ¼°ÄÚºËÖÐÓëʱ¼äÏà¹ØµÄͳ¼ÆÐÅÏ¢£¬²¢¸ù¾ÝÕâЩÐÅÏ¢×÷³öÏàÓ¦µÄ¶¯×÷£¬±ÈÈ磺ÖØнøÐе÷¶È£¬Ïòµ±Ç°½ø³Ì·¢³öÐźŵȡ£¸Ãº¯Êý½öÓÐÒ»¸ö²ÎÊýuser_tick£¬È¡ÖµÎª1»ò0£¬Æ京ÒåÔÚÇ°ÃæÒѾ­ÐðÊö¹ý¡£
    ¸Ãº¯ÊýµÄÔ´´úÂëÈçÏ£¨kernel/timer.c£©£º
    /*
    * Called from the timer interrupt handler to charge one tick to the current
    * process. user_tick is 1 if the tick is user time, 0 for system.
    */
    void update_process_times(int user_tick)
    {
    struct task_struct *p = current;
    int cpu = smp_processor_id(), system = user_tick ^ 1;

    update_one_process(p, user_tick, system, cpu);
    if (p->pid) {
    if (--p->counter <= 0) {
    p->counter = 0;
    p->need_resched = 1;
    }
    if (p->nice > 0)
    kstat.per_cpu_nice[cpu] += user_tick;
    else
    kstat.per_cpu_user[cpu] += user_tick;
    kstat.per_cpu_system[cpu] += system;
    } else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)
    kstat.per_cpu_system[cpu] += system;
    }
    £¨1£©Ê×ÏÈ£¬ÓÃsmp_processor_id()ºêµÃµ½µ±Ç°½ø³ÌµÄCPU ID¡£
    £¨2£©È»ºó£¬Èþֲ¿±äÁ¿system£½user_tick^1£¬±íʾµ±·¢ÉúʱÖÓÖжÏʱCPUÊÇ·ñÕý´¦ÓÚºËÐÄ̬Ï¡£Òò´Ë£¬Èç¹ûuser_tick=1£¬Ôòsystem=0£»Èç¹ûuser_tick£½0£¬Ôòsystem=1¡£
    £¨3£©µ÷ÓÃupdate_one_process()º¯ÊýÀ´¸üе±Ç°½ø³ÌµÄtask_struct½á¹¹ÖеÄËùÓÐÓëʱ¼äÏà¹ØµÄͳ¼ÆÐÅÏ¢ÒÔ¼°³ÉÔ±±äÁ¿¡£¸Ãº¯Êý»¹»áÊÓÐèÒªÏòµ±Ç°½ø³Ì·¢ËÍÏàÓ¦µÄÐźţ¨signal£©¡£
    £¨4£©Èç¹ûµ±Ç°½ø³ÌµÄPID·Ç0£¬ÔòÖ´ÐÐÏÂÁв½ÖèÀ´¾ö¶¨ÊÇ·ñÖØнøÐе÷¶È£¬²¢¸üÐÂÄÚºËʱ¼äͳ¼ÆÐÅÏ¢£º
    l ½«µ±Ç°½ø³ÌµÄ¿ÉÔËÐÐʱ¼äƬ³¤¶È£¨ÓÉtask_struct½á¹¹ÖеÄcounter³ÉÔ±±íʾ£¬Æ䵥λÊÇʱÖӵδð´ÎÊý£©¼õ1¡£Èç¹û¼õµ½0Öµ£¬Ôò˵Ã÷µ±Ç°½ø³ÌÒѾ­ÓÃÍêÁËϵͳ·ÖÅä¸øËüµÄµÄÔËÐÐʱ¼äƬ£¬Òò´Ë±ØÐëÖØнøÐе÷¶È¡£ÓÚÊǽ«µ±Ç°½ø³ÌµÄtask_struct½á¹¹ÖеÄneed_resched³ÉÔ±±äÁ¿ÉèÖÃΪ1£¬±íʾÐèÒªÖØÐÂÖ´Ðе÷¶È¡£
    l Èç¹ûµ±Ç°½ø³ÌµÄtask_struct½á¹¹ÖеÄnice³ÉÔ±Öµ´óÓÚ0£¬ÄÇô½«ÄÚºËÈ«¾Öͳ¼ÆÐÅÏ¢±äÁ¿kstatÖеÄper_cpu_nice£Ûcpu£ÝÖµ½«ÉÏuser_tick¡£·ñÔò¾Í½«user_tickÖµ¼Óµ½ÄÚºËÈ«¾Öͳ¼ÆÐÅÏ¢±äÁ¿kstatÖеÄper_cpu_user£Ûcpu£Ý³ÉÔ±ÉÏ¡£
    l ½«system±äÁ¿Öµ¼Óµ½ÄÚºËÈ«¾Öͳ¼ÆÐÅÏ¢kstat.per_cpu_system£Ûcpu£ÝÉÏ¡£
    £¨5£©·ñÔò£¬¾ÍÅжϵ±Ç°CPUÔÚ·þÎñʱÖÓÖжÏÇ°ÊÇ·ñ´¦ÓÚsoftirqÈíÖжϷþÎñµÄÖ´ÐÐÖУ¬»òÔòÕýÔÚ·þÎñÒ»´ÎµÍÓÅÏȼ¶±ðµÄÓ²¼þÖжÏÖС£Èç¹ûÊÇÕâÑùµÄ»°£¬Ôò½«system±äÁ¿µÄÖµ¼Óµ½ÄÚºËÈ«¾Öͳ¼ÆÐÅÏ¢kstat.per_cpu.system£Ûcpu£ÝÉÏ¡£

    l update_one_process()º¯Êý
    ʵÏÖÔÚkernel/timer.cÎļþÖеÄupdate_one_process()º¯ÊýÓÃÀ´ÔÚʱÖÓÖжϷ¢Éúʱ¸üÐÂÒ»¸ö½ø³ÌµÄtask_struc½á¹¹ÖеÄʱ¼äͳ¼ÆÐÅÏ¢¡£ÆäÔ´ÂëÈçÏ£¨kernel/timer.c£©£º

    void update_one_process(struct task_struct *p, unsigned long user,
    unsigned long system, int cpu)
    {
    p->per_cpu_utime[cpu] += user;
    p->per_cpu_stime[cpu] += system;
    do_process_times(p, user, system);
    do_it_virt(p, user);
    do_it_prof(p);
    }
    ×¢ÊÍÈçÏ£º
    £¨1£©ÓÉÓÚÔÚÒ»¸ö½ø³ÌµÄÕû¸öÉúÃüÆÚ£¨Lifetime£©ÖУ¬Ëü¿ÉÄÜ»áÔÚ²»Í¬µÄCPUÉÏÖ´ÐУ¬Ò²¼´Ò»¸ö½ø³Ì¿ÉÄÜÒ»¿ªÊ¼ÔÚCPU1ÉÏÖ´ÐУ¬µ±ËüÓÃÍêÔÚCPU1ÉϵÄÔËÐÐʱ¼äƬºó£¬Ëü¿ÉÄÜÓֻᱻµ÷¶Èµ½CPU2ÉÏÈ¥Ö´ÐС£ÁíÍ⣬µ±½ø³ÌÔÚij¸öCPUÉÏÖ´ÐÐʱ£¬Ëü¿ÉÄÜÓÖ»áÔÚÓû§Ì¬ºÍÄÚºË̬Ï·ֱð¸÷Ö´ÐÐÒ»¶Îʱ¼ä¡£ËùÒÔΪÁËͳ¼ÆÕâЩʼþÐÅÏ¢£¬½ø³Ìtask_struct½á¹¹ÖеÄper_cpu_utime£ÛNR_CPUS£ÝÊý×é¾Í±íʾ¸Ã½ø³ÌÔÚ¸÷CPUµÄÓû§Ì¨ÏÂÖ´ÐеÄÀÛ¼Æʱ¼ä³¤¶È£¬per_cpu_stime£ÛNR_CPUS£ÝÊý×é¾Í±íʾ¸Ã½ø³ÌÔÚ¸÷CPUµÄºËÐÄ̬ÏÂÖ´ÐеÄÀÛ¼Æʱ¼ä³¤¶È£»ËüÃǶ¼ÒÔʱÖӵδð´ÎÊýΪµ¥Î»¡£
    ËùÒÔ£¬update_one_process()º¯ÊýµÄµÚÒ»¸ö²½Öè¾ÍÊǸüнø³ÌÔÚµ±Ç°CPUÉϵÄÓû§Ì¬Ö´ÐÐʱ¼äͳ¼Æper_cpu_utime£Ûcpu£ÝºÍºËÐÄִ̬ÐÐʱ¼äͳ¼Æper_cpu_stime£Ûcpu£Ý¡£
    £¨2£©µ÷ÓÃdo_process_times()º¯Êý¸üе±Ç°½ø³ÌµÄ×Üʱ¼äͳ¼ÆÐÅÏ¢¡£
    £¨3£©µ÷ÓÃdo_it_virt()º¯ÊýΪµ±Ç°½ø³ÌµÄITIMER_VIRTUALÈí¼þ¶¨Ê±Æ÷¸üÐÂʱ¼ä¼ä¸ô¡£
    £¨4£©µ÷ÓÃdo_it_prof£¨£©º¯ÊýΪµ±Ç°½ø³ÌµÄITIMER_PROFÈí¼þ¶¨Ê±Æ÷¸üÐÂʱ¼ä¼ä¸ô¡£

    l do_process_times()º¯Êý
    º¯Êýdo_process_times()½«¸üÐÂÖ¸¶¨½ø³ÌµÄ×Üʱ¼äͳ¼ÆÐÅÏ¢¡£Ã¿¸ö½ø³Ìtask_struct½á¹¹Öж¼ÓÐÒ»¸ö³ÉÔ±times£¬ËüÊÇÒ»¸ötms½á¹¹ÀàÐÍ£¨include/linux/times.h£©£º
    struct tms {
    clock_t tms_utime; £¯£ª ±¾½ø³ÌÔÚÓû§Ì¨ÏµÄÖ´ÐÐʱ¼ä×ÜºÍ £ª£¯
    clock_t tms_stime; £¯£ª ±¾½ø³ÌÔÚºËÐÄ̬ϵÄÖ´ÐÐʱ¼ä×ÜºÍ £ª£¯
    clock_t tms_cutime; £¯£ª ËùÓÐ×Ó½ø³ÌÔÚÓû§Ì¬ÏµÄÖ´ÐÐʱ¼ä×ÜºÍ £ª£¯
    clock_t tms_cstime; £¯£ª ËùÓÐ×Ó½ø³ÌÔÚºËÐÄ̬ϵÄÖ´ÐÐʱ¼ä×ÜºÍ £ª£¯
    };
    ÉÏÊö½á¹¹µÄËùÓгÉÔ±¶¼ÒÔʱÖӵδð´ÎÊýΪµ¥Î»¡£
    º¯Êýdo_process_times()µÄÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    static inline void do_process_times(struct task_struct *p,
    unsigned long user, unsigned long system)
    {
    unsigned long psecs;

    psecs = (p->times.tms_utime += user);
    psecs += (p->times.tms_stime += system);
    if (psecs / HZ > p->rlim[RLIMIT_CPU].rlim_cur) {
    /* Send SIGXCPU every second.. */
    if (!(psecs % HZ))
    send_sig(SIGXCPU, p, 1);
    /* and SIGKILL when we go over max.. */
    if (psecs / HZ > p->rlim[RLIMIT_CPU].rlim_max)
    send_sig(SIGKILL, p, 1);
    }
    }
    ×¢ÊÍÈçÏ£º
    £¨1£©¸ù¾Ý²ÎÊýuser¸üÐÂÖ¸¶¨½ø³Ìtask_struct½á¹¹ÖеÄtimes.tms_utimeÖµ¡£¸ù¾Ý²ÎÊýsystem¸üÐÂÖ¸¶¨½ø³Ìtask_struct½á¹¹ÖеÄtimes.tms_stimeÖµ¡£
    £¨2£©½«¸üкóµÄtimes.tms_utimeÖµÓëtimes.tms_stimeÖµµÄºÍ±£´æµ½¾Ö²¿±äÁ¿psecsÖУ¬Òò´Ëpsecs¾Í±íʾÁËÖ¸¶¨½ø³Ìpµ½Ä¿Ç°ÎªÖ¹ÒѾ­ÔËÐеÄ×Üʱ¼ä³¤¶È£¨ÒÔʱÖӵδð´ÎÊý¼Æ£©¡£Èç¹ûÕâÒ»×ÜÔËÐÐʱ¼ä³¤³¬¹ý½ø³ÌPµÄ×ÊÔ´Ï޶ÄǾÍÿ¸ô1Ãë¸ø½ø³Ì·¢ËÍÒ»¸öÐźÅSIGXCPU£»Èç¹ûÔËÐÐʱ¼ä³¤¶È³¬¹ýÁ˽ø³Ì×ÊÔ´ÏÞ¶îµÄ×î´óÖµ£¬ÄǾͷ¢ËÍÒ»¸öSIGKILLÐźÅɱËÀ¸Ã½ø³Ì¡£

    l do_it_virt()º¯Êý
    ÿ¸ö½ø³Ì¶¼ÓÐÒ»¸öÓû§Ì¬Ö´ÐÐʱ¼äµÄitimerÈí¼þ¶¨Ê±Æ÷¡£½ø³ÌÈÎÎñ½á¹¹task_structÖеÄit_virt_value³ÉÔ±ÊÇÕâ¸öÈí¼þ¶¨Ê±Æ÷µÄʱ¼ä¼ÆÊýÆ÷¡£µ±½ø³ÌÔÚÓû§Ì¬ÏÂÖ´ÐÐʱ£¬Ã¿Ò»´ÎʱÖӵδð¶¼Ê¹¼ÆÊýÆ÷it_virt_value¼õ1£¬µ±¼õµ½0ʱÄÚºËÏò½ø³Ì·¢ËÍSIGVTALRMÐźţ¬²¢ÖØÖóõÖµ¡£³õÖµ±£´æÔÚ½ø³ÌµÄtask_struct½á¹¹µÄit_virt_incr³ÉÔ±ÖС£
    º¯Êýdo_it_virt()µÄÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    static inline void do_it_virt(struct task_struct * p, unsigned long ticks)
    {
    unsigned long it_virt = p->it_virt_value;

    if (it_virt) {
    it_virt -= ticks;
    if (!it_virt) {
    it_virt = p->it_virt_incr;
    send_sig(SIGVTALRM, p, 1);
    }
    p->it_virt_value = it_virt;
    }
    }

    l do_it_prof£¨£©º¯Êý
    ÀàËƵأ¬Ã¿¸ö½ø³ÌÒ²¶¼ÓÐÒ»¸öitimerÈí¼þ¶¨Ê±Æ÷ITIMER_PROF¡£½ø³Ìtask_structÖеÄit_prof_value³ÉÔ±¾ÍÊÇÕâ¸ö¶¨Ê±Æ÷µÄʱ¼ä¼ÆÊýÆ÷¡£²»¹Ü½ø³ÌÊÇÔÚÓû§Ì¬Ï»¹ÊÇÔÚÄÚºË̬ÏÂÔËÐУ¬Ã¿¸öʱÖӵδð¶¼Ê¹it_prof_value¼õ1¡£µ±¼õµ½0ʱÄں˾ÍÏò½ø³Ì·¢ËÍSIGPROFÐźţ¬²¢ÖØÖóõÖµ¡£³õÖµ±£´æÔÚ½ø³Ìtask_struct½á¹¹ÖеÄit_prof_incr³ÉÔ±ÖС£
    º¯Êýdo_it_prof()¾ÍÊÇÓÃÀ´Íê³ÉÉÏÊö¹¦Äܵģ¬ÆäÔ´ÂëÈçÏ£¨kernel/timer.c£©£º
    static inline void do_it_prof(struct task_struct *p)
    {
    unsigned long it_prof = p->it_prof_value;

    if (it_prof) {
    if (--it_prof == 0) {
    it_prof = p->it_prof_incr;
    send_sig(SIGPROF, p, 1);
    }
    p->it_prof_value = it_prof;
    }
    }

  6. hfh08 ÓÚ 2006-08-21 00:24:57·¢±í:

    7£®3 Linux¶Ôʱ¼äµÄ±íʾ
    ͨ³££¬²Ù×÷ϵͳ¿ÉÒÔʹÓÃÈýÖÖ·½·¨À´±íʾϵͳµÄµ±Ç°Ê±¼äÓëÈÕÆÚ£º¢Ù×î¼òµ¥µÄÒ»ÖÖ·½·¨¾ÍÊÇÖ±½ÓÓÃÒ»¸ö64λµÄ¼ÆÊýÆ÷À´¶ÔʱÖӵδð½øÐмÆÊý¡£¢ÚµÚ¶þÖÖ·½·¨¾ÍÊÇÓÃÒ»¸ö32λ¼ÆÊýÆ÷À´¶ÔÃë½øÐмÆÊý£¬Í¬Ê±»¹ÓÃÒ»¸ö32λµÄ¸¨Öú¼ÆÊýÆ÷¶ÔʱÖӵδð¼ÆÊý£¬Ö®×ÓÀÛ»ýµ½Ò»ÃëΪֹ¡£ÒòΪ232³¬¹ý136Ä꣬Òò´ËÕâÖÖ·½·¨Ö±ÖÁ22ÊÀ¼Í¶¼¿ÉÒÔÈÃϵͳ¹¤×÷µÃºÜºÃ¡£¢ÛµÚÈýÖÖ·½·¨Ò²ÊÇ°´Ê±Öӵδð½øÐмÆÊý£¬µ«ÊÇÊÇÏà¶ÔÓÚϵͳÆô¶¯ÒÔÀ´µÄµÎ´ð´ÎÊý£¬¶ø²»ÊÇÏà¶ÔÓÚÏà¶ÔÓÚij¸öÈ·¶¨µÄÍⲿʱ¿Ì£»µ±¶ÁÍⲿºó±¸Ê±ÖÓ£¨ÈçRTC£©»òÓû§ÊäÈëʵ¼Êʱ¼äʱ£¬¸ù¾Ýµ±Ç°µÄµÎ´ð´ÎÊý¼ÆËãϵͳµ±Ç°Ê±¼ä¡£
    UNIXÀà²Ù×÷ϵͳͨ³£¶¼²ÉÓõÚÈýÖÖ·½·¨À´Î¬»¤ÏµÍ³µÄʱ¼äÓëÈÕÆÚ¡£

    7£®3£®1 »ù±¾¸ÅÄî
    Ê×ÏÈ£¬ÓбØÒªÃ÷ȷһЩLinuxÄÚºËʱÖÓÇý¶¯ÖеĻù±¾¸ÅÄî¡£
    £¨1£©Ê±ÖÓÖÜÆÚ£¨clock cycle£©µÄƵÂÊ£º8253£¯8254 PITµÄ±¾ÖʾÍÊǶÔÓɾ§ÌåÕñµ´Æ÷²úÉúµÄʱÖÓÖÜÆÚ½øÐмÆÊý£¬¾§ÌåÕñµ´Æ÷ÔÚ1Ãëʱ¼äÄÚ²úÉúµÄʱÖÓÂö³å¸öÊý¾ÍÊÇʱÖÓÖÜÆÚµÄƵÂÊ¡£LinuxÓúêCLOCK_TICK_RATEÀ´±íʾ8254 PITµÄÊäÈëʱÖÓÂö³åµÄƵÂÊ£¨ÔÚPC»úÖÐÕâ¸öֵͨ³£ÊÇ1193180HZ£©£¬¸Ãºê¶¨ÒåÔÚinclude/asm-i386/timex.hÍ·ÎļþÖУº
    #define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
    £¨2£©Ê±Öӵδð£¨clock tick£©£ºÎÒÃÇÖªµÀ£¬µ±PITͨµÀ0µÄ¼ÆÊýÆ÷¼õµ½0ֵʱ£¬Ëü¾ÍÔÚIRQ0ÉϲúÉúÒ»´ÎʱÖÓÖжϣ¬Ò²¼´Ò»´ÎʱÖӵδð¡£PITͨµÀ0µÄ¼ÆÊýÆ÷µÄ³õʼֵ¾ö¶¨ÁËÒª¹ý¶àÉÙʱÖÓÖÜÆڲŲúÉúÒ»´ÎʱÖÓÖжϣ¬Òò´ËÒ²¾Í¾ö¶¨ÁËÒ»´ÎʱÖӵδðµÄʱ¼ä¼ä¸ô³¤¶È¡£
    £¨3£©Ê±ÖӵδðµÄƵÂÊ£¨HZ£©£ºÒ²¼´1Ãëʱ¼äÄÚPITËù²úÉúµÄʱÖӵδð´ÎÊý¡£ÀàËƵأ¬Õâ¸öÖµÒ²ÊÇÓÉPITͨµÀ0µÄ¼ÆÊýÆ÷³õÖµ¾ö¶¨µÄ£¨·´¹ýÀ´Ëµ£¬È·¶¨ÁËʱÖӵδðµÄƵÂÊÖµºóÒ²¾Í¿ÉÒÔÈ·¶¨8254 PITͨµÀ0µÄ¼ÆÊýÆ÷³õÖµ£©¡£LinuxÄÚºËÓúêHZÀ´±íʾʱÖӵδðµÄƵÂÊ£¬¶øÇÒÔÚ²»Í¬µÄƽ̨ÉÏHZÓв»Í¬µÄ¶¨ÒåÖµ¡£¶ÔÓÚALPHAºÍIA62ƽ̨HZµÄÖµÊÇ1024£¬¶ÔÓÚSPARC¡¢MIPS¡¢ARMºÍi386µÈƽ̨HZµÄÖµ¶¼ÊÇ100¡£¸ÃºêÔÚi386ƽ̨ÉϵĶ¨ÒåÈçÏ£¨include/asm-i386/param.h£©£º
    #ifndef HZ
    #define HZ 100
    #endif
    ¸ù¾ÝHZµÄÖµ£¬ÎÒÃÇÒ²¿ÉÒÔÖªµÀÒ»´ÎʱÖӵδðµÄ¾ßÌåʱ¼ä¼ä¸ôÓ¦¸ÃÊÇ£¨1000ms£¯HZ£©£½10ms¡£
    £¨4£©Ê±ÖӵδðµÄʱ¼ä¼ä¸ô£ºLinuxÓÃÈ«¾Ö±äÁ¿tickÀ´±íʾʱÖӵδðµÄʱ¼ä¼ä¸ô³¤¶È£¬¸Ã±äÁ¿¶¨ÒåÔÚkernel/timer.cÎļþÖУ¬ÈçÏ£º
    long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */
    tick±äÁ¿µÄµ¥Î»ÊÇ΢Ã¦Ìs£©£¬ÓÉÓÚÔÚ²»Í¬Æ½Ì¨ÉϺêHZµÄÖµ»áÓÐËù²»Í¬£¬Òò´Ë·½³Ìʽtick£½1000000¡ÂHZµÄ½á¹û¿ÉÄÜ»áÊǸöСÊý£¬Òò´Ë½«Æä½øÐÐËÄÉáÎåÈë³ÉÒ»¸öÕûÊý£¬ËùÒÔLinux½«tick¶¨Òå³É£¨1000000£«HZ£¯2£©£¯HZ£¬ÆäÖб»³ýÊý±í´ïʽÖеÄHZ£¯2µÄ×÷ÓþÍÊÇÓÃÀ´½«tickÖµÏòÉÏÔ²Õû³ÉÒ»¸öÕûÐÍÊý¡£
    ÁíÍ⣬Linux»¹ÓúêTICK_SIZEÀ´×÷Ϊtick±äÁ¿µÄÒýÓñðÃû£¨alias£©£¬Æ䶨ÒåÈçÏ£¨arch£¯i386/kernel/time.c£©£º
    #define TICK_SIZE tick
    £¨5£©ºêLATCH£ºLinuxÓúêLATCHÀ´¶¨ÒåҪдµ½PITͨµÀ0µÄ¼ÆÊýÆ÷ÖеÄÖµ£¬Ëü±íʾPIT½«Ã»¸ô¶àÉÙ¸öʱÖÓÖÜÆÚ²úÉúÒ»´ÎʱÖÓÖжϡ£ÏÔÈ»LATCHÓ¦¸ÃÓÉÏÂÁй«Ê½¼ÆË㣺
    LATCH£½£¨1ÃëÖ®ÄÚµÄʱÖÓÖÜÆÚ¸öÊý£©¡Â£¨1ÃëÖ®ÄÚµÄʱÖÓÖжϴÎÊý£©£½£¨CLOCK_TICK_RATE£©¡Â£¨HZ£©
    ÀàËƵأ¬ÉÏÊö¹«Ê½µÄ½á¹û¿ÉÄÜ»áÊǸöСÊý£¬Ó¦¸Ã¶ÔÆä½øÐÐËÄÉáÎåÈë¡£ËùÒÔ£¬Linux½«LATCH¶¨ÒåΪ£¨include/linux/timex.h£©£º
    /* LATCH is used in the interval timer and ftape setup. */
    #define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
    ÀàËƵأ¬±»³ýÊý±í´ïʽÖеÄHZ£¯2Ò²ÊÇÓÃÀ´½«LATCHÏòÉÏÔ²Õû³ÉÒ»¸öÕûÊý¡£

    7£®3£®2 ±íʾϵͳµ±Ç°Ê±¼äµÄÄÚºËÊý¾Ý½á¹¹
    ×÷ΪһÖÖUNIXÀà²Ù×÷ϵͳ£¬LinuxÄÚºËÏÔÈ»²ÉÓñ¾½ÚÒ»¿ªÊ¼ËùÊöµÄµÚÈýÖÖ·½·¨À´±íʾϵͳµÄµ±Ç°Ê±¼ä¡£LinuxÄÚºËÔÚ±íʾϵͳµ±Ç°Ê±¼äʱÓõ½ÁËÈý¸öÖØÒªµÄÊý¾Ý½á¹¹£º
    ¢ÙÈ«¾Ö±äÁ¿jiffies£ºÕâÊÇÒ»¸ö32λµÄÎÞ·ûºÅÕûÊý£¬ÓÃÀ´±íʾ×ÔÄÚºËÉÏÒ»´ÎÆô¶¯ÒÔÀ´µÄʱÖӵδð´ÎÊý¡£Ã¿·¢ÉúÒ»´ÎʱÖӵδð£¬Äں˵ÄʱÖÓÖжϴ¦Àíº¯Êýtimer_interrupt£¨£©¶¼Òª½«¸ÃÈ«¾Ö±äÁ¿jiffies¼Ó1¡£¸Ã±äÁ¿¶¨ÒåÔÚkernel/timer.cÔ´ÎļþÖУ¬ÈçÏÂËùʾ£º
    unsigned long volatile jiffies;
    CÓïÑÔÏÞ¶¨·ûvolatile±íʾjiffiesÊÇÒ»¸öÒ׸ñäµÄ±äÁ¿£¬Òò´Ë±àÒëÆ÷½«Ê¹¶Ô¸Ã±äÁ¿µÄ·ÃÎÊ´Ó²»Í¨¹ýCPUÄÚ²¿cacheÀ´½øÐС£
    ¢ÚÈ«¾Ö±äÁ¿xtime£ºËüÊÇÒ»¸ötimeval½á¹¹ÀàÐ͵ıäÁ¿£¬ÓÃÀ´±íʾµ±Ç°Ê±¼ä¾àUNIXʱ¼ä»ù×¼1970£­01£­01 00£º00£º00µÄÏà¶ÔÃëÊýÖµ¡£½á¹¹timevalÊÇLinuxÄں˱íʾʱ¼äµÄÒ»ÖÖ¸ñʽ£¨LinuxÄں˶Ôʱ¼äµÄ±íʾÓжàÖÖ¸ñʽ£¬Ã¿ÖÖ¸ñʽ¶¼Óв»Í¬µÄʱ¼ä¾«¶È£©£¬Æäʱ¼ä¾«¶ÈÊÇ΢Ãë¡£¸Ã½á¹¹ÊÇÄں˱íʾʱ¼äʱ×î³£ÓõÄÒ»ÖÖ¸ñʽ£¬Ëü¶¨ÒåÔÚÍ·Îļþinclude/linux/time.hÖУ¬ÈçÏÂËùʾ£º
    struct timeval {
    time_t tv_sec; /* seconds */
    suseconds_t tv_usec; /* microseconds */
    };
    ÆäÖУ¬³ÉÔ±tv_sec±íʾµ±Ç°Ê±¼ä¾àUNIXʱ¼ä»ù×¼µÄÃëÊýÖµ£¬¶ø³ÉÔ±tv_usecÔò±íʾһÃëÖ®ÄÚµÄ΢ÃëÖµ£¬ÇÒ1000000>tv_usec>£½0¡£
    LinuxÄÚºËͨ¹ýtimeval½á¹¹ÀàÐ͵ÄÈ«¾Ö±äÁ¿xtimeÀ´Î¬³Öµ±Ç°Ê±¼ä£¬¸Ã±äÁ¿¶¨ÒåÔÚkernel/timer.cÎļþÖУ¬ÈçÏÂËùʾ£º
    /* The current time */
    volatile struct timeval xtime __attribute__ ((aligned (16)));
    µ«ÊÇ£¬È«¾Ö±äÁ¿xtimeËùά³ÖµÄµ±Ç°Ê±¼äͨ³£Êǹ©Óû§À´¼ìË÷ºÍÉèÖõģ¬¶øÆäËûÄÚºËÄ£¿éͨ³£ºÜÉÙʹÓÃËü£¨ÆäËûÄÚºËÄ£¿éÓõÃ×î¶àµÄÊÇjiffies£©£¬Òò´Ë¶ÔxtimeµÄ¸üв¢²»ÊÇÒ»Ïî½ôÆȵÄÈÎÎñ£¬ËùÒÔÕâÒ»¹¤×÷ͨ³£±»ÑÓ³Ùµ½Ê±ÖÓÖжϵĵװ벿·Ö£¨bottom half£©ÖÐÀ´½øÐС£ÓÉÓÚbottom halfµÄÖ´ÐÐʱ¼ä´øÓв»È·¶¨ÐÔ£¬Òò´ËΪÁ˼ÇסÄÚºËÉÏÒ»´Î¸üÐÂxtimeÊÇʲôʱºò£¬LinuxÄں˶¨ÒåÁËÒ»¸öÀàËÆÓÚjiffiesµÄÈ«¾Ö±äÁ¿wall_jiffies£¬À´±£´æÄÚºËÉÏÒ»´Î¸üÐÂxtimeʱµÄjiffiesÖµ¡£Ê±ÖÓÖжϵĵװ벿·Öÿһ´Î¸üÐÂxtimeµÄʱºî¶¼»á½«wall_jiffies¸üÐÂΪµ±Ê±µÄjiffiesÖµ¡£È«¾Ö±äÁ¿wall_jiffies¶¨ÒåÔÚkernel/timer.cÎļþÖУº
    /* jiffies at the most recent update of wall time */
    unsigned long wall_jiffies;
    ¢ÛÈ«¾Ö±äÁ¿sys_tz£ºËüÊÇÒ»¸ötimezone½á¹¹ÀàÐ͵ÄÈ«¾Ö±äÁ¿£¬±íʾϵͳµ±Ç°µÄʱÇøÐÅÏ¢¡£½á¹¹ÀàÐÍtimezone¶¨ÒåÔÚinclude/linux/time.hÍ·ÎļþÖУ¬ÈçÏÂËùʾ£º
    struct timezone {
    int tz_minuteswest; /* minutes west of Greenwich */
    int tz_dsttime; /* type of dst correction */
    };
    »ùÓÚÉÏÊö½á¹¹£¬LinuxÔÚkernel/time.cÎļþÖж¨ÒåÁËÈ«¾Ö±äÁ¿sys_tz±íʾϵͳµ±Ç°Ëù´¦µÄʱÇøÐÅÏ¢£¬ÈçÏÂËùʾ£º
    struct timezone sys_tz;

    7£®3£®3 Linux¶ÔTSCµÄ±à³ÌʵÏÖ
    LinuxÓö¨ÒåÔÚarch/i386/kernel/time.cÎļþÖеÄÈ«¾Ö±äÁ¿use_tscÀ´±íʾÄÚºËÊÇ·ñʹÓÃCPUµÄTSC¼Ä´æÆ÷£¬use_tsc£½1±íʾʹÓÃTSC£¬use_tsc£½0±íʾ²»Ê¹ÓÃTSC¡£¸Ã±äÁ¿µÄÖµÊÇÔÚtime_init()³õʼ»¯º¯ÊýÖб»³õʼ»¯µÄ£¨Ïê¼ûÏÂÒ»½Ú£©¡£¸Ã±äÁ¿µÄ¶¨ÒåÈçÏ£º
    static int use_tsc;
    ºêcpu_has_tsc¿ÉÒÔÈ·¶¨µ±Ç°ÏµÍ³µÄCPUÊÇ·ñÅäÖÃÓÐTSC¼Ä´æÆ÷¡£´ËÍ⣬ºêCONFIG_X86_TSCÒ²±íʾÊÇ·ñ´æÔÚTSC¼Ä´æÆ÷¡£

    7£®3£®3£®1 ¶ÁTSC¼Ä´æÆ÷µÄºê²Ù×÷
    x86 CPUµÄrdtscÖ¸ÁTSC¼Ä´æÆ÷µÄ¸ß32λֵ¶Áµ½EDX¼Ä´æÆ÷ÖС¢µÍ32λ¶Áµ½EAX¼Ä´æÆ÷ÖС£Linux¸ù¾Ý²»Í¬µÄÐèÒª£¬ÔÚrdtscÖ¸ÁîµÄ»ù´¡ÉÏ·â×°¼¸¸ö¸ß²ãºê²Ù×÷£¬ÒÔ¶ÁÈ¡TSC¼Ä´æÆ÷µÄÖµ¡£ËüÃǾù¶¨ÒåÔÚinclude/asm-i386/msr.hÍ·ÎļþÖУ¬ÈçÏ£º
    #define rdtsc(low,high) \
    __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))

    #define rdtscl(low) \
    __asm__ __volatile__ ("rdtsc" : "=a" (low) : : "edx")

    #define rdtscll(val) \
    __asm__ __volatile__ ("rdtsc" : "=A" (val))
    ºêrdtsc£¨£©Í¬Ê±¶ÁÈ¡TSCµÄLSBÓëMSB£¬²¢·Ö±ð±£´æµ½ºê²ÎÊýlowºÍhighÖС£ºêrdtsclÔòÖ»¶ÁÈ¡TSC¼Ä´æÆ÷µÄLSB£¬²¢±£´æµ½ºê²ÎÊýlowÖС£ºêrdtscll¶ÁÈ¡TSCµÄµ±Ç°64λֵ£¬²¢½«Æä±£´æµ½ºê²ÎÊývalÕâ¸ö64λ±äÁ¿ÖС£

    7£®3£®3£®2 У׼TSC
    Óë¿É±à³Ì¶¨Ê±Æ÷PITÏà±È£¬ÓÃTSC¼Ä´æÆ÷¿ÉÒÔ»ñµÃ¸ü¾«È·µÄʱ¼ä¶ÈÁ¿¡£µ«ÊÇÔÚ¿ÉÒÔʹÓÃTSC֮ǰ£¬Ëü±ØÐ뾫ȷµØÈ·¶¨1¸öTSC¼ÆÊýÖµµ½µ×´ú±í¶à³¤µÄʱ¼ä¼ä¸ô£¬Ò²¼´µ½µ×Òª¹ý¶à³¤Ê±¼ä¼ä¸ôTSC¼Ä´æÆ÷²Å»á¼Ó1¡£LinuxÄÚºËÓÃÈ«¾Ö±äÁ¿fast_gettimeoffset_quotientÀ´±íʾÕâ¸öÖµ£¬Æ䶨ÒåÈçÏ£¨arch/i386/kernel/time.c£©£º
    /* Cached *multiplier* to convert TSC counts to microseconds.
    * (see the equation below).
    * Equal to 2^32 * (1 / (clocks per usec) ).
    * Initialized in time_init.
    */
    unsigned long fast_gettimeoffset_quotient;
    ¸ù¾ÝÉÏÊö¶¨ÒåµÄ×¢ÊÍÎÒÃÇ¿ÉÒÔ¿´³ö£¬Õâ¸ö±äÁ¿µÄÖµÊÇͨ¹ýÏÂÊö¹«Ê½À´¼ÆËãµÄ£º
    fast_gettimeoffset_quotient £½ (2^32) / (ÿ΢ÃëÄÚµÄʱÖÓÖÜÆÚ¸öÊý)
    ¶¨ÒåÔÚarch/i386/kernel/time.cÎļþÖеĺ¯Êýcalibrate_tsc()¾ÍÊǸù¾ÝÉÏÊö¹«Ê½À´¼ÆËãfast_gettimeoffset_quotientµÄÖµµÄ¡£ÏÔÈ»Õâ¸ö¼ÆËã¹ý³Ì±ØÐëÔÚÄÚºËÆô¶¯Ê±Íê³É£¬Òò´Ë£¬º¯Êýcalibrate_tsc()Ö»±»³õʼ»¯º¯Êýtime_init()Ëùµ÷Óá£

    ÓÃTSCʵÏָ߾«¶ÈµÄʱ¼ä·þÎñ
    ÔÚÓµÓÐTSC£¨TimeStamp Counter£©µÄx86 CPUÉÏ£¬LinuxÄں˿ÉÒÔʵÏÖ΢Ã뼶µÄ¸ß¾«¶È¶¨Ê±·þÎñ£¬Ò²¼´¿ÉÒÔÈ·¶¨Á½´ÎʱÖÓÖжÏÖ®¼äµÄij¸öʱ¿ÌµÄ΢Ã뼶ʱ¼äÖµ¡£ÈçÏÂͼËùʾ£º
    ͼ7£­7 TSCʱ¼ä¹Øϵ

    ´ÓÉÏͼÖпÉÒÔ¿´³ö£¬ÒªÈ·¶¨Ê±¿ÌxµÄ΢Ã뼶ʱ¼äÖµ£¬¾Í±ØÐëÈ·¶¨Ê±¿Ìx¾àÉÏÒ»´ÎʱÖÓÖжϲúÉúʱ¿ÌµÄʱ¼ä¼ä¸ôÆ«ÒÆoffset_usecµÄÖµ£¨ÒÔ΢ÃëΪµ¥Î»£©¡£Îª´Ë£¬Äں˶¨ÒåÁËÒÔÏÂÁ½¸ö±äÁ¿£º
    £¨1£©ÖжϷþÎñÖ´ÐÐÑÓ³Ùdelay_at_last_interrupt£ºÓÉÓÚ´Ó²úÉúʱÖÓÖжϵÄÄǸöʱ¿Ìµ½ÄÚºËʱÖÓÖжϷþÎñº¯Êýtimer_interruptÕæÕýÔÚCPUÉÏÖ´ÐеÄÄǸöʱ¿ÌÖ®¼äÊÇÓÐÒ»¶ÎÑÓ³Ù¼ä¸ôµÄ£¬Òò´Ë£¬LinuxÄÚºËÓñäÁ¿delay_at_last_interruptÀ´±íʾÕâÒ»¶Îʱ¼äÑÓ³Ù¼ä¸ô£¬Æ䶨ÒåÈçÏ£¨arch/i386/kernel/time.c£©£º
    /* Number of usecs that the last interrupt was delayed */
    static int delay_at_last_interrupt;
    ¹ØÓÚdelay_at_last_interruptµÄ¼ÆËã²½ÖèÎÒÃǽ«ÔÚ·ÖÎötimer_interrupt£¨£©º¯ÊýʱÌÖÂÛ¡£
    £¨2£©È«¾Ö±äÁ¿last_tsc_low£ºËü±íʾÖжϷþÎñtimer_interruptÕæÕýÔÚCPUÉÏÖ´ÐÐʱ¿ÌµÄTSC¼Ä´æÆ÷ÖµµÄµÍ32루LSB£©¡£
    ÏÔÈ»£¬Í¨¹ýdelay_at_last_interrupt¡¢last_tsc_lowºÍʱ¿Ìx´¦µÄTSC¼Ä´æÆ÷Öµ£¬ÎÒÃǾͿÉÒÔÍêÈ«È·¶¨Ê±¿Ìx¾àÉÏÒ»´ÎʱÖÓÖжϲúÉúʱ¿ÌµÄʱ¼ä¼ä¸ôÆ«ÒÆoffset_usecµÄÖµ¡£ÊµÏÖÔÚarch/i386/kernel/time.cÖеĺ¯Êýdo_fast_gettimeoffset()¾ÍÊÇÕâÑù¼ÆËãʱ¼ä¼ä¸ôÆ«ÒƵģ¬µ±È»Ëü½öÔÚCPUÅäÖÃÓÐTSC¼Ä´æÆ÷ʱ²Å±»Ê¹Ó㬺óÃæÎÒÃÇ»áÏêϸ·ÖÎöÕâ¸öº¯Êý¡£

  7. hfh08 ÓÚ 2006-08-21 00:24:39·¢±í:

    7£®2 LinuxÄں˶ÔRTCµÄ±à³Ì
    MC146818 RTCоƬ£¨»òÆäËû¼æÈÝоƬ£¬ÈçDS12887£©¿ÉÒÔÔÚIRQ8ÉϲúÉúÖÜÆÚÐÔµÄÖжϣ¬ÖжϵÄƵÂÊÔÚ2HZ¡«8192HZÖ®¼ä¡£ÓëMC146818 RTC¶ÔÓ¦µÄÉ豸Çý¶¯³ÌÐòʵÏÖÔÚinclude/linux/rtc.hºÍdrivers£¯char/rtc.cÎļþÖУ¬¶ÔÓ¦µÄÉ豸ÎļþÊÇ£¯dev/rtc£¨major=10,minor=135£¬Ö»¶Á×Ö·ûÉ豸£©¡£Òò´ËÓû§½ø³Ì¿ÉÒÔͨ¹ý¶ÔËý½øÐбà³ÌÒÔʹµÃµ±RTCµ½´ïij¸öÌض¨µÄʱ¼äֵʱ¼¤»îIRQ8Ïߣ¬´Ó¶ø½«RTCµ±×÷Ò»¸öÄÖÖÓÀ´Óá£
    ¶øLinuxÄں˶ÔRTCµÄΨһÓÃ;¾ÍÊÇ°ÑRTCÓÃ×÷¡°ÀëÏß¡±»ò¡°ºǫ́¡±µÄʱ¼äÓëÈÕÆÚά»¤Æ÷¡£µ±LinuxÄÚºËÆô¶¯Ê±£¬Ëü´ÓRTCÖжÁȡʱ¼äÓëÈÕÆڵĻù×¼Öµ¡£È»ºóÔÙÔËÐÐÆÚ¼äÄں˾ÍÍêÈ«Å׿ªRTC£¬´Ó¶øÒÔÈí¼þµÄÐÎʽά»¤ÏµÍ³µÄµ±Ç°Ê±¼äÓëÈÕÆÚ£¬²¢ÔÚÐèҪʱ½«Ê±¼ä»Øдµ½RTCоƬÖС£
    LinuxÔÚinclude/linux/mc146818rtc.hºÍinclude/asm-i386/mc146818rtc.hÍ·ÎļþÖзֱð¶¨ÒåÁËmc146818 RTCоƬ¸÷¼Ä´æÆ÷µÄº¬ÒåÒÔ¼°RTCоƬÔÚi386ƽ̨ÉϵÄI/O¶Ë¿Ú²Ù×÷¡£¶øͨÓõÄRTC½Ó¿ÚÔòÉùÃ÷ÔÚinclude/linux/rtc.hÍ·ÎļþÖС£

    7£®2£®1 RTCоƬµÄI/O¶Ë¿Ú²Ù×÷
    LinuxÔÚinclude/asm-i386/mc146818rtc.hÍ·ÎļþÖж¨ÒåÁËRTCоƬµÄI/O¶Ë¿Ú²Ù×÷¡£¶Ë¿Ú0x70±»³ÆΪ¡°RTC¶Ë¿Ú0¡±£¬¶Ë¿Ú0x71±»³ÆΪ¡°RTC¶Ë¿Ú1¡±£¬ÈçÏÂËùʾ£º
    #ifndef RTC_PORT
    #define RTC_PORT(x) (0x70 + (x))
    #define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */
    #endif
    ÏÔÈ»£¬RTC_PORT(0)¾ÍÊÇÖ¸¶Ë¿Ú0x70£¬RTC_PORT(1)¾ÍÊÇÖ¸I/O¶Ë¿Ú0x71¡£
    ¶Ë¿Ú0x70±»ÓÃ×÷RTCоƬÄÚ²¿¼Ä´æÆ÷µÄµØÖ·Ë÷Òý¶Ë¿Ú£¬¶ø¶Ë¿Ú0x71Ôò±»ÓÃ×÷RTCоƬÄÚ²¿¼Ä´æÆ÷µÄÊý¾Ý¶Ë¿Ú¡£ÔÙ¶Áдһ¸öRTC¼Ä´æÆ÷֮ǰ£¬±ØÐëÏȰѸüĴæÆ÷ÔÚRTCоƬÄÚ²¿µÄµØÖ·Ë÷Òýֵдµ½¶Ë¿Ú0x70ÖС£¸ù¾ÝÕâÒ»µã£¬¶Áдһ¸öRTC¼Ä´æÆ÷µÄºê¶¨ÒåCMOS_READ()ºÍCMOS_WRITE()ÈçÏ£º
    #define CMOS_READ(addr) ({ \
    outb_p((addr),RTC_PORT(0)); \
    inb_p(RTC_PORT(1)); \
    })
    #define CMOS_WRITE(val, addr) ({ \
    outb_p((addr),RTC_PORT(0)); \
    outb_p((val),RTC_PORT(1)); \
    })
    #define RTC_IRQ 8
    ÔÚÉÏÊöºê¶¨ÒåÖУ¬²ÎÊýaddrÊÇRTC¼Ä´æÆ÷ÔÚоƬÄÚ²¿µÄµØÖ·Öµ£¬È¡Öµ·¶Î§ÊÇ0x00~0x3F£¬²ÎÊývalÊÇ´ýдÈë¼Ä´æÆ÷µÄÖµ¡£ºêRTC_IRQÊÇÖ¸RTCоƬËùÁ¬½ÓµÄÖжÏÇëÇóÊäÈëÏߺţ¬Í¨³£ÊÇ8¡£

    7£®2£®2 ¶ÔRTC¼Ä´æÆ÷µÄ¶¨Òå
    LinuxÔÚinclude/linux/mc146818rtc.hÕâ¸öÍ·ÎļþÖж¨ÒåÁËRTC¸÷¼Ä´æÆ÷µÄº¬Òå¡£

    £¨1£©¼Ä´æÆ÷ÄÚ²¿µØÖ·Ë÷ÒýµÄ¶¨Òå
    LinuxÄں˽öʹÓÃRTCоƬµÄʱ¼äÓëÈÕÆڼĴæÆ÷×éºÍ¿ØÖƼĴæÆ÷×飬µØַΪ0x00~0x09Ö®¼äµÄ10¸öʱ¼äÓëÈÕÆڼĴæÆ÷µÄ¶¨ÒåÈçÏ£º
    #define RTC_SECONDS 0
    #define RTC_SECONDS_ALARM 1
    #define RTC_MINUTES 2
    #define RTC_MINUTES_ALARM 3
    #define RTC_HOURS 4
    #define RTC_HOURS_ALARM 5
    /* RTC_*_alarm is always true if 2 MSBs are set */
    # define RTC_ALARM_DONT_CARE 0xC0

    #define RTC_DAY_OF_WEEK 6
    #define RTC_DAY_OF_MONTH 7
    #define RTC_MONTH 8
    #define RTC_YEAR 9

    Ëĸö¿ØÖƼĴæÆ÷µÄµØÖ·¶¨ÒåÈçÏ£º
    #define RTC_REG_A 10
    #define RTC_REG_B 11
    #define RTC_REG_C 12
    #define RTC_REG_D 13

    £¨2£©¸÷¿ØÖƼĴæÆ÷µÄ״̬λµÄÏêϸ¶¨Òå
    ¿ØÖƼĴæÆ÷A£¨0x0A£©Ö÷ÒªÓÃÓÚÑ¡ÔñRTCоƬµÄ¹¤×÷ƵÂÊ£¬Òò´ËÒ²³ÆΪRTCƵÂÊÑ¡Ôñ¼Ä´æÆ÷¡£Òò´ËLinuxÓÃÒ»¸öºê±ðÃûRTC_FREQ_SELECTÀ´±íʾ¿ØÖƼĴæÆ÷A£¬ÈçÏ£º
    #define RTC_FREQ_SELECT RTC_REG_A
    RTCƵÂʼĴæÆ÷ÖеÄλ±»·ÖΪÈý×飺¢Ùbit£Û7£Ý±íʾUIP±êÖ¾£»¢Úbit£Û6£º4£ÝÓÃÓÚ³ý·¨Æ÷µÄƵÂÊÑ¡Ôñ£»¢Ûbit£Û3£º0£ÝÓÃÓÚËÙÂÊÑ¡Ôñ¡£ËüÃǵĶ¨ÒåÈçÏ£º
    # define RTC_UIP 0x80
    # define RTC_DIV_CTL 0x70
    /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
    # define RTC_RATE_SELECT 0x0F
    ÕýÈç7.1.1.1½ÚËù½éÉܵÄÄÇÑù£¬bit£Û6£º4£ÝÓÐ5ÖпÉÄܵÄÈ¡Öµ£¬·Ö±ðΪ³ý·¨Æ÷Ñ¡Ôñ²»Í¬µÄ¹¤×÷ƵÂÊ»òÓÃÓÚÖØÖóý·¨Æ÷£¬¸÷ÖÖ¿ÉÄܵÄÈ¡ÖµÈç϶¨ÒåËùʾ£º
    /* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */
    # define RTC_REF_CLCK_4MHZ 0x00
    # define RTC_REF_CLCK_1MHZ 0x10
    # define RTC_REF_CLCK_32KHZ 0x20
    /* 2 values for divider stage reset, others for "testing purposes only" */
    # define RTC_DIV_RESET1 0x60
    # define RTC_DIV_RESET2 0x70

    ¼Ä´æÆ÷BÖеĸ÷λÓÃÓÚʹÄÜ£¯½ûÖ¹RTCµÄ¸÷ÖÖÌØÐÔ£¬Òò´Ë¿ØÖƼĴæÆ÷B£¨0x0B£©Ò²³ÆΪ¡°¿ØÖƼĴæÆ÷¡±£¬LinuxÓúê±ðÃûRTC_CONTROLÀ´±íʾ¿ØÖƼĴæÆ÷B£¬ËüÓëÆäÖеĸ÷±ê־λµÄ¶¨ÒåÈçÏÂËùʾ£º
    #define RTC_CONTROL RTC_REG_B
    # define RTC_SET 0x80 /* disable updates for clock setting */
    # define RTC_PIE 0x40 /* periodic interrupt enable */
    # define RTC_AIE 0x20 /* alarm interrupt enable */
    # define RTC_UIE 0x10 /* update-finished interrupt enable */
    # define RTC_SQWE 0x08 /* enable square-wave output */
    # define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */
    # define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */
    # define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */

    ¼Ä´æÆ÷CÊÇRTCоƬµÄÖжÏÇëÇó״̬¼Ä´æÆ÷£¬LinuxÓúê±ðÃûRTC_INTR_FLAGSÀ´±íʾ¼Ä´æÆ÷C£¬ËüÓëÆäÖеĸ÷±ê־λµÄ¶¨ÒåÈçÏÂËùʾ£º
    #define RTC_INTR_FLAGS RTC_REG_C
    /* caution - cleared by read */
    # define RTC_IRQF 0x80 /* any of the following 3 is active */
    # define RTC_PF 0x40
    # define RTC_AF 0x20
    # define RTC_UF 0x10

    ¼Ä´æÆ÷D½ö¶¨ÒåÁËÆä×î¸ßλbit£Û7£Ý£¬ÒÔ±íʾRTCоƬÊÇ·ñÓÐЧ¡£Òò´Ë¼Ä´æÆ÷DÒ²³ÆΪRTCµÄÓÐЧ¼Ä´æÆ÷¡£LinuxÓúê±ðÃûRTC_VALIDÀ´±íʾ¼Ä´æÆ÷D£¬ÈçÏ£º
    #define RTC_VALID RTC_REG_D
    # define RTC_VRT 0x80 /* valid RAM and time */

    £¨3£©¶þ½øÖƸñʽÓëBCD¸ñʽµÄÏ໥ת»»
    ÓÉÓÚʱ¼äÓëÈÕÆڼĴæÆ÷ÖеÄÖµ¿ÉÄÜÒÔBCD¸ñʽ´æ´¢£¬Ò²¿ÉÄÜÒÔ¶þ½øÖƸñʽ´æ´¢£¬Òò´ËÐèÒª¶¨Òå¶þ½øÖƸñʽÓëBCD¸ñʽ֮¼äµÄÏ໥ת»»ºê£¬ÒÔ·½±ã±à³Ì¡£ÈçÏ£º
    #ifndef BCD_TO_BIN
    #define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
    #endif

    #ifndef BIN_TO_BCD
    #define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
    #endif

    7£®2£®3 Äں˶ÔRTCµÄ²Ù×÷
    ÈçÇ°ËùÊö£¬LinuxÄÚºËÓëRTC½øÐл¥²Ù×÷µÄʱ»úÖ»ÓÐÁ½¸ö£º£¨1£©ÄÚºËÔÚÆô¶¯Ê±´ÓRTCÖжÁÈ¡Æô¶¯Ê±µÄʱ¼äÓëÈÕÆÚ£»£¨2£©ÄÚºËÔÚÐèҪʱ½«Ê±¼äÓëÈÕÆÚ»Øдµ½RTCÖС£Îª´Ë£¬LinuxÄÚºËÔÚarch/i386/kernel/time.cÎļþÖÐʵÏÖÁ˺¯Êýget_cmos_time()À´½øÐжÔRTCµÄµÚÒ»ÖÖ²Ù×÷¡£ÏÔÈ»£¬get_cmos_time()º¯Êý½ö½öÔÚÄÚºËÆô¶¯Ê±±»µ÷ÓÃÒ»´Î¡£¶ø¶ÔÓÚµÚ¶þÖÖ²Ù×÷£¬LinuxÔòͬÑùÔÚarch/i386/kernel/time.cÎļþÖÐʵÏÖÁ˺¯Êýset_rtc_mmss()£¬ÒÔÖ§³ÖÏòRTCÖлØдµ±Ç°Ê±¼äÓëÈÕÆÚ¡£ÏÂÃæÎÒÃǽ«À´·ÖÎöÕâ¶þ¸öº¯ÊýµÄʵÏÖ¡£
    ÔÚ·ÖÎöget_cmos_time()º¯Êý֮ǰ£¬ÎÒÃÇÏÈÀ´¿´¿´RTCоƬ¶ÔÆäʱ¼äÓëÈÕÆڼĴæÆ÷×éµÄ¸üÐÂÔ­Àí¡£

    £¨1£©Update In Progress
    µ±¿ØÖƼĴæÆ÷BÖеÄSET±ê־λΪ0ʱ£¬MC146818оƬÿÃ붼»áÔÚоƬÄÚ²¿Ö´ÐÐÒ»¸ö¡°¸üÐÂÖÜÆÚ¡±£¨Update Cycle£©£¬Æä×÷ÓÃÊÇÔö¼ÓÃë¼Ä´æÆ÷µÄÖµ£¬²¢¼ì²éÃë¼Ä´æÆ÷ÊÇ·ñÒç³ö¡£Èç¹ûÒç³ö£¬ÔòÔö¼Ó·ÖÖӼĴæÆ÷µÄÖµ£¬Èç´ËÒ»ÖÂÏÂÈ¥Ö±µ½Äê¼Ä´æÆ÷¡£ÔÚ¡°¸üÐÂÖÜÆÚ¡±Æڼ䣬ʱ¼äÓëÈÕÆڼĴæÆ÷×飨0x00~0x09£©ÊDz»¿ÉÓõģ¬´ËʱÈç¹û¶ÁÈ¡ËüÃǵÄÖµ½«µÃµ½Î´¶¨ÒåµÄÖµ£¬ÒòΪMC146818ÔÚÕû¸ö¸üÐÂÖÜÆÚÆÚ¼ä»á°Ñʱ¼äÓëÈÕÆڼĴæÆ÷×é´ÓCPU×ÜÏßÉÏÍÑÀ룬´Ó¶ø·ÀÖ¹Èí¼þ³ÌÐò¶Áµ½Ò»¸ö½¥±äµÄÊý¾Ý¡£
    ÔÚMC146818µÄÊäÈëʱÖÓƵÂÊ£¨Ò²¼´¾§ÌåÔöµ´Æ÷µÄƵÂÊ£©Îª4.194304MHZ»ò1.048576MHZµÄÇé¿öÏ£¬¡°¸üÐÂÖÜÆÚ¡±ÐèÒª»¨·Ñ248us£¬¶ø¶ÔÓÚÊäÈëʱÖÓƵÂÊΪ32.768KHZµÄÇé¿ö£¬¡°¸üÐÂÖÜÆÚ¡±ÐèÒª»¨·Ñ1984us£½1.984ms¡£¿ØÖƼĴæÆ÷AÖеÄUIP±ê־λÓÃÀ´±íʾMC146818ÊÇ·ñÕý´¦ÓÚ¸üÐÂÖÜÆÚÖУ¬µ±UIP´Ó0±äΪ1µÄÄǸöʱ¿Ì£¬¾Í±íʾMC146818½«ÔÚÉÔºóÂíÉϾͿª¸üÐÂÖÜÆÚ¡£ÔÚUIP´Ó0±äµ½1µÄÄǸöʱ¿ÌÓëMC146818ÕæÕý¿ªÊ¼Update CycleµÄÄǸöʱ¿ÌÖ®¼äʱÓÐÒ»¶Îʱ¼ä¼ä¸ôµÄ£¬Í¨³£ÊÇ244us¡£Ò²¾ÍÊÇ˵£¬ÔÚUIP´Ó0±äµ½1µÄ244usÖ®ºó£¬Ê±¼äÓëÈÕÆڼĴæÆ÷×éÖеÄÖµ²Å»áÕæÕý¿ªÊ¼¸Ä±ä£¬¶øÔÚÕâÖ®¼äµÄ244us¼ä¸ôÄÚ£¬ËüÃǵÄÖµ²¢²»»áÕæÕý¸Ä±ä¡£ÈçÏÂͼËùʾ£º

    £¨2£©get_cmos_time()º¯Êý
    ¸Ãº¯ÊýÖ»±»Äں˵ijõʼ»¯Àý³Ìtime_init()ºÍÄں˵ÄAPMÄ£¿éËùµ÷Óá£ÆäÔ´ÂëÈçÏ£º
    /* not static: needed by APM */
    unsigned long get_cmos_time(void)
    {
    unsigned int year, mon, day, hour, min, sec;
    int i;

    /* The Linux interpretation of the CMOS clock register contents:
    * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
    * RTC registers show the second which has precisely just started.
    * Let's hope other operating systems interpret the RTC the same way.
    */
    /* read RTC exactly on falling edge of update flag */
    for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
    if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
    break;
    for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
    if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
    break;
    do { /* Isn't this overkill ? UIP above should guarantee consistency */
    sec = CMOS_READ(RTC_SECONDS);
    min = CMOS_READ(RTC_MINUTES);
    hour = CMOS_READ(RTC_HOURS);
    day = CMOS_READ(RTC_DAY_OF_MONTH);
    mon = CMOS_READ(RTC_MONTH);
    year = CMOS_READ(RTC_YEAR);
    } while (sec != CMOS_READ(RTC_SECONDS));
    if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
    {
    BCD_TO_BIN(sec);
    BCD_TO_BIN(min);
    BCD_TO_BIN(hour);
    BCD_TO_BIN(day);
    BCD_TO_BIN(mon);
    BCD_TO_BIN(year);
    }
    if ((year += 1900) < 1970)
    year += 100;
    return mktime(year, mon, day, hour, min, sec);
    }
    ¶Ô¸Ãº¯ÊýµÄ×¢ÊÍÈçÏ£º
    £¨1£©ÔÚ´ÓRTCÖжÁȡʱ¼äʱ£¬ÓÉÓÚRTC´æÔÚUpdate Cycle£¬Òò´ËÈí¼þ·¢³ö¶Á²Ù×÷µÄʱ»úÊǺÜÖØÒªµÄ¡£¶Ô´Ë£¬get_cmos_time()º¯Êýͨ¹ýUIP±ê־λÀ´½â¾öÕâ¸öÎÊÌ⣺µÚÒ»¸öforÑ­»·²»Í£µØ¶ÁÈ¡RTCƵÂÊÑ¡Ôñ¼Ä´æÆ÷ÖеÄUIP±ê־룬²¢ÇÒÖ»Òª¶Áµ½UIPµÄֵΪ1¾ÍÂíÉÏÍ˳öÕâ¸öforÑ­»·¡£µÚ¶þ¸öforÑ­»·Í¬Ñù²»Í£µØ¶ÁÈ¡UIP±ê־룬µ«ËûÖ»ÒªÒ»¶Áµ½UIPµÄֵΪ0¾ÍÂíÉÏÍ˳öÕâ¸öforÑ­»·¡£ÕâÁ½¸öforÑ­»·µÄÄ¿µÄ¾ÍÊÇÒªÔÚÈí¼þÂß¼­ÉÏͬ²½RTCµÄUpdate Cycle£¬ÏÔÈ»µÚ¶þ¸öforÑ­»·×î´ó¿ÉÄÜÐèÒª2.228ms(TBUC+max(TUC)=244us+1984us=2.228ms)
    (2)´ÓµÚ¶þ¸öforÑ­»·Í˳öºó£¬RTCµÄUpdate CycleÒѾ­½áÊø¡£´ËʱÎÒÃǾÍÒѾ­°Ñµ±Ç°Ê±¼äÂß¼­¶¨×¼ÔÚRTCµÄµ±Ç°Ò»Ãëʱ¼ä¼ä¸ôÄÚ¡£Ò²¾ÍÊÇ˵£¬ÕâÊÇÎÒÃǾͿÉÒÔ¿ªÊ¼´ÓRTC¼Ä´æÆ÷ÖжÁÈ¡µ±Ç°Ê±¼äÖµ¡£µ«ÊÇҪעÒ⣬¶Á²Ù×÷Ó¦¸Ã±£Ö¤ÔÚ244usÄÚÍê³É£¨×¼È·µØ˵£¬¶Á²Ù×÷ÒªÔÚRTCµÄÏÂÒ»¸ö¸üÐÂÖÜÆÚ¿ªÊ¼Ö®Ç°Íê³É£¬244usµÄÏÞÖÆÊǹý·ÖÆ«Ö´µÄ£º£­£©¡£ËùÒÔ£¬get_cmos_time()º¯Êý½ÓÏÂÀ´Í¨¹ýCMOS_READ()ºê´ÓRTCÖÐÒÀ´Î¶ÁÈ¡Ãë¡¢·ÖÖÓ¡¢Ð¡Ê±¡¢ÈÕÆÚ¡¢Ô·ݺÍÄê·Ö¡£ÕâÀïµÄdo{}while(sec!=CMOS_READ(RTC_SECOND))Ñ­»·¾ÍÊÇÓÃÀ´È·±£ÉÏÊö6¸ö¶Á²Ù×÷±ØÐëÔÚÏÂÒ»¸öUpdate Cycle¿ªÊ¼Ö®Ç°Íê³É¡£
    £¨3£©½ÓÏÂÀ´Åж¨Ê±¼äµÄÊý¾Ý¸ñʽ£¬PC»úÖÐÒ»°ã×ÜÊÇʹÓÃBCD¸ñʽµÄʱ¼ä£¬Òò´ËÐèҪͨ¹ýBCD_TO_BIN()ºê°ÑBCD¸ñʽת»»Îª¶þ½øÖƸñʽ¡£
    £¨4£©½ÓÏÂÀ´¶ÔÄê·Ö½øÐÐÐÞÕý£¬ÒÔ½«Äê·Ýת»»Îª¡°19XX¡±µÄ¸ñʽ£¬Èç¹ûÊÇ1970ÒÔÇ°µÄÄê·Ý£¬Ôò½«Æä¼ÓÉÏ100¡£
    £¨5£©×îºóµ÷ÓÃmktime()º¯Êý½«µ±Ç°Ê±¼äÓëÈÕÆÚת»»ÎªÏà¶ÔÓÚ1970£­01£­01 00£º00£º00µÄÃëÊýÖµ£¬²¢½«Æä×÷Ϊº¯Êý·µ»ØÖµ·µ»Ø¡£

    º¯Êýmktime()¶¨ÒåÔÚinclude/linux/time.hÍ·ÎļþÖУ¬ËüÓÃÀ´¸ù¾ÝGaussËã·¨½«ÒÔyear/mon/day/hour/min/sec£¨Èç1980£­12£­31 23£º59£º59£©¸ñʽ±íʾµÄʱ¼äת»»ÎªÏà¶ÔÓÚ1970£­01£­01 00£º00£º00Õâ¸öUNIXʱ¼ä»ù×¼ÒÔÀ´µÄÏà¶ÔÃëÊý¡£ÆäÔ´ÂëÈçÏ£º
    static inline unsigned long
    mktime (unsigned int year, unsigned int mon,
    unsigned int day, unsigned int hour,
    unsigned int min, unsigned int sec)
    {
    if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
    mon += 12; /* Puts Feb last since it has leap day */
    year -= 1;
    }

    return (((
    (unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +
    year*365 - 719499
    )*24 + hour /* now have hours */
    )*60 + min /* now have minutes */
    )*60 + sec; /* finally seconds */
    }

    £¨3£©set_rtc_mmss()º¯Êý
    ¸Ãº¯ÊýÓÃÀ´¸üÐÂRTCÖеÄʱ¼ä£¬Ëü½öÓÐÒ»¸ö²ÎÊýnowtime£¬ÊÇÒÔÃëÊý±íʾµÄµ±Ç°Ê±¼ä£¬ÆäÔ´ÂëÈçÏ£º
    static int set_rtc_mmss(unsigned long nowtime)
    {
    int retval = 0;
    int real_seconds, real_minutes, cmos_minutes;
    unsigned char save_control, save_freq_select;

    /* gets recalled with irq locally disabled */
    spin_lock(&rtc_lock);
    save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
    CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);

    save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
    CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);

    cmos_minutes = CMOS_READ(RTC_MINUTES);
    if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
    BCD_TO_BIN(cmos_minutes);

    /*
    * since we're only adjusting minutes and seconds,
    * don't interfere with hour overflow. This avoids
    * messing with unknown time zones but requires your
    * RTC not to be off by more than 15 minutes
    */
    real_seconds = nowtime % 60;
    real_minutes = nowtime / 60;
    if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
    real_minutes += 30; /* correct for half hour time zone */
    real_minutes %= 60;

    if (abs(real_minutes - cmos_minutes) < 30) {
    if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
    BIN_TO_BCD(real_seconds);
    BIN_TO_BCD(real_minutes);
    }
    CMOS_WRITE(real_seconds,RTC_SECONDS);
    CMOS_WRITE(real_minutes,RTC_MINUTES);
    } else {
    printk(KERN_WARNING
    "set_rtc_mmss: can't update from %d to %d\n",
    cmos_minutes, real_minutes);
    retval = -1;
    }

    /* The following flags have to be released exactly in this order,
    * otherwise the DS12887 (popular MC146818A clone with integrated
    * battery and quartz) will not reset the oscillator and will not
    * update precisely 500 ms later. You won't find this mentioned in
    * the Dallas Semiconductor data sheets, but who believes data
    * sheets anyway ... -- Markus Kuhn
    */
    CMOS_WRITE(save_control, RTC_CONTROL);
    CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
    spin_unlock(&rtc_lock);

    return retval;
    }
    ¶Ô¸Ãº¯ÊýµÄ×¢ÊÍÈçÏ£º
    £¨1£©Ê×ÏȶÔ×ÔÐýËørtc_lock½øÐмÓËø¡£¶¨ÒåÔÚarch/i386/kernel/time.cÎļþÖеÄÈ«¾Ö×ÔÐýËørtc_lockÓÃÀ´´®Ðл¯ËùÓÐCPU¶ÔRTCµÄ²Ù×÷¡£
    £¨2£©½ÓÏÂÀ´£¬ÔÚRTC¿ØÖƼĴæÆ÷ÖÐÉèÖÃSET±ê־룬ÒÔ±ã֪ͨRTCÈí¼þ³ÌÐòËæºóÂíÉϽ«Òª¸üÐÂËüµÄʱ¼äÓëÈÕÆÚ¡£Îª´ËÏÈ°ÑRTC_CONTROL¼Ä´æÆ÷µÄµ±Ç°Öµ¶Áµ½±äÁ¿save_controlÖУ¬È»ºóÔÙ°ÑÖµ£¨save_control | RTC_SET£©»Øдµ½¼Ä´æÆ÷RTC_CONTROLÖС£
    £¨3£©È»ºó£¬Í¨¹ýRTC_FREQ_SELECT¼Ä´æÆ÷ÖÐbit£Û6£º4£ÝÖØÆôRTCоƬÄÚ²¿µÄ³ý·¨Æ÷¡£Îª´Ë£¬ÀàËƵØÏÈ°ÑRTC_FREQ_SELECT¼Ä´æÆ÷µÄµ±Ç°Öµ¶Áµ½±äÁ¿save_freq_selectÖУ¬È»ºóÔÙ°ÑÖµ£¨save_freq_select £ü RTC_DIV_RESET2£©»Øдµ½RTC_FREQ_SELECT¼Ä´æÆ÷ÖС£
    £¨4£©½Ó׎«RTC_MINUTES¼Ä´æÆ÷µÄµ±Ç°Öµ¶Áµ½±äÁ¿cmos_minutesÖУ¬²¢¸ù¾ÝÐèÒª½«Ëü´ÓBCD¸ñʽת»¯Îª¶þ½øÖƸñʽ¡£
    £¨5£©´Ónowtime²ÎÊýÖеõ½µ±Ç°Ê±¼äµÄÃëÊýºÍ·ÖÖÓÊý¡£·Ö±ð±£´æµ½real_secondsºÍreal_minutes±äÁ¿¡£×¢Ò⣬ÕâÀï¶ÔÓÚ°ëСʱÇøµÄÇé¿öÒªÐÞÕý·ÖÖÓÊýreal_minutesµÄÖµ¡£
    £¨6£©È»ºó£¬ÔÚreal_minutesÓëRTC_MINUTES¼Ä´æÆ÷µÄÔ­Öµcmos_minutes¶þÕßÏà²î²»³¬¹ý30·ÖÖÓµÄÇé¿öÏ£¬½«real_secondsºÍreal_minutesËù±íʾµÄʱ¼äֵдµ½RTCµÄÃë¼Ä´æÆ÷ºÍ·ÖÖӼĴæÆ÷ÖС£µ±È»£¬ÔÚ»Øд֮ǰҪ¼ÇµÃ°Ñ¶þ½øÖÆת»»ÎªBCD¸ñʽ¡£
    £¨7£©×îºó£¬»Ö¸´RTC_CONTROL¼Ä´æÆ÷ºÍRTC_FREQ_SELECT¼Ä´æÆ÷Ô­À´µÄÖµ¡£Õâ¶þÕßµÄÏȺó´ÎÐòÊÇ£ºÏȻָ´RTC_CONTROL¼Ä´æÆ÷£¬ÔÙ»Ö¸´RTC_FREQ_SELECT¼Ä´æÆ÷¡£È»ºóÔÚ½â³ý×ÔÐýËørtc_lockºó¾Í¿ÉÒÔ·µ»ØÁË¡£

    ×îºó£¬ÐèҪ˵Ã÷µÄÒ»µãÊÇ£¬set_rtc_mmss()º¯Êý¾¡¿ÉÄÜÔÚ¿¿½üÒ»Ãëʱ¼ä¼ä¸ôµÄÖмäλÖã¨Ò²¼´500ms´¦£©×óÓÒ±»µ÷ÓᣴËÍ⣬LinuxÄں˶Ôÿһ´Î³É¹¦µÄ¸üÐÂRTCʱ¼ä¶¼ÁôÏÂʱ¼ä¹ì¼££¬ËüÓÃÒ»¸öϵͳȫ¾Ö±äÁ¿last_rtc_updateÀ´±íʾÄÚºË×î½üÒ»´Î³É¹¦µØ¶ÔRTC½øÐиüеÄʱ¼ä£¨µ¥Î»ÊÇÃëÊý£©¡£¸Ã±äÁ¿¶¨ÒåÔÚarch/i386/kernel/time.cÎļþÖУº
    /* last time the cmos clock got updated */
    static long last_rtc_update;
    ÿһ´Î³É¹¦µØµ÷ÓÃset_rtc_mmss()º¯Êýºó£¬Äں˶¼»áÂíÉϽ«last_rtc_update¸üÐÂΪµ±Ç°Ê±¼ä£¨¾ßÌåÇë¼û7.4.3½Ú£©¡£