⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 timesync.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 2 页
字号:
#include <u.h>#include <libc.h>#include <auth.h>#include <ip.h>#include <mp.h>/* nanosecond times */#define SEC 1000000000LL#define MIN (60LL*SEC)#define HOUR (60LL*MIN)#define DAY (24LL*HOUR)enum {	Fs,	Rtc,	Ntp,	Utc,	Gps,	HZAvgSecs=	3*60,	/* target averaging period for the frequency in seconds */	MinSampleSecs=	60,	/* minimum sampling time in seconds */};char *dir = "/tmp";	// directory sample files live inchar *logfile = "timesync";char *timeserver;char *Rootid;int utcfil;int gpsfil;int debug;int impotent;int logging;int type;int gmtdelta;	// rtc+gmtdelta = gmtuvlong avgerr;// ntp server infoint stratum = 14;vlong mydisp, rootdisp;vlong mydelay, rootdelay;vlong avgdelay;vlong lastutc;uchar rootid[4];char *sysid;int myprec;// list of time samplestypedef struct Sample Sample;struct Sample{	Sample	*next;	uvlong	ticks;	vlong	ltime;	vlong	stime;};// ntp packettypedef struct NTPpkt NTPpkt;struct NTPpkt{	uchar	mode;	uchar	stratum;	uchar	poll;	uchar	precision;	uchar	rootdelay[4];	uchar	rootdisp[4];	uchar	rootid[4];	uchar	refts[8];	uchar	origts[8];		// departed client	uchar	recvts[8];		// arrived at server	uchar	xmitts[8];		// departed server	uchar	keyid[4];	uchar	digest[16];};// ntp servertypedef struct NTPserver NTPserver;struct NTPserver{	NTPserver *next;	char	*name;	uchar	stratum;	uchar	precision;	vlong	rootdelay;	vlong	rootdisp;	vlong	rtt;	vlong	dt;};NTPserver *ntpservers;enum{	NTPSIZE= 	48,		// basic ntp packet	NTPDIGESTSIZE=	20,		// key and digest};// error bound of last sampleulong	ε;static void	addntpserver(char *name);static int	adjustperiod(vlong diff, vlong accuracy, int secs);static void	background(void);static int	caperror(vlong dhz, int tsecs, vlong taccuracy);static long	fstime(void);static int	gettime(vlong *nsec, uvlong *ticks, uvlong *hz); // returns time, ticks, hzstatic int	getclockprecision(vlong);static vlong	gpssample(void);static void	hnputts(void *p, vlong nsec);static void	hnputts(void *p, vlong nsec);static void	inittime(void);static vlong	nhgetts(void *p);static vlong	nhgetts(void *p);static void	ntpserver(char*);static vlong	ntpsample(void);static int	ntptimediff(NTPserver *ns);static int	openfreqfile(void);static vlong	readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz);static long	rtctime(void);static vlong	sample(long (*get)(void));static void	setpriority(void);static void	setrootid(char *d);static void	settime(vlong now, uvlong hz, vlong delta, int n); // set time, hz, delta, periodstatic vlong	utcsample(void);static uvlong	vabs(vlong);static uvlong	whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt, vlong ticks, vlong period);static void	writefreqfile(int fd, vlong hz, int secs, vlong diff);// ((1970-1900)*365 + 17/*leap days*/)*24*60*60#define EPOCHDIFF 2208988800ULvoidmain(int argc, char **argv){	int i;	int secs;	// sampling period	int tsecs;	// temporary sampling period	int t, fd;	Sample *s, *x, *first, **l;	vlong diff, accuracy, taccuracy;	uvlong hz, minhz, maxhz, period, nhz;	char *servenet[4];	int nservenet;	char *a;	Tm tl, tg;	type = Fs;		// by default, sync with the file system	debug = 0;	accuracy = 1000000LL;	// default accuracy is 1 millisecond	nservenet = 0;	tsecs = secs = MinSampleSecs;	timeserver = "";	ARGBEGIN{	case 'a':		a = ARGF();		if(a == nil)			sysfatal("bad accuracy specified");		accuracy = strtoll(a, 0, 0);	// accuracy specified in ns		if(accuracy <= 1LL)			sysfatal("bad accuracy specified");		break;	case 'f':		type = Fs;		stratum = 2;		break;	case 'r':		type = Rtc;		stratum = 0;		break;	case 'U':		type = Utc;		stratum = 1;		break;	case 'G':		type = Gps;		stratum = 1;		break;	case 'n':		type = Ntp;		break;	case 'D':		debug = 1;		break;	case 'd':		dir = ARGF();		break;	case 'L':		//		// Assume time source in local time rather than GMT.		// Calculate difference so that rtctime can return GMT.		// This is useful with the rtc on PC's that run Windows		// since Windows keeps the local time in the rtc.		//		t = time(0);		tl = *localtime(t);		tg = *gmtime(t);		// if the years are different, we're at most a day off, so just rewrite		if(tl.year < tg.year){			tg.year--;			tg.yday = tl.yday + 1;		}else if(tl.year > tg.year){			tl.year--;			tl.yday = tg.yday+1;		}		assert(tl.year == tg.year);		tg.sec -= tl.sec;		tg.min -= tl.min;		tg.hour -= tl.hour;		tg.yday -= tl.yday;		gmtdelta = tg.sec+60*(tg.min+60*(tg.hour+tg.yday*24));		assert(abs(gmtdelta) <= 24*60*60);		break;	case 'i':		impotent = 1;		break;	case 'I':		Rootid = ARGF();		break;	case 's':		if(nservenet >= nelem(servenet))			sysfatal("too many networks to serve on");		a = ARGF();		if(a == nil)			sysfatal("must specify network to serve on");		servenet[nservenet++] = a;		break;	case 'l':		logging = 1;		break;	case 'S':		a = ARGF();		if(a == nil)			sysfatal("bad stratum specified");		stratum = strtoll(a, 0, 0);		break;	}ARGEND;	fmtinstall('E', eipfmt);	fmtinstall('I', eipfmt);	fmtinstall('V', eipfmt);	sysid = getenv("sysname");	//  detach from the current namespace	if(debug)		rfork(RFNAMEG);	switch(type){	case Utc:		if(argc > 0)			timeserver = argv[0];		else			sysfatal("bad time source");		break;	case Gps:		if(argc > 0)			timeserver = argv[0];		else			timeserver = "/mnt/gps/time";		break;	case Fs:		if(argc > 0)			timeserver = argv[0];		else			timeserver = "/srv/boot";		break;	case Ntp:		if(argc > 0){			for(i = 0; i <argc; i++)				addntpserver(argv[i]);		} else {			addntpserver("$ntp");		}		break;	}	setpriority();	// figure out our time interface and initial frequency	inittime();	gettime(0, 0, &hz);	minhz = hz/10;	maxhz = hz*10;	myprec = getclockprecision(hz);	// convert the accuracy from nanoseconds to ticks	taccuracy = hz*accuracy/SEC;	//	//  bind in clocks	//	switch(type){	case Fs:		fd = open(timeserver, ORDWR);		if(fd < 0)			sysfatal("opening %s: %r\n", timeserver);		if(amount(fd, "/n/boot", MREPL, "") < 0)			sysfatal("mounting %s: %r\n", timeserver);		close(fd);		break;	case Rtc:		bind("#r", "/dev", MAFTER);		if(access("/dev/rtc", AREAD) < 0)			sysfatal("accessing /dev/rtc: %r\n");		break;	case Utc:		fd = open(timeserver, OREAD);		if(fd < 0)			sysfatal("opening %s: %r\n", timeserver);		utcfil = fd;		break;	case Gps:		fd = open(timeserver, OREAD);		if(fd < 0)			sysfatal("opening %s: %r\n", timeserver);		gpsfil = fd;		break;	}	//	//  start a local ntp server(s)	//	for(i = 0; i < nservenet; i++){		switch(rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){		case -1:			sysfatal("forking: %r");			break;		case 0:			ntpserver(servenet[i]);			_exits(0);			break;		default:			break;		}	}	// get the last known frequency from the file	fd = openfreqfile();	hz = readfreqfile(fd, hz, minhz, maxhz);	// this is the main loop.  it gets a sample, adjusts the	// clock and computes a sleep period until the next loop.	// we balance frequency drift against the length of the	// period to avoid blowing the accuracy limit.	first = nil;	l = &first;	avgerr = accuracy>>1;	for(;; background(),sleep(tsecs*(1000))){		s = mallocz(sizeof(*s), 1);		diff = 0;		// get times for this sample		ε = ~0;		switch(type){		case Fs:			s->stime = sample(fstime);			break;		case Rtc:			s->stime = sample(rtctime);			break;		case Utc:			s->stime = utcsample();			if(s->stime == 0LL){				if(logging)					syslog(0, logfile, "no sample");				free(s);				if (secs > 60 * 15)					tsecs = 60*15;				continue;			}			break;		case Ntp:			diff = ntpsample();			if(diff == 0LL){				if(logging)					syslog(0, logfile, "no sample");				free(s);				if(secs > 60*15)					tsecs = 60*15;				continue;			}			break;		case Gps:			diff = gpssample();			if(diff == 0LL){				if(logging)					syslog(0, logfile, "no sample");				free(s);				if(secs > 60*15)					tsecs = 60*15;				continue;			}		}		// use fastest method to read local clock and ticks		gettime(&s->ltime, &s->ticks, 0);		if(type == Ntp || type == Gps)			s->stime = s->ltime + diff;		// if the sample was bad, ignore it		if(s->stime < 0){			free(s);			continue;		}		// reset local time		diff = s->stime - s->ltime;		if(diff > 10*SEC || diff < -10*SEC){			// we're way off, just set the time			secs = MinSampleSecs;			settime(s->stime, 0, 0, 0);		} else {			// keep a running average of the error.			avgerr = (avgerr>>1) + (vabs(diff)>>1);			// the time to next sample depends on how good or			// bad we're doing.			tsecs = secs = adjustperiod(diff, accuracy, secs);			// work off the fixed difference.  This is done			// by adding a ramp to the clock.  Each 100th of a			// second (or so) the kernel will add diff/(4*secs*100)			// to the clock.  we only do 1/4 of the difference per			// period to dampen any measurement noise.			//			// any difference greater than ε we work off during the			// sampling period.			if(abs(diff) > ε){				if(diff > 0)					settime(-1, 0, diff-((3*ε)/4), secs);				else					settime(-1, 0, diff+((3*ε)/4), secs);			} else				settime(-1, 0, diff, 4*secs);		}		if(debug)			fprint(2, "δ %lld avgδ %lld f %lld\n", diff, avgerr, hz);		// dump old samples (keep at least one)		while(first != nil){			if(first->next == nil)				break;			if(s->stime - first->next->stime < DAY)				break;			x = first;			first = first->next;			free(x);		}		// The sampling error is limited by the total error.  If		// we make sure the sampling period is at least 16 million		// times the average error, we should calculate a frequency		// with on average a 1e-7 error.		//		// So that big hz changes don't blow our accuracy requirement,		// we shorten the period to make sure that δhz*secs will be		// greater than the accuracy limit.		period = avgerr<<24;		for(x = first; x != nil; x = x->next){			if(s->stime - x->stime < period)				break;			if(x->next == nil || s->stime - x->next->stime < period)				break;		}		if(x != nil){			nhz = whatisthefrequencykenneth(				hz, minhz, maxhz,				s->stime - x->stime,				s->ticks - x->ticks,				period);			tsecs = caperror(vabs(nhz-hz), tsecs, taccuracy);			hz = nhz;			writefreqfile(fd, hz, (s->stime - x->stime)/SEC, diff);		}		// add current sample to list.		*l = s;		l = &s->next;		if(logging)			syslog(0, logfile, "δ %lld avgδ %lld hz %lld",				diff, avgerr, hz);	}}//// adjust the sampling period with some histeresis//static intadjustperiod(vlong diff, vlong accuracy, int secs){	uvlong absdiff;	absdiff = vabs(diff);	if(absdiff < (accuracy>>1))		secs += 60;	else if(absdiff > accuracy)		secs >>= 1;	else		secs -= 60;	if(secs < MinSampleSecs)		secs = MinSampleSecs;	return secs;}//// adjust the frequency//static uvlongwhatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt, vlong ticks, vlong period){	static mpint *mpdt;	static mpint *mpticks;	static mpint *mphz;	static mpint *mpbillion;	uvlong ohz = hz;	// sanity check	if(dt <= 0 || ticks <= 0)		return hz;	if(mphz == nil){		mphz = mpnew(0);		mpbillion = uvtomp(SEC, nil);	}	// hz = (ticks*SEC)/dt	mpdt = vtomp(dt, mpdt);	mpticks = vtomp(ticks, mpticks);	mpmul(mpticks, mpbillion, mpticks);	mpdiv(mpticks, mpdt, mphz, nil);	hz = mptoui(mphz);	// sanity	if(hz < minhz || hz > maxhz)		return ohz;	// damp the change if we're shorter than the target period	if(period > dt)		hz = (12ULL*ohz + 4ULL*hz)/16ULL;	settime(-1, hz, 0, 0);	return hz;}// We may be changing the frequency to match a bad measurement// or to match a condition no longer in effet.  To make sure// that this doesn't blow our error budget over the next measurement// period, shorten the period to make sure that δhz*secs will be// less than the accuracy limit.  Here taccuracy is accuracy converted// from nanoseconds to ticks.static intcaperror(vlong dhz, int tsecs, vlong taccuracy){	if(dhz*tsecs <= taccuracy)		return tsecs;	if(debug)		fprint(2, "δhz %lld tsecs %d tacc %lld\n", dhz, tsecs, taccuracy);	tsecs = taccuracy/dhz;	if(tsecs < MinSampleSecs)		tsecs = MinSampleSecs;	return tsecs;}////  kernel interface//enum{	Ibintime,	Insec,	Itiming,};int ifc;int bintimefd = -1;int timingfd = -1;int nsecfd = -1;int fastclockfd = -1;static voidinittime(void){	int mode;	if(impotent)		mode = OREAD;	else		mode = ORDWR;	// bind in clocks	if(access("/dev/time", 0) < 0)		bind("#c", "/dev", MAFTER);	if(access("/dev/rtc", 0) < 0)		bind("#r", "/dev", MAFTER);	// figure out what interface we have	ifc = Ibintime;	bintimefd = open("/dev/bintime", mode);	if(bintimefd >= 0)		return;	ifc = Insec;	nsecfd = open("/dev/nsec", mode);	if(nsecfd < 0)		sysfatal("opening /dev/nsec");	fastclockfd = open("/dev/fastclock", mode);	if(fastclockfd < 0)		sysfatal("opening /dev/fastclock");	timingfd = open("/dev/timing", OREAD);	if(timingfd < 0)		return;	ifc = Itiming;}////  convert binary numbers from/to kernel//static uvlong uvorder = 0x0001020304050607ULL;static uchar*be2vlong(vlong *to, uchar *f){	uchar *t, *o;	int i;	t = (uchar*)to;	o = (uchar*)&uvorder;	for(i = 0; i < sizeof(vlong); i++)		t[o[i]] = f[i];	return f+sizeof(vlong);}static uchar*vlong2be(uchar *t, vlong from){	uchar *f, *o;	int i;	f = (uchar*)&from;	o = (uchar*)&uvorder;	for(i = 0; i < sizeof(vlong); i++)		t[i] = f[o[i]];	return t+sizeof(vlong);}static long order = 0x00010203;static uchar*be2long(long *to, uchar *f){	uchar *t, *o;	int i;	t = (uchar*)to;	o = (uchar*)&order;	for(i = 0; i < sizeof(long); i++)		t[o[i]] = f[i];	return f+sizeof(long);}static uchar*long2be(uchar *t, long from){	uchar *f, *o;	int i;	f = (uchar*)&from;	o = (uchar*)&order;	for(i = 0; i < sizeof(long); i++)		t[i] = f[o[i]];	return t+sizeof(long);}//// read ticks and local time in nanoseconds//static intgettime(vlong *nsec, uvlong *ticks, uvlong *hz){	int i, n;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -