adjtimex() 是 Linux 才有的調校時間系統呼叫,使用 David L. Mills 的 clock adjustment algorithm (見 RFC 5905),透過 struct timex * 設定參數和回傳資料。ntp_adjtime() 只是用不同的 mode 名稱,偏好用在 NTP daemon。
#include <sys/timex.h>
int adjtimex(struct timex *buf);
int ntp_adjtime(struct timex *buf);
struct timex {
int modes; /* Mode selector */
long offset; /* Time offset; nanoseconds (有設 STA_NANO) 或 microseconds */
long freq; /* Frequency offset; see NOTES for units */
long maxerror; /* Maximum error (microseconds) */
long esterror; /* Estimated error (microseconds) */
int status; /* Clock command/status */
long constant; /* PLL time constant */
long precision; /* Clock precision
(microseconds, read-only) */
long tolerance; /* Clock frequency tolerance (read-only);
see NOTES for units */
struct timeval time;
/* Current time (read-only, except for
ADJ_SETOFFSET); upon return, time.tv_usec
contains nanoseconds, if STA_NANO status
flag is set, otherwise microseconds */
long tick; /* Microseconds between clock ticks */
long ppsfreq; /* PPS (pulse per second) frequency
(read-only); see NOTES for units */
long jitter; /* PPS jitter (read-only); nanoseconds, if
STA_NANO status flag is set, otherwise
microseconds */
int shift; /* PPS interval duration
(seconds, read-only) */
long stabil; /* PPS stability (read-only);
see NOTES for units */
long jitcnt; /* PPS count of jitter limit exceeded
events (read-only) */
long calcnt; /* PPS count of calibration intervals
(read-only) */
long errcnt; /* PPS count of calibration errors
(read-only) */
long stbcnt; /* PPS count of stability limit exceeded
events (read-only) */
int tai; /* TAI offset, as set by previous ADJ_TAI
operation (seconds, read-only,
since Linux 2.6.26) */
/* Further padding bytes to allow for future expansion */
};
modes 決定哪些參數需要設,是下列的 bit‐wise 組合:
- ADJ_OFFSET:offset 設定時間偏移。Linux 2.6.26 以後會切去大於 ±0.5s 的部份,之前超出回 EINVAL。
- ADJ_FREQUENCY:freq 設定頻率偏移。Linux 2.6.26 以後會切去大於 ±32768000 的部份,之前超出範圍回 EINVAL。
- ADJ_MAXERROR:maxerror 設定 maximum time error。
- ADJ_ESTERROR:esterror 設定 estimated time error。
- ADJ_STATUS:status 設定 clock status bits,如下:
The buf.status field is a bit mask that is used to set and/or retrieve status bits associated with the NTP implementation. Some bits in the mask are both readable and settable, while others are read-only. STA_PLL (read-write) Enable phase-locked loop (PLL) updates via ADJ_OFFSET. STA_PPSFREQ (read-write) Enable PPS (pulse-per-second) frequency discipline. STA_PPSTIME (read-write) Enable PPS time discipline. STA_FLL (read-write) Select frequency-locked loop (FLL) mode. STA_INS (read-write) Insert a leap second after the last second of the UTC day, thus extending the last minute of the day by one second. Leap-second insertion will occur each day, so long as this flag remains set. STA_DEL (read-write) Delete a leap second at the last second of the UTC day. Leap second deletion will occur each day, so long as this flag re‐ mains set. STA_UNSYNC (read-write) Clock unsynchronized. STA_FREQHOLD (read-write) Hold frequency. Normally adjustments made via ADJ_OFFSET result in dampened frequency adjustments also being made. So a single call corrects the current offset, but as offsets in the same di‐ rection are made repeatedly, the small frequency adjustments will accumulate to fix the long-term skew. This flag prevents the small frequency adjustment from being made when correcting for an ADJ_OFFSET value. STA_PPSSIGNAL (read-only) A valid PPS (pulse-per-second) signal is present. STA_PPSJITTER (read-only) PPS signal jitter exceeded. STA_PPSWANDER (read-only) PPS signal wander exceeded. STA_PPSERROR (read-only) PPS signal calibration error. STA_CLOCKERR (read-only) Clock hardware fault. STA_NANO (read-only; since Linux 2.6.26) Resolution (0 = microsecond, 1 = nanoseconds). Set via ADJ_NANO, cleared via ADJ_MICRO. STA_MODE (since Linux 2.6.26) Mode (0 = Phase Locked Loop, 1 = Frequency Locked Loop). STA_CLK (read-only; since Linux 2.6.26) Clock source (0 = A, 1 = B); currently unused. Attempts to set read-only status bits are silently ignored.- ADJ_TIMECONST:constant 設定 PLL time constant。If the STA_NANO status flag (see below) is clear, the kernel adds 4 to this value.
- ADJ_SETOFFSET (Linux 2.6.39+):time 增加到 current time. If buf.status includes the ADJ_NANO flag, then buf.time.tv_usec is interpreted as a nanosecond value; otherwise it is interpreted as microseconds.
- ADJ_MICRO、ADJ_NANO (Linux 2.6.26+):分別選擇 microsecond 或 nanosecond resolution,兩者不能同時使用。
- ADJ_TAI (Linux 2.6.26+):constant 設 TAI (Atomic International Time) offset。 ADJ_TAI should not be used in conjunction with ADJ_TIMECONST, since the latter mode also employs the buf.constant field. For a complete explanation of TAI and the difference between TAI and UTC, see BIPM ⟨http://www.bipm.org/en/bipm/tai/tai.html⟩
- ADJ_TICK:tick 設定 tick value。
另外,modes 可以使用下列多 bit 組成的值,此時其它 bit 不能使用:
- ADJ_OFFSET_SINGLESHOT (含有 ADJ_OFFSET):傳統 adjtime() 方式,使用 offset 的 µs,kernel 進行每次最多 MAX_TICKADJ 逐步調整。offset 回傳之前剩餘未調整的。
- ADJ_OFFSET_SS_READ (Linux 2.6.28+):offset 回傳先前 ADJ_OFFSET_SINGLESHOT 還有多少未調整。
一般使用者 modes 只能用 0 或 ADJ_OFFSET_SS_READ,其它要 superuser。
回傳值:成功回傳如下 clock state,失敗回傳 -1 並設 errno。
- TIME_OK:Clock synchronized, no leap second adjustment pending.
- TIME_INS:Indicates that a leap second will be added at the end of the UTC day.
- TIME_DEL:Indicates that a leap second will be deleted at the end of the UTC day.
- TIME_OOP:Insertion of a leap second is in progress.
- TIME_WAIT:A leap-second insertion or deletion has been completed. This value will be returned until the next ADJ_STATUS oper‐ ation clears the STA_INS and STA_DEL flags.
- TIME_ERROR (或 TIME_BAD):The system clock is not synchronized to a reliable server. This value is returned when any of the following holds true:
- Either STA_UNSYNC or STA_CLOCKERR is set.
- STA_PPSSIGNAL is clear and either STA_PPSFREQ or STA_PPSTIME is set.
- STA_PPSTIME and STA_PPSJITTER are both set.
- STA_PPSFREQ is set and either STA_PPSWANDER or STA_PPSJITTER is set.
註:Linux 3.4 開始,the call operates asynchronously and the return value usually will not reflect a state change caused by the call itself.
失敗 errno 有:
- EFAULT:參數不可寫。
- EINVAL (kernels before Linux 2.6.26) An attempt was made to set buf.freq to a value outside the range (-33554432, +33554432).
- EINVAL (kernels before Linux 2.6.26) An attempt was made to set buf.offset to a value outside the permitted range. In kernels before Linux 2.0, the permitted range was (-131072, +131072). From Linux 2.0 onwards, the per‐ mitted range was (-512000, +512000).
- EINVAL An attempt was made to set buf.status to a value other than those listed above.
- EINVAL An attempt was made to set buf.tick to a value outside the range 900000/HZ to 1100000/HZ, where HZ is the system timer interrupt frequency.
- EPERM buf.modes is neither 0 nor ADJ_OFFSET_SS_READ, and the caller does not have sufficient privilege. Under Linux, the CAP_SYS_TIME capability is required.
NOTES
In struct timex, freq, ppsfreq, and stabil are ppm (parts per million)
with a 16-bit fractional part, which means that a value of 1 in one of
those fields actually means 2^-16 ppm, and 2^16=65536 is 1 ppm. This
is the case for both input values (in the case of freq) and output val‐
ues.
The leap-second processing triggered by STA_INS and STA_DEL is done by
the kernel in timer context. Thus, it will take one tick into the sec‐
ond for the leap second to be inserted or deleted.
SEE ALSO
settimeofday(2), adjtime(3), ntp_gettime(3), capabilities(7), time(7),
adjtimex(8), hwclock(8)
NTP "Kernel Application Program Interface"
⟨http://www.slac.stanford.edu/comp/unix/package/rtems/src/ssrlApps/
ntpNanoclock/api.htm⟩
#define ADJ_OFFSET 0x0001 /* time offset */
#define ADJ_FREQUENCY 0x0002 /* frequency offset */
#define ADJ_MAXERROR 0x0004 /* maximum time error */
#define ADJ_ESTERROR 0x0008 /* estimated time error */
#define ADJ_STATUS 0x0010 /* clock status */
#define ADJ_TIMECONST 0x0020 /* pll time constant */
#define ADJ_TICK 0x4000 /* tick value */
#define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */
asmlinkage long sys_adjtimex(struct timex __user *txc_p)
{
struct timex txc; /* Local copy of parameter */
int ret;
/* Copy the user data space into the kernel copy
* structure. But bear in mind that the structures
* may change
*/
if(copy_from_user(&txc, txc_p, sizeof(struct timex)))
return -EFAULT;
ret = do_adjtimex(&txc);
return copy_to_user(txc_p, &txc, sizeof(struct timex)) ? -EFAULT : ret;
}
參考
- man adjtimex
- Linux kernel 原始碼
- 軟體時鐘使用系統呼叫 adjtimex 來同步其它外部時鐘來源時,可以每 11 分鐘去調整硬體時鐘。
沒有留言:
張貼留言