사전 CLOCK_REALTIME
개념

CLOCK_REALTIME

gabury1고친 사람 github-actions[bot]

CLOCK_REALTIME 은 프로그램이 운영체제에게 지금 몇 시인지 물을 때 고르는 시계의 이름입니다. 이 시계는 달력과 벽시계가 가리키는 시각을 알려 줍니다. 로그에 찍히는 시각도 대개 이 시계에서 옵니다. 대신 이 시계는 누가 시각을 고칠 때마다 앞뒤로 뜁니다.

쉽고 빠른 이해

무슨 일을 하는 물건인가 — 운영체제가 들고 있는 여러 시계 가운데 「지금 날짜와 시각」을 알려 주는 시계를 고르는 이름입니다. 로그 한 줄 앞에 붙는 시각이 이 시계를 읽은 값입니다.

왜 이렇게 하나 — 사람과 컴퓨터, 컴퓨터와 컴퓨터가 같은 시각을 말하려면 모두가 합의한 기준 시각이 있어야 합니다. 기기가 켜진 뒤 흐른 시간만으로는 「몇 시」를 말할 수 없습니다.

어떻게 도나

  1. 프로그램이 이 이름을 넘겨 운영체제의 시계를 읽습니다
  2. 1970년 첫날 0시부터 흐른 초와 그 아래 나노초가 돌아옵니다
  3. 운영체제는 인터넷의 시각 서버에 맞춰 이 시계를 계속 고칩니다

대가 — 고치는 순간 값이 뜁니다. 두 번 읽어 빼면 걸린 시간이 음수로 나오기도 합니다. 걸린 시간은 앞으로만 가는 다른 시계로 잽니다.

상세

사무실 벽에 걸린 시계를 떠올려 보세요. 모두가 그 시계를 보고 회의 시각을 맞춥니다. 시계가 늦으면 누군가 의자에 올라가 바늘을 돌려 맞춥니다. 바늘을 돌린 순간 시계는 몇 분을 건너뛰거나 되돌아갑니다.

CLOCK_REALTIME 은 운영체제 안에 걸린 이 벽시계입니다. 이 절은 먼저 이 이름이 무엇을 고르는지, 왜 이런 시계가 따로 있어야 하는지를 봅니다. 이어서 읽는 코드와 돌아오는 값, 값이 뛰는 까닭을 봅니다. 끝으로 이 시계를 쓰면 안 되는 일과 백엔드 언어에서 이 시계를 읽는 함수를 봅니다.

운영체제가 내주는 시계를 고르는 이름

응용 프로그램은 시각을 스스로 세지 않습니다. 운영체제의 중심부인 커널에 물어봅니다. 커널은 쓰임이 다른 시계를 여러 개 들고 있습니다.

어느 시계를 읽을지는 이름으로 고릅니다. POSIX(Portable Operating System Interface, 이식 가능한 운영체제 인터페이스)는 리눅스와 macOS 같은 유닉스 계열 운영체제가 함께 지키는 규격입니다. 이 규격이 시계마다 붙여 둔 이름 가운데 하나가 CLOCK_REALTIME 입니다.

POSIX 를 따르는 시스템이라면 이 시계는 반드시 있습니다. 다른 시계는 있을 수도 없을 수도 있습니다. 어느 시스템에서나 기댈 수 있는 기본 시계라는 뜻입니다.

이름 속 realtime 의 뜻

realtime 은 여기서 「실제 세계의 시각」이라는 뜻입니다. 사람이 달력과 손목시계로 쓰는 그 시각입니다. 영어권에서는 이 시각을 흔히 wall-clock time이라고 부릅니다.

정해진 시간 안에 반드시 응답해야 하는 실시간 운영체제의 「실시간」과는 관계가 없습니다. CLOCK_REALTIME 을 읽는다고 프로그램이 더 빨리 돌거나 마감을 지켜 주지 않습니다.

이 시계가 따로 있어야 하는 까닭

로그에 찍는 시각과 쿠폰의 만료 시각은 기기 밖으로 나갑니다. 다른 서버가 읽습니다. 사람도 읽습니다. 그러려면 모든 기기가 같은 기준으로 센 시각이어야 합니다.

기기가 켜진 뒤 흐른 시간으로는 이 일을 못 합니다. 서버마다 켜진 때가 달라서 같은 순간에도 값이 다릅니다. 「지금 몇 시」를 말하려면 온 세계가 합의한 시각을 따르는 시계가 하나 있어야 합니다. 그 시계가 CLOCK_REALTIME 입니다.

읽는 방법과 돌아오는 값

코드에서 CLOCK_REALTIME 은 상수 하나입니다. C 프로그램이 불러 쓰는 헤더 파일 time.h 에 들어 있습니다. 이 상수를 넘겨서 시계를 고릅니다.

이 시계는 clock_gettime 이라는 시스템 콜로 읽습니다. 시스템 콜은 프로그램이 커널에 일을 부탁하는 호출입니다. 첫 인자로 시계 이름을 넘깁니다. 둘째 인자로 값을 받을 구조체를 넘깁니다.

값은 struct timespec 이라는 구조체에 담겨 돌아옵니다. 필드가 둘입니다. tv_sec 에 초가, tv_nsec 에 1초보다 작은 부분이 나노초로 들어갑니다. 나노초는 10억분의 1초입니다.

아래는 어느 한 순간에 이 시계를 읽었을 때 두 필드에 든 값의 예입니다.

C
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec;    // 1000000000
ts.tv_nsec;   // 250000000

초 필드는 에포크부터 흐른 초의 개수입니다. 에포크는 1970년 1월 1일 0시 0분 0초입니다. 이 시각의 기준은 UTC(Coordinated Universal Time, 협정 세계시)입니다. 협정 세계시는 세계가 시각을 맞출 때 기준으로 삼는 시각입니다.

초 필드 1000000000 은 협정 세계시로 2001년 9월 9일 01:46:40 입니다. 나노초 필드 250000000 은 0.25초이니 그 순간에서 4분의 1초가 더 지난 때입니다.

C 의 time() 과 gettimeofday 도 같은 시각을 돌려줍니다. 앞의 것은 초까지, 뒤의 것은 마이크로초까지 담습니다. 마이크로초는 100만분의 1초입니다. 나노초까지 필요하면 clock_gettime 을 씁니다.

값이 앞뒤로 뛰는 까닭

이 소절은 CLOCK_REALTIME 의 값이 왜 고쳐지는지, 고칠 때 값이 어떻게 변하는지를 봅니다.

컴퓨터의 시계는 수정 진동자의 떨림을 세서 갑니다. 수정 진동자는 전기를 흘리면 거의 일정한 빠르기로 떨리는 작은 부품입니다. 떨림을 정해진 수만큼 세면 1초가 지난 것으로 칩니다.

그런데 이 떨림의 빠르기가 기기마다 조금씩 다릅니다. 가만두면 하루에 몇 초까지 어긋나기도 합니다. 이렇게 시계가 조금씩 벌어지는 현상을 클록 드리프트라고 합니다.

그래서 운영체제는 CLOCK_REALTIME 을 계속 고칩니다. 맞추는 기준은 인터넷의 시각 서버입니다. NTP(Network Time Protocol, 네트워크 시간 프로토콜)는 이 서버와 시각을 맞추는 규약입니다.

이 규약으로 시각 서버에 늘 물어보는 프로그램을 NTP 데몬이라고 부릅니다. 데몬은 뒤에서 멈추지 않고 도는 프로그램입니다.

NTP 데몬은 이 시계를 시각 서버와 견줘 차이를 잽니다. 그리고 그 차이만큼 시계를 고쳐 달라고 커널에 부탁합니다. 값을 실제로 바꾸는 쪽은 커널입니다.

고치는 방법은 둘입니다. 시계가 얼마나 벌어졌느냐에 따라 고릅니다.

고치는 방법 언제 쓰나 값이 어떻게 변하나
조금씩 빠르게·느리게 돌리기 차이가 작을 때 NTP 데몬이 뒤로 가지는 않고 1초가 조금 길거나 짧게 흐른다
한 번에 옮기기 차이가 클 때 NTP 데몬이, 또는 관리자가 손으로 몇 초든 한순간에 건너뛰거나 되돌아간다

위 줄은 값이 매끄럽게 이어집니다. 아래 줄은 값이 끊깁니다. 어느 쪽이든 시각을 고치려면 관리자 권한이 있어야 합니다.

아래 그림은 1초마다 이 시계를 읽는 동안 3초째에 누군가 시각을 5초 되돌린 경우입니다.

xychart-beta
    title "1초마다 읽은 CLOCK_REALTIME 초 값"
    x-axis "실제로 흐른 초" [0, 1, 2, 3, 4, 5]
    y-axis "읽은 초 값" 995 --> 1005
    bar [1000, 1001, 1002, 998, 999, 1000]

세계는 5초 흘렀습니다. 그런데 읽은 값은 1000 에서 시작해 1000 으로 끝났습니다. 3초째에 1003 이 될 값이 998 로 떨어졌기 때문입니다.

윤초도 값을 흔듭니다. 윤초는 지구 자전에 시각을 맞추려고 가끔 끼워 넣는 1초입니다. CLOCK_REALTIME 은 하루를 언제나 86400초로 치고 윤초를 세지 않습니다.

윤초가 끼어든 날에는 실제 세계에 1초가 더 흐릅니다. 이 시계는 그 1초를 세지 않으니 그대로 두면 실제 시각보다 1초 앞서게 됩니다.

그래서 윤초가 들어가는 날에는 이 시계를 1초 뒤로 맞춥니다. 1초를 한 번에 되돌리기도 합니다. 몇 시간에 걸쳐 조금씩 늦추기도 합니다. 앞의 방법이면 같은 초 값이 두 번 나옵니다.

걸린 시간을 재면 안 되는 까닭

앞 그림의 0초와 5초를 빼면 0초가 걸린 것으로 나옵니다. 2초와 3초를 빼면 -4초가 나옵니다. 요청 하나가 몇 초 걸렸는지 이 시계로 재면 이런 값이 로그에 남습니다.

걸린 시간은 단조 시계로 잽니다. 단조 시계는 누가 시각을 고쳐도 뒤로 가지 않는 시계입니다. POSIX 에서는 그 이름이 CLOCK_MONOTONIC 입니다. 같은 clock_gettime 에 이 이름을 넘기면 됩니다.

고르는 기준은 한 줄입니다. 두 시점 사이의 거리를 재면 단조 시계입니다. 사람이나 다른 기기에 보여 줄 시각이면 CLOCK_REALTIME 입니다.

하려는 일 고를 시계
로그 한 줄에 타임스탬프를 찍는다 CLOCK_REALTIME
쿠폰 만료 시각을 데이터베이스에 넣는다 CLOCK_REALTIME
함수 하나가 몇 초 걸렸는지 잰다 CLOCK_MONOTONIC
타임아웃까지 얼마나 남았는지 센다 CLOCK_MONOTONIC

위 두 줄은 값이 기기 밖으로 나가거나 오래 남는 일입니다. 아래 두 줄은 한 기기 안에서 두 시점을 빼는 일입니다.

이 시계로 기다릴 때

프로그램은 시계를 읽기만 하지 않고 시계에 맞춰 기다리기도 합니다. 기다리는 방식은 둘입니다. 「지금부터 5초」처럼 길이로 기다리는 방식과 「오전 9시까지」처럼 시각으로 기다리는 방식입니다.

길이로 기다리면 CLOCK_REALTIME 이 고쳐져도 영향을 받지 않습니다. 5초를 기다리기로 했으면 누가 시각을 되돌려도 5초 뒤에 깹니다.

시각으로 기다리면 고친 시각을 따라갑니다. 시계가 오전 9시를 넘어가게 앞으로 옮겨지면 바로 깹니다. 뒤로 옮겨지면 그만큼 더 기다립니다. 「매일 9시에 보고서를 보낸다」처럼 달력 시각에 묶인 일에는 이쪽이 맞습니다.

C 에서는 clock_nanosleep 에 시계 이름과 함께 어느 방식으로 기다릴지를 넘깁니다.

타임아웃은 이 둘 사이에서 자주 탈이 납니다. 「지금부터 5초」를 「지금 시각 + 5초」라는 마감 시각으로 바꿔 넘기는 코드가 많습니다. 이 마감을 CLOCK_REALTIME 으로 세면 누가 시각을 1시간 되돌릴 때 1시간 5초를 기다립니다.

그래서 마감 시각으로 기다리는 타임아웃은 CLOCK_MONOTONIC 으로 셉니다. 단조 시계의 지금 값에 5초를 더한 값을 마감으로 삼습니다. 단조 시계는 누가 시각을 고쳐도 뒤로 가지 않으니 5초 뒤에 깹니다.

백엔드 언어에서 이 시계를 읽는 함수

백엔드 코드는 clock_gettime 을 직접 부를 일이 드뭅니다. 언어마다 벽시계를 읽는 함수와 단조 시계를 읽는 함수를 따로 둡니다. 리눅스에서 벽시계 쪽 함수들이 읽는 시계가 CLOCK_REALTIME 입니다.

언어 벽시계를 읽는 함수 단조 시계를 읽는 함수
자바 System.currentTimeMillis() · Instant.now() System.nanoTime()
파이썬 time.time() time.monotonic()
자바스크립트 Date.now() performance.now()

표의 가운데 열 값으로 두 시점을 빼면 앞 소절과 같은 문제가 납니다. 걸린 시간을 재는 코드라면 오른쪽 열 함수를 씁니다.

관련 항목

CLOCK_REALTIME 과 맞세워지는 시계

CLOCK_MONOTONIC · CLOCK_BOOTTIME · CLOCK_TAI · CLOCK_REALTIME_COARSE · CLOCK_PROCESS_CPUTIME_ID · 단조 시계

CLOCK_REALTIME 을 읽고 고치는 호출

clock_gettime · clock_settime · clock_getres · clock_nanosleep · gettimeofday · settimeofday · time(2) · adjtimex · vDSO

CLOCK_REALTIME 이 돌려주는 값의 단위와 타입

struct timespec · struct timeval · time_t · 나노초 · 유닉스 시간

CLOCK_REALTIME 이 세는 기준 시각

에포크 · UTC · 윤초 · TAI · 시간대 · GPS 시각

CLOCK_REALTIME 값을 흔드는 원인과 보정

NTP · 클록 드리프트 · 클록 스큐 · 클럭 스텝 · 윤초 문지르기 · 수정 진동자 · chrony · ntpd

CLOCK_REALTIME 을 정하고 내주는 규격과 소프트웨어

POSIX · time.h · 운영체제 · 커널 · 시스템 콜 · 리눅스 · 유닉스

CLOCK_REALTIME 을 읽는 언어별 함수

System.currentTimeMillis · java.time.Instant · System.nanoTime · time.time · time.monotonic · Date.now · performance.now

CLOCK_REALTIME 이 속하는 상위 분류

벽시계 · 시간과 시계 · 시스템 클록 · POSIX 시계

CLOCK_REALTIME 과 이름이 겹치는 개념

실시간 운영체제 · 실시간 시스템 · 실시간 시계 · 실시간 스케줄링

CLOCK_REALTIME 을 잘못 써서 터지는 문제

타임아웃 · 타임스탬프 · 2038 문제 · 로그 · 분산 시스템 · 논리 시계 · pthread_cond_timedwait

다른 이름: clock_realtime · REALTIME 시계