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_OFFSET
buf.offset
으로 시간 오프셋 설정. 리눅스 2.6.26부터는 제공된 값을 (-0.5s, +0.5s) 범위로 잘라낸다. 이전 커널에서는 제공된 값이 범위를 벗어나면EINVAL
오류가 발생한다.ADJ_FREQUENCY
buf.freq
로 진동수 오프셋 설정. 리눅스 2.6.26부터는 제공된 값을 (-32768000, +32768000) 범위로 잘라낸다. 이전 커널에서는 제공된 값이 범위를 벗어나면EINVAL
오류가 발생한다.ADJ_MAXERROR
buf.maxerror
로 최대 시간 오차 설정.ADJ_ESTERROR
buf.esterror
로 추정 시간 오차 설정.ADJ_STATUS
buf.status
로 클럭 상태 비트 설정. 이 비트들에 대해선 아래에서 설명한다.ADJ_TIMECONST
buf.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_TICK
buf.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
EFAULT
buf
가 쓰기 가능한 메모리를 가리키고 있지 않다.EINVAL
(리눅스 2.6.26 전 커널)buf.freq
를 범위 (-33554432, +33554432) 밖의 값으로 설정하려 했다.EINVAL
(리눅스 2.6.26 전 커널)buf.offset
을 허용 범위 밖의 값으로 설정하려 했다. 리눅스 2.0 전의 커널에서는 허용 범위가 (-131072, +131072)였다. 리눅스 2.0부터는 허용 범위가 (-512000, +512000)이다.EINVAL
buf.status
를 위에 나열된 것 외의 값으로 설정하려 했다.EINVAL
clock_adjtime()
에 준clk_id
가 유효하지 않다. 시스템 V 방식의 하드코딩된 양수 클럭 ID가 범위를 벗어났거나, 동적인clk_id
가 유효한 클럭 객체 인스턴스를 가리키고 있지 않다. 동적 클럭에 대한 설명은 clock_gettime(2)을 보라.EINVAL
buf.tick
을900000/HZ
에서1100000/HZ
까지 범위 밖의 값으로 설정하려 했다. 여기서HZ
는 시스템 타이머 인터럽트 빈도다.ENODEV
- 동적
clk_id
가 나타내는 (예컨대 USB 같은) 핫플러그 장치가 문자 장치가 열린 후에 사라졌다. 동적 클럭에 대한 설명은 clock_gettime(2)을 보라. EOPNOTSUPP
- 지정한
clk_id
가 조정을 지원하지 않는다. EPERM
buf.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