linux下对系统时间的设置

hwclock可以设置系统时间,大家可能都用过。但是我想每个人对知道此命令的途径却不完全相同。 我陈述一下自己得知此命令的过程,希望能那些对linux望而却步的初学者有些帮助,linux本身 公开源码,这对那些追根问底的人是个不错的选择。 文中过程废话较多,假设读者是初学者,对一些过程不是很清楚。高手见谅! 几天前要做几台服务器的时间同步,和同事讨论系统时间设置的问题,我模糊记得date可以设置 系统时间,和他打赌可以使用(为此输了三条鱼,:( )比如: [root@TestRA test]# date -s "Fri Jan 11 15:32:16 GMT 2002" Fri Jan 11 23:32:16 CST 2002 再用date一看成功了! 正得意间,同事的reboot了机器,重启后发现时间未变。郁闷,找原因。 先看看这个date是哪个土程序写的? [root@TestRA test]# whereis date date: /bin/date /usr/man/man1/date.1 [root@TestRA test]# rpm -qa -filesbypkg |grep "/bin/date" sh-utils /bin/date 原来是sh-utils这个包,从redhat7.1的光盘中找到sh-utils-2.0.src.rpm文件,解开用source-insight 看了看,发现在date.c中有date命令的实现。发现其实现非常简单: date.c: main()->stime()->settimeofday() 这个settimeofday就没有实现了,在linux下man了一下 发现原来是个libc中的函数,可是在libc的工程中也未发现其实现(建议大家手头最好有glibc的源码) 估计是个系统调用,打开linux-kernel的工程,查找sys_settimeofday,果然有此函数,发现 kernel/time.c#L191 191 asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz) 192 { 193 struct timeval new_tv; 194 struct timezone new_tz; 195 196 if (tv) { 197 if (copy_from_user(&new_tv, tv, sizeof(*tv))) 198 return -EFAULT; 199 } 200 if (tz) { 201 if (copy_from_user(&new_tz, tz, sizeof(*tz))) 202 return -EFAULT; 203 } 204 205 return do_sys_settimeofday(tv ? &new_tv : NULL, tz ? &new_tz : NULL); 206 } 函数很简单,从用户空间得到参数,然后调do_sys_settimeofday(kernel中常用的命名方式) 也在time.c中。 165 int do_sys_settimeofday(struct timeval *tv, struct timezone *tz) 166 { 167 static int firsttime = 1; 168 169 if (!capable(CAP_SYS_TIME)) 170 return -EPERM; 171 172 if (tz) { 173 /* SMP safe, global irq locking makes it work. */ 174 sys_tz = *tz; 175 if (firsttime) { 176 firsttime = 0; 177 if (!tv) 178 warp_clock(); 179 } 180 } 181 if (tv) 182 { 183 /* SMP safe, again the code in arch/foo/time.c should 184 * globally block out interrupts when it runs. 185 */ 186 do_settimeofday(tv); 187 } 188 return 0; 189 } 也很简单,判断了一下是否是root,不然没有权限return -PERM,主要是do_settimeofday。 arch/i386/kernel/time.c#L264 264 void do_settimeofday(struct timeval *tv) 265 { 266 write_lock_irq(&xtime_lock); 267 /* This is revolting. We need to set the xtime.tv_usec 268 * correctly. However, the value in this location is 269 * is value at the last tick. 270 * Discover what correction gettimeofday 271 * would have done, and then undo it! 272 */ 273 tv->tv_usec -= do_gettimeoffset(); 274 275 while (tv->tv_usec < 0) { 276 tv->tv_usec += 1000000; 277 tv->tv_sec--; 278 } 279 280 xtime = *tv; 281 time_adjust = 0; /* stop active adjtime() */ 282 time_status |= STA_UNSYNC; 283 time_maxerror = NTP_PHASE_LIMIT; 284 time_esterror = NTP_PHASE_LIMIT; 285 write_unlock_irq(&xtime_lock); 286 } 最终的实现,发现只是对一些系统变量的设置,果然没有对CMOS的write操作。不甘心,为了那三条鱼 拼了,觉得既然变量变了,是不是在时钟中断时会写CMOS同步呢,找到time中断函数timer_interrupt 在原文中有一段描述, 356 /* 357 * timer_interrupt() needs to keep up the real-time clock, 358 * as well as call the "do_timer()" routine every clocktick 359 */ 欣喜若狂,再看在do_timer_interrupt中有实现 380 /* 381 * If we have an externally synchronized Linux clock, then update 382 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be 383 * called as close as possible to 500 ms before the new second starts. 384 */ 385 if ((time_status & STA_UNSYNC) == 0 && 386 xtime.tv_sec > last_rtc_update + 660 && 387 xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && 388 xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { 389 if (set_rtc_mmss(xtime.tv_sec) == 0) 390 last_rtc_update = xtime.tv_sec; 391 else 392 last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ 393 } 我kao,敌人搞得这么复杂,懒得看程序,看看敌人的描述吧。。。好像和程序实现差不多。主要是 set_rtc_mmss(xtime.tv_sec),那个程序也不短,不全copy了,免得像是灌水(也差不多了),主要是 314 /* 315 * since we're only adjusting minutes and seconds, 316 * don't interfere with hour overflow. This avoids 317 * messing with unknown time zones but requires your 318 * RTC not to be off by more than 15 minutes 319 */ 320 real_seconds = nowtime % 60; 321 real_minutes = nowtime / 60; 322 if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) 323 real_minutes += 30; /* correct for half hour time zone */ 324 real_minutes %= 60; 325 326 if (abs(real_minutes - cmos_minutes) < 30) { 327 if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 328 BIN_TO_BCD(real_seconds); 329 BIN_TO_BCD(real_minutes); 330 } 331 CMOS_WRITE(real_seconds,RTC_SECONDS); 332 CMOS_WRITE(real_minutes,RTC_MINUTES); sorry,不是灌水,不要打我!这段比较重要。看了看发现又是理解不对,敌人并不同步分钟以上的 时间,没有搞头了,不过发现CMOS_WRITE这个顺眼的程序,TNND,这么大个操作系统总该有设系统 时间的地方吧。不过找CMOS_WRITE还是比较痛苦的,比我头发都多(有些夸张,是我头发比较少), 有RTC_SECONDS嘛,找其定义 include/linux/mc146818rtc.h#L32 32 #define RTC_SECONDS 0 33 #define RTC_SECONDS_ALARM 1 34 #define RTC_MINUTES 2 35 #define RTC_MINUTES_ALARM 3 36 #define RTC_HOURS 4 37 #define RTC_HOURS_ALARM 5 38 /* RTC_*_alarm is always true if 2 MSBs are set */ 39 # define RTC_ALARM_DONT_CARE 0xC0 40 41 #define RTC_DAY_OF_WEEK 6 42 #define RTC_DAY_OF_MONTH 7 43 #define RTC_MONTH 8 44 #define RTC_YEAR 9 有定义就好办,系统中总应该有写年的地方吧,找。。。 drivers/char/rtc.c#L 363 CMOS_WRITE(yrs, RTC_YEAR); 364 CMOS_WRITE(mon, RTC_MONTH); 365 CMOS_WRITE(day, RTC_DAY_OF_MONTH); 366 CMOS_WRITE(hrs, RTC_HOURS); 367 CMOS_WRITE(min, RTC_MINUTES); 368 CMOS_WRITE(sec, RTC_SECONDS); 应该是此处,看看是在char/rtc.c中,怎么跑到驱动中来了,发现是在ioctl中的RTC_SET_TIME中, 原来系统实现将对CMOS时间的设置搞到这里,看看系统/dev中果然有rtc文件,试试。。 #include #include #include #include #include #include #include #include #include "getdate.h" extern long timezone; int main(int argc,char *argv[]) { struct rtc_time time; struct tm *tm; struct timeval tt; int fd = -1; int ret; if (argc != 2) { printf("settime [date string]/n"); return -1; } timezone = 8; tt.tv_sec = get_date(argv[1],NULL); tt.tv_usec = 0; if (tt.tv_sec <= 0) { printf("[date string] is not right/n"); return -2; } memset(&time,0,sizeof(struct rtc_time)); memset(&tm,0,sizeof(struct tm)); fd = open("/dev/rtc",O_RDONLY); if (fd >= 0) { tm = localtime(&tt.tv_sec); memcpy(&time,tm,sizeof(struct rtc_time)); ret = ioctl(fd,RTC_SET_TIME,&time); settimeofday(&tt,NULL); } printf("return is %d/n",ret); if (fd >= 0 ) close(fd); return ret; } get_date函数是我从sh-utils中抄的,试了一下,OK了。 高兴之余觉得不对吧,敌人作出来不会只为编程用,命令行应该也有现成的,果然在/sbin中 [root@TestRA /sbin]# strings * -f|grep "/dev/rtc" 找到hwclock和clock命令,man了一下,发现hwclock可以设置CMOS时间。 FT,白忙了这么半天!!!发现最终在用date设时间后只要hwclock -w一下就可以写到CMOS中, 原理当然也是/dev/rtc。 结论:敌人的东西很完善,不要怀疑,有问题从自己身上找原因。呵呵!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值