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

📄 nt_clockstuff.c

📁 Network time protocol source study
💻 C
字号:
/* Windows NT Clock Routines * * * Revision History: * $Log$ * Revision 1.9  2000/11/19 09:02:12  dietrich * From: Ron Thornton [rthornto@pictel.com] * Sent: Thu 11/16/00 8:51 AM * On Windows 2000 it requires a privilege on the current process token * that is disabled by default on Windows 2000. * * I set the token by adding the following code at the beginning of the * init_winnt_time() function in nt_clockstuff.c. * * Revision 1.8  2000/11/19 08:03:20  dietrich * From: "Colin Dancer" <colin.dancer@pyrochrome.net> * To: <bugs@ntp.org> * Sent: 10 November 2000 12:59 * Subject: NT bug in NTP 4.0.99j * * I've found a bug in (and produced a fix for) the NT clock interpolation * code in NTP 4.0.99j. * * The symptoms of the problem are that gettimeofday() function on NT * can be wrong by hundreds of seconds if, while a gettimeofday() call * is being processed, an APC completes after the query of the performance * counter but before the lock is grabbed.  The most obvious fix is to move * the lock to include the querying of the performance counter, but this * could affect the predictability of the timestamp so I have instead * tweaked the code to detect and sidestep the duff calculation. * * I've also found that on a loaded system the execution of the APC can be * delayed, leading to errors of upto 10ms.  There is no easy fix to this, * but I do have code for an alternative interpolation scheme which avoids * the problem on single processor systems. I'm currently integrating this * along with code for deciding which algorithm to use based on whether * the system is SP or MP. * * Created by Sven Dietrich  sven@inter-yacht.com * */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include "ntp_stdlib.h"#include "clockstuff.h"#include "ntp_timer.h"extern double sys_residual;	/* residual from previous adjustment */static long last_Adj = 0;static CRITICAL_SECTION TimerCritialSection; /* lock for LastTimerCount & LastTimerTime */static ULONGLONG RollOverCount = 0;static ULONGLONG LastTimerCount = 0;static ULONGLONG LastTimerTime = 0;static HANDLE ClockThreadHandle = NULL;static HANDLE TimerThreadExitRequest = NULL;static DWORD every = 0;static DWORD initial_units_per_tick = 0;static DWORD lastLowTimer = 0;ULONGLONG PerfFrequency = 0;static DWORD units_per_tick = 0;static DOUBLE ppm_per_adjust_unit = 0.0;/* * adj_systime - called once every second to make system time adjustments. * Returns 1 if okay, 0 if trouble. */intadj_systime(	double now	){	double dtemp;	u_char isneg = 0;	int rc;   long dwTimeAdjustment;	/*	 * Add the residual from the previous adjustment to the new	 * adjustment, bound and round.	 */	dtemp = sys_residual + now;	sys_residual = 0;	if (dtemp < 0) {		isneg = 1;		dtemp = -dtemp;	}	if (dtemp > NTP_MAXFREQ)		dtemp = NTP_MAXFREQ;	dtemp = dtemp * 1e6;	if (isneg)		dtemp = -dtemp;	/* dtemp is in micro seconds. NT uses 100 ns units,	 * so a unit change in dwTimeAdjustment corresponds	 * to slewing 10 ppm on a 100 Hz system.	 * Calculate the number of 100ns units to add,	 * using OS tick frequency as per suggestion from Harry Pyle,	 * and leave the remainder in dtemp */	dwTimeAdjustment = (DWORD)( dtemp / ppm_per_adjust_unit + (isneg ? -0.5 : 0.5)) ;	dtemp += (double) -dwTimeAdjustment * ppm_per_adjust_unit;		/* only adjust the clock if adjustment changes */	if (last_Adj != dwTimeAdjustment) { 				last_Adj = dwTimeAdjustment;# ifdef DEBUG		if (debug > 1)			printf("SetSystemTimeAdjustment( %ld) + (%ld)\n", dwTimeAdjustment, units_per_tick);			# endif			dwTimeAdjustment += units_per_tick;			rc = !SetSystemTimeAdjustment(dwTimeAdjustment, FALSE);	}	else rc = 0;	if (rc)	{		msyslog(LOG_ERR, "Can't adjust time: %m");		return 0;	}	else {		sys_residual = dtemp / 1000000.0;	}#ifdef DEBUG	if (debug > 6)		printf("adj_systime: adj %.9f -> remaining residual %.9f\n", now, sys_residual);#endif	return 1;}void init_winnt_time(void) {	BOOL noslew;	HANDLE hToken;	TOKEN_PRIVILEGES tkp;	/*	 * Get privileges needed for fiddling with the clock	  */	  /* get the current process token handle */	if (!OpenProcessToken(GetCurrentProcess(),	  TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {		  msyslog(LOG_ERR, "OpenProcessToken failed: %m");		  exit(-1);		  }	  /* get the LUID for system-time privilege. */	LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);	tkp.PrivilegeCount = 1;  /* one privilege to set */	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;	/* get set-time privilege for this process. */	AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,	 	(PTOKEN_PRIVILEGES) NULL, 0);	/* cannot use return value of AdjustTokenPrivileges. */	/* (success does not indicate all privileges were set) */	if (GetLastError() != ERROR_SUCCESS) 		{	  	msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");	 	/* later set time call will probably fail */	  	}	/* Reset the Clock to a reasonable increment */	if (!GetSystemTimeAdjustment(&initial_units_per_tick, &every,&noslew))		{		msyslog(LOG_ERR, "GetSystemTimeAdjustment failed: %m\n");		exit (-1);		}	units_per_tick = initial_units_per_tick;	/* Calculate the time adjustment resulting from incrementing	 * units per tick by 1 unit for 1 second */	ppm_per_adjust_unit = 1000000.0 / (double) every;#ifdef DEBUG	msyslog(LOG_INFO, "Initial Clock increment %7.1f us",			(float) (units_per_tick / 10));	msyslog(LOG_INFO, "Adjustment rate %5.3f ppm/s", ppm_per_adjust_unit);#endif}void reset_winnt_time(void) {	/* restore the clock frequency back to its original value */	if (!SetSystemTimeAdjustment(initial_units_per_tick, FALSE)) {		msyslog(LOG_ERR, "Failed to reset clock frequency, SetSystemTimeAdjustment(): %m");	}	if (!SetSystemTimeAdjustment(0, TRUE)) {		msyslog(LOG_ERR, "Failed to reset clock state, SetSystemTimeAdjustment(): %m");	}}intgettimeofday(	struct timeval *tv	){	/*  Use the system time (roughly synchronised to the tick, and	 *  extrapolated using the system performance counter.	 */	ULONGLONG Count;	LARGE_INTEGER LargeIntNowCount;	ULONGLONG Time;	ULONGLONG NowCount;   ULONGLONG PreCount;                                                  /*FIX*/	LONGLONG TicksElapsed;	LONG time_adjustment;	/*  Mark a mark ASAP. The latency to here should	 *  be reasonably deterministic	 */   PreCount = LastTimerCount;                                           /*FIX*/	if (!QueryPerformanceCounter(&LargeIntNowCount)) {		msyslog(LOG_ERR, "QueryPeformanceCounter failed: %m");		exit(1);	}	NowCount = LargeIntNowCount.QuadPart;	/*  Get base time we are going to extrapolate from	 */		EnterCriticalSection(&TimerCritialSection);		Count = LastTimerCount;		Time = LastTimerTime;	LeaveCriticalSection(&TimerCritialSection);	/*  Calculate when now is.	 *	 *  Result = LastTimerTime +  (NowCount - LastTimerCount) / PerfFrequency	 */	if (NowCount >= Count)   {	    TicksElapsed = NowCount - Count; /* linear progression of ticks */   }	else   {     /************************************************************************/     /* Differentiate between real rollover and the case of taking a         */     /* perfcount then the APC coming in.                                    */     /************************************************************************/     if (Count > PreCount)                                              /*FIX*/     {                                                                  /*FIX*/       TicksElapsed = 0;                                                /*FIX*/     }                                                                  /*FIX*/     else                                                               /*FIX*/     {                                                                  /*FIX*/       TicksElapsed = NowCount + (RollOverCount - Count);               /*FIX*/     }                                                                  /*FIX*/   }	/*  Calculate the new time (in 100's of nano-seconds)	 */    time_adjustment = (long) ((TicksElapsed * HECTONANOSECONDS) / PerfFrequency);	Time += time_adjustment;	/* Convert the hecto-nano second time to tv format	 */	Time -= FILETIME_1970;	tv->tv_sec = (LONG) ( Time / 10000000ui64);	tv->tv_usec = (LONG) (( Time % 10000000ui64) / 10);	return 0;}static void CALLBACKTimerApcFunction(	LPVOID lpArgToCompletionRoutine,	DWORD dwTimerLowValue,	DWORD dwTimerHighValue	){	LARGE_INTEGER LargeIntNowCount;	(void) lpArgToCompletionRoutine; /* not used */	if (dwTimerLowValue == lastLowTimer) return;		/* Grab the counter first of all */	QueryPerformanceCounter(&LargeIntNowCount);	/* Save this for next time */	lastLowTimer = dwTimerLowValue;	/* Check to see if the counter has rolled. This happens	   more often on Multi-CPU systems */	if ((ULONGLONG) LargeIntNowCount.QuadPart < LastTimerCount) {		/* Counter Rolled - try and estimate the rollover point using		  the nominal counter frequency divided by an estimate of the		  OS frequency */		RollOverCount = LastTimerCount + PerfFrequency * every /  HECTONANOSECONDS -			(ULONGLONG) LargeIntNowCount.QuadPart;#ifdef DEBUG		msyslog(LOG_INFO,			"Performance Counter Rollover %I64u:\rLast Timer Count %I64u\rCurrent Count %I64u",				RollOverCount, LastTimerCount, LargeIntNowCount.QuadPart);#endif	}	/* Now we can hang out and wait for the critical section to free up;	   we will get the CPU this timeslice. Meanwhile other tasks can use	   the last value of LastTimerCount */			EnterCriticalSection(&TimerCritialSection);	LastTimerCount = (ULONGLONG) LargeIntNowCount.QuadPart;	LastTimerTime = ((ULONGLONG) dwTimerHighValue << 32) +				(ULONGLONG) dwTimerLowValue;	LeaveCriticalSection(&TimerCritialSection);}DWORD WINAPI ClockThread(void *arg){	LARGE_INTEGER DueTime;	HANDLE WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);	(void) arg; /* not used */	if (WaitableTimerHandle != NULL) {		DueTime.QuadPart = 0i64;		if (SetWaitableTimer(WaitableTimerHandle, &DueTime, 1L /* ms */, TimerApcFunction, &WaitableTimerHandle, FALSE) != NO_ERROR) {			for(;;) {				if (WaitForSingleObjectEx(TimerThreadExitRequest, INFINITE, TRUE) == WAIT_OBJECT_0) {					break; /* we've been asked to exit */				}			}		}		CloseHandle(WaitableTimerHandle);		WaitableTimerHandle = NULL;	}	return 0;}static void StartClockThread(void){	DWORD tid;	FILETIME StartTime;	LARGE_INTEGER Freq = { 0, 0 };		/* get the performance counter freq*/    if (!QueryPerformanceFrequency(&Freq)) {		msyslog(LOG_ERR, "QueryPerformanceFrequency failed: %m\n");		exit (-1);	}	PerfFrequency = Freq.QuadPart;	/* init variables with the time now */	GetSystemTimeAsFileTime(&StartTime);	LastTimerTime = (((ULONGLONG) StartTime.dwHighDateTime) << 32) +		(ULONGLONG) StartTime.dwLowDateTime;	/* init sync objects */	InitializeCriticalSection(&TimerCritialSection);	TimerThreadExitRequest = CreateEvent(NULL, FALSE, FALSE, NULL);	ClockThreadHandle = CreateThread(NULL, 0, ClockThread, NULL, 0, &tid);	if (ClockThreadHandle != NULL) {		/* remember the thread priority is only within the process class */		  if (!SetThreadPriority(ClockThreadHandle, THREAD_PRIORITY_TIME_CRITICAL)) {#ifdef DEBUG			printf("Error setting thread priority\n");#endif		  }	}}static void StopClockThread(void){		if (SetEvent(TimerThreadExitRequest) &&	    WaitForSingleObject(ClockThreadHandle, 10000L) == 0) {		CloseHandle(TimerThreadExitRequest);		TimerThreadExitRequest = NULL;		CloseHandle(ClockThreadHandle);		ClockThreadHandle = NULL;		DeleteCriticalSection(&TimerCritialSection);		msyslog(LOG_INFO, "The Network Time Protocol Service has stopped.");	}	else		msyslog(LOG_ERR, "Network Time Protocol Service Failed to Stop");}typedef void (__cdecl *CRuntimeFunction)(void);#pragma data_seg(".CRT$XIY")	CRuntimeFunction _StartClockThread = StartClockThread;#pragma data_seg(".CRT$XTY")	CRuntimeFunction _StopClockThread = StopClockThread;#pragma data_seg()  /* reset */

⌨️ 快捷键说明

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