NAME
adjtimex, clock_adjtime, ntp_adjtime - 커널 클럭 조정
SYNOPSIS
#include <sys/timex.h>
int adjtimex(struct timex *buf);
int clock_adjtime(clockid_t clk_id, struct timex *buf);
int ntp_adjtime(struct timex *buf);
DESCRIPTION
리눅스에서는 David L. Mills의 클럭 조정 알고리즘(RFC 5905)을 사용한다. 시스템 호출 adjtimex()는 이 알고리즘의 조정 매개변수들을 읽고 설정한다. timex 구조체 포인터를 받아서 (선택된) 필드 값들로 커널 매개변수를 갱신하고, 그 구조체를 현행 커널 값들로 갱신해서 반환한다. 구조체는 다음과 같이 선언돼 있다.
struct timex {
int modes; /* 동작 선택 */
long offset; /* 시간 오프셋. 상태 플래그 STA_NANO가
설정돼 있으면 나노초, 아니면
마이크로초 */
long freq; /* 진동수 오프셋. 단위는 NOTES 참고 */
long maxerror; /* 최대 오차 (마이크로초) */
long esterror; /* 추정 오차 (마이크로초) */
int status; /* 클럭 명령/상태 */
long constant; /* 위상 동기 루프(PLL) 시간 상수 */
long precision; /* 클럭 정밀도
(마이크로초, 읽기 전용) */
long tolerance; /* 클럭 진동수 허용 오차 (읽기 전용).
단위는 NOTES 참고 */
struct timeval time;
/* 현재 시간. (읽기 전용, ADJ_SETOFFSET에선
예외.) 반환 시 time.tv_usec에 담기는
값이 STA_NANO 상태 플래그가 설정돼
있으면 나노초, 아니면 마이크로초 */
long tick; /* 클럭 틱 간격 (마이크로초) */
long ppsfreq; /* 펄스 반복(PPS) 진동수
(읽기 전용). 단위는 NOTES 참고 */
long jitter; /* PPS 지터 (읽기 전용). 상태 플래그
STA_NANO가 설정돼 있으면 나노초,
아니면 마이크로초 */
int shift; /* PPS 구간 길이
(초, 읽기 전용) */
long stabil; /* PPS 안정성 (읽기 전용).
단위는 NOTES 참고 */
long jitcnt; /* PPS 지터 제한 초과 발생 횟수
(읽기 전용) */
long calcnt; /* PPS 보정 구간 수 (읽기 전용) */
long errcnt; /* PPS 보정 오류 횟수 (읽기 전용) */
long stbcnt; /* PPS 안정성 제한 초과 발생 횟수
(읽기 전용) */
int tai; /* TAI 오프셋. 앞선 ADJ_TAI 동작에서
설정한 값. (초, 읽기 전용,
리눅스 2.6.26부터.) */
/* 향후 확장을 위한 추가 패딩 바이트 */
};
modes 필드에 따라 설정할 매개변수가 정해진다. (잠시 후 설명하겠지만 ntp_adjtime()에는 동등하지만 이름이 다른 상수들을 쓴다.) 그 필드는 다음 비트들을 0개 이상 비트 or 조합해서 담은 비트 마스크다.
ADJ_OFFSETbuf.offset으로 시간 오프셋 설정. 리눅스 2.6.26부터는 제공된 값을 (-0.5s, +0.5s) 범위로 잘라낸다. 이전 커널에서는 제공된 값이 범위를 벗어나면EINVAL오류가 발생한다.ADJ_FREQUENCYbuf.freq로 진동수 오프셋 설정. 리눅스 2.6.26부터는 제공된 값을 (-32768000, +32768000) 범위로 잘라낸다. 이전 커널에서는 제공된 값이 범위를 벗어나면EINVAL오류가 발생한다.ADJ_MAXERRORbuf.maxerror로 최대 시간 오차 설정.ADJ_ESTERRORbuf.esterror로 추정 시간 오차 설정.ADJ_STATUSbuf.status로 클럭 상태 비트 설정. 이 비트들에 대해선 아래에서 설명한다.ADJ_TIMECONSTbuf.constant로 PLL 시간 상수 설정.STA_NANO상태 플래그(아래 참고)가 해제돼 있으면 커널에서 이 값에 4를 더한다.ADJ_SETOFFSET(리눅스 2.6.39부터)-
현재 시간에
buf.time더하기.buf.status에ADJ_NANO플래그가 포함돼 있으면buf.time.tv_usec을 나노초 값으로 해석한다. 아니면 마이크로초로 해석한다.buf.time의 값은 그 두 필드의 합이되,buf.time.tv_usec필드가 음수가 아니어야 한다. 다음은 나노초 해상도로timeval을 정규화하는 방법을 보여 주는 예시다.while (buf.time.tv_usec < 0) { buf.time.tv_sec -= 1; buf.time.tv_usec += 1000000000; } ADJ_MICRO(리눅스 2.6.26부터)- 마이크로초 정밀도 선택.
ADJ_NANO(리눅스 2.6.26부터)- 나노초 정밀도 선택.
ADJ_MICRO와ADJ_NANO중 하나만 지정해야 한다. ADJ_TAI(리눅스 2.6.26부터)-
buf.tai로 국제원자시(TAI) 오프셋 설정.ADJ_TAI를ADJ_TIMECONST와 함께 쓰지 말아야 한다. 그 모드에서도buf.constant필드를 이용하기 때문이다.TAI가 무엇이고 TAI와 UTC의 차이가 뭔지에 대한 설명은 BIPM(http://www.bipm.org/en/bipm/tai/tai.html) 참고.
ADJ_TICKbuf.tick으로 틱 값을 설정.
또는 modes에 다음 (여러 비트로 된 마스크) 값들 중 하나를 지정할 수도 있으며, 그 경우 modes에 다른 비트들은 지정하지 않는 게 좋다.
ADJ_OFFSET_SINGLESHOT- 구식 adjtime(3) 방식:
buf.offset에 마이크로초 단위로 지정된 조정 값에 따라 시간을 (점진적으로) 조정한다. ADJ_OFFSET_SS_READ(리눅스 2.6.28부터 동작)- 앞선
ADJ_OFFSET_SINGLESHOT동작 후에 남은 조정 시간 양을 (buf.offset으로) 반환한다. 이 기능은 리눅스 2.6.24에서 추가되었는데 리눅스 2.6.28까지는 올바로 동작하지 않았다.
일반 사용자는 modes에 0 또는 ADJ_OFFSET_SS_READ 값만 지정할 수 있다. 수퍼유저만 매개변수 설정을 할 수 있다.
buf.status 필드는 NTP 구현과 관련된 상태 비트를 설정 및/또는 조회하는 데 쓰는 비트 마스크다. 마스크의 일부 비트는 읽기와 설정이 모두 가능하지만 나머지는 읽기 전용이다.
STA_PLL(읽기-쓰기)ADJ_OFFSET을 통한 위상 동기 루프(PLL) 갱신 활성화.STA_PPSFREQ(읽기-쓰기)- 펄스 반복(PPS) 진동수 조정 활성화.
STA_PPSTIME(읽기-쓰기)- PPS 시간 조정 활성화.
STA_FLL(읽기-쓰기)- 진동수 동기 루프(FLL) 모드 선택.
STA_INS(읽기-쓰기)- 그 UTC 일의 마지막 초 다음에 윤초를 삽입한다. 그래서 그 날의 마지막 분을 1초만큼 늘인다. 이 플래그가 설정돼 있는 동안은 매일 윤초 삽입이 일어나게 된다.
STA_DEL(읽기-쓰기)- 그 UTC 일의 마지막 초에서 윤초를 삭제한다. 이 플래그가 설정돼 있는 동안은 매일 윤초 삭제가 일어나게 된다.
STA_UNSYNC(읽기-쓰기)- 클럭이 비동기 상태임.
STA_FREQHOLD(읽기-쓰기)-
진동수 유지.
ADJ_OFFSET을 통해 조정을 하면 보통 진동수 감쇄 조정도 이뤄지게 된다. 그래서 호출 한 번으로는 현재 오프셋을 바로잡고, 같은 방향으로 오프셋 정정이 반복해서 이뤄지면 작은 진동수 조정이 누적돼서 장기적인 왜곡을 수정하게 된다.이 플래그는
ADJ_OFFSET값으로 정정을 할 때 그 작은 진동수 조정이 이뤄지지 않게 한다. STA_PPSSIGNAL(읽기 전용)- 유효한 펄스 반복(PPS) 신호 있음.
STA_PPSJITTER(읽기 전용)- PPS 신호 지터 초과.
STA_PPSWANDER(읽기 전용)- PPS 신호 원더 초과.
STA_PPSERROR(읽기 전용)- PPS 신호 보정 오류.
STA_CLOCKERR(읽기 전용)- 클럭 하드웨어 오동작.
STA_NANO(읽기 전용, 리눅스 2.6.26부터)- 해상도 (0 = 마이크로초, 1 = 나노초).
ADJ_NANO를 통해 설정하고ADJ_MICRO를 통해 해제한다. STA_MODE(리눅스 2.6.26부터)- 모드 (0 = 위상 동기 루프, 1 = 진동수 동기 루프).
STA_CLK(읽기 전용, 리눅스 2.6.26부터)- 클럭 원천 (0 = A, 1 = B). 현재 쓰지 않음.
읽기 전용인 status 비트를 설정하려고 시도하면 조용히 무시된다.
clock_adjtime()
(리눅스 2.6.39에서 추가된) clock_adjtime() 시스템 호출은 adjtimex()처럼 동작하되, 동작을 수행할 클럭을 지정하는 clk_id 인자를 추가로 받는다.
ntp_adjtime()
(NTP "Kernel Application Program API", 즉 KAPI에 기술돼 있는) ntp_adjtime() 라이브러리 함수는 adjtimex()와 같은 일을 수행할 수 있는 더 이식성 좋은 인터페이스다. 다음 사항들을 제외하면 adjtimex()와 동일하다.
-
modes에 쓰는 상수들이 "ADJ_" 대신 "MOD_"로 시작하고 뒷부분이 같다. (즉MOD_OFFSET,MOD_FREQUENCY등이다.) 단 아래 항목들은 예외이다. -
ADJ_OFFSET_SINGLESHOT와 의미가 같은 항목은MOD_CLKA다. -
ADJ_TICK와 의미가 같은 항목은MOD_CLKB다. -
ADJ_OFFSET_SS_READ와 의미가 같은 항목은 없으며, KAPI에도 기술돼 있지 않다.
RETURN VALUE
성공 시 adjtimex() 및 ntp_adjtime()은 클럭 상태를 반환한다. 즉 다음 값들 중 하나를 반환한다.
TIME_OK- 클럭이 동기화돼 있고 대기 중인 윤초 조정이 없다.
TIME_INS- 그 UTC 일 끝에서 윤초가 추가될 것임을 나타낸다.
TIME_DEL- 그 UTC 일 끝에서 윤초가 삭제될 것임을 나타낸다.
TIME_OOP- 윤초 삽입이 진행 중이다.
TIME_WAIT- 윤초 삽입 내지 삭제가 완료됐다. 다음
ADJ_STATUS동작에서STA_INS및STA_DEL플래그를 해제할 때까지 이 값이 반환된다. TIME_ERROR-
시스템 클럭이 믿을 만한 서버에 동기화돼 있지 않다. 다음 중 하나라도 참일 때 이 값이 반환된다.
-
STA_UNSYNC나STA_CLOCKERR중 하나가 설정돼 있다. -
STA_PPSSIGNAL이 해제돼 있으면서STA_PPSFREQ나STA_PPSTIME이 설정돼 있다. -
STA_PPSTIME과STA_PPSJITTER가 모두 설정돼 있다. -
STA_PPSFREQ가 설정돼 있으면서STA_PPSWANDER나STA_PPSJITTER가 설정돼 있다.
심볼 이름
TIME_BAD는TIME_ERROR와 의미가 같으며 하위 호환성을 위해 제공된다. -
참고로 리눅스 3.4부터는 호출이 비동기적으로 동작하므로 일반적으로 반환 값이 그 호출 자체로 인한 상태 변화를 반영하지 않게 된다.
실패 시 이 호출들은 -1을 반환하고 오류를 나타내도록 errno를 설정한다.
ERRORS
EFAULTbuf가 쓰기 가능한 메모리를 가리키고 있지 않다.EINVAL(리눅스 2.6.26 전 커널)buf.freq를 범위 (-33554432, +33554432) 밖의 값으로 설정하려 했다.EINVAL(리눅스 2.6.26 전 커널)buf.offset을 허용 범위 밖의 값으로 설정하려 했다. 리눅스 2.0 전의 커널에서는 허용 범위가 (-131072, +131072)였다. 리눅스 2.0부터는 허용 범위가 (-512000, +512000)이다.EINVALbuf.status를 위에 나열된 것 외의 값으로 설정하려 했다.EINVALclock_adjtime()에 준clk_id가 유효하지 않다. 시스템 V 방식의 하드코딩된 양수 클럭 ID가 범위를 벗어났거나, 동적인clk_id가 유효한 클럭 객체 인스턴스를 가리키고 있지 않다. 동적 클럭에 대한 설명은 clock_gettime(2)을 보라.EINVALbuf.tick을900000/HZ에서1100000/HZ까지 범위 밖의 값으로 설정하려 했다. 여기서HZ는 시스템 타이머 인터럽트 빈도다.ENODEV- 동적
clk_id가 나타내는 (예컨대 USB 같은) 핫플러그 장치가 문자 장치가 열린 후에 사라졌다. 동적 클럭에 대한 설명은 clock_gettime(2)을 보라. EOPNOTSUPP- 지정한
clk_id가 조정을 지원하지 않는다. EPERMbuf.modes가 0이나ADJ_OFFSET_SS_READ가 아니며 호출자에게 충분한 특권이 없다. 리눅스에서는CAP_SYS_TIME역능이 필요하다.
ATTRIBUTES
이 절에서 사용하는 용어들에 대한 설명은 attributes(7)를 보라.
| 인터페이스 | 속성 | 값 |
|---|---|---|
ntp_adjtime() |
스레드 안전성 | MT-Safe |
CONFORMING TO
이 인터페이스들 중 어느 것도 POSIX.1에 기술돼 있지 않다.
adjtimex()와 clock_adjtime()은 리눅스 전용이므로 이식성이 있어야 하는 프로그램에서는 사용하지 말아야 한다.
NTP 데몬에게 적당한 API는 ntp_adjtime()이다.
NOTES
timex 구조체에서 freq, ppsfreq, stabil은 백만분율(ppm)이고 16비트는 소수부다. 즉 그 필드들에서 값 1은 2^-16 ppm을 뜻하고 2^16=65536이 1 ppm이다. 입력 값(freq)과 출력 값 모두 그렇다.
STA_INS 및 STA_DEL로 인한 윤초 처리는 커널 타이머 문맥에서 이뤄진다. 따라서 윤초를 추가 내지 삭제하려면 그 초에 한 틱만큼 더 걸리게 된다.
SEE ALSO
clock_gettime(2), clock_settime(2), 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)
2021-03-22