NAME
time - 시간 및 타이머 소개
DESCRIPTION
실제 시간과 프로세스 시간
실제 시간(real time)은 어떤 고정 시점을 기준으로 측정한 시간으로 정의한다. 그 시점이 과거의 어떤 표준 시점일 수도 있고 (아래 에포크 및 달력 시간 설명 참고), 프로세스 수명 중의 어느 (가령 시작) 시점부터 잰 것(경과 시간)일 수도 있다.
프로세스 시간(process time)은 프로세스가 쓴 CPU 시간의 양으로 정의한다. 때로는 그걸 사용자 부분과 시스템 부분으로 나누기도 한다. 사용자 CPU 시간이란 사용자 모드에서 코드를 실행하며 쓴 시간이다. 그리고 시스템 CPU 시간이란 프로세스를 대신해 시스템 모드에서 커널이 실행되며 (가령 시스템 호출을 실행하며) 쓴 시간이다. time(1)
명령을 쓰면 프로그램 실행 동안 소모한 CPU 시간 양을 알아낼 수 있다. 그리고 프로그램에서 times(2), getrusage(2), clock(3)을 써서 자기가 소모한 CPU 시간 양을 알아낼 수 있다.
하드웨어 클럭
대부분의 컴퓨터에는 (배터리가 달린) 하드웨어 클럭이 있다. 커널이 부팅 할 때 그 클럭을 읽어서 소프트웨어 클럭을 초기화 한다. 자세한 내용은 rtc(4)와 hwclock(8)을 보라.
소프트웨어 클럭, HZ, 지피
(select(2), sigtimedwait(2)처럼) 타임아웃을 설정하거나 (getrusage(2)처럼) CPU 시간을 측정하는 여러 시스템 호출의 정밀도 한계가 되는 게 소프트웨어 클럭 해상도인데, 커널에서 유지하는 그 클럭은 지피(jiffy) 단위로 시간을 측정한다. 그리고 지피의 크기를 정하는 게 커널 상수 HZ
의 값이다.
HZ
의 값은 커널 버전과 하드웨어 플랫폼에 따라 다양하다. i386의 상황을 보자면, 2.4.x까지 커널에서는 HZ가 100이어서 지피가 0.01초였고, 2.6.0부터 HZ가 1000으로 올라서 지피가 0.001초가 됐다. 그리고 커널 2.6.13부터는 HZ 값이 커널 구성 매개변수여서 100, 250 (기본값), 1000일 수 있고, 각각 지피 값 0.01초, 0.004초, 0.001초가 나온다. 커널 2.6.20부터는 추가로 300도 사용할 수 있는데, 일반적인 영상 프레임 속도(PAL 25 HZ, NTSC 30 HZ)로 나눠 떨어지는 값이다.
times(2) 시스템 호출은 특별한 경우다. 이 호출은 커널 상수 USER_HZ
에 의한 정밀도로 시간을 알려 준다. 사용자 공간 응용에서 sysconf(_SC_CLK_TCK)
로 그 상수의 값을 알아낼 수 있다.
시스템 클럭과 프로세스 클럭, 시간 네임스페이스
다양한 종류의 경과 시간과 가상 (CPU 소모) 시간을 측정하는 여러 클럭들을 커널에서 지원한다. clock_gettime(2)에서 그 클럭들을 설명한다. 그 클럭들 중 일부를 clock_settime(2)을 써서 설정할 수 있다. 어떤 클럭 값들은 시간 네임스페이스를 통해 가상화된다. time_namespaces(7)를 보라.
고해상도 타이머
리눅스 2.6.21 전에서 타이머 및 잠들기 시스템 호출(아래 참고)의 정밀도 한계도 지피 크기였다.
리눅스 2.6.21부터 리눅스에서 고해상도 타이머(HRT, high-resolution timer)를 지원하여 CONFIG_HIGH_RES_TIMERS
를 통해 선택적으로 구성 가능하다. HRT를 지원하는 시스템에서는 잠들기 및 타이머 시스템 호출의 정밀도가 더이상 지피에 의해 제약받지 않으며 하드웨어에서 허용하는 만큼 정밀할 수 있다. (요즘 하드웨어에선 마이크로초 정밀도가 일반적이다.) 고해상도 타이머가 지원되는지 알아내려면 clock_getres(2) 호출이 반환하는 해상도를 확인하거나 /proc/timer_list
에서 "resolution" 항목을 보면 된다.
모든 하드웨어 아키텍처에서 HRT를 지원하지는 않는다. (x86, arm, powerpc 등에서만 지원한다.)
에포크
유닉스 시스템들은 에포크(Epoch), 즉 1970-01-01 00:00:00 +0000 (UTC) 이후 지난 초 수로 시간을 나타낸다.
프로그램에서 clock_gettime(2)의 CLOCK_REALTIME
클럭을 통해 달력 시간(calendar time)을 알아낼 수 있는데, 에포크 이후 지난 시간이 (초와 나노초로) 반환된다. time(2)도 비슷한 정보를 제공하되 정밀도가 초 단위이다. clock_settime(2)으로 시스템 시간을 바꿀 수 있다.
분할 시간
어떤 라이브러리 함수들은 tm
타입 구조체를 써서 분할 시간(broken-down time)을 나타내는데, 시간 값을 별개 요소들(년, 월, 일, 시간, 분, 초 등)로 나눠서 저장하는 것이다. ctime(3)에서 그 구조체를 설명하며 또 달력 시간과 분할 시간을 서로 변환하는 함수들도 설명한다. ctime(3), strftime(3), strptime(3)에서는 분할 시간과 출력 가능한 문자열 시간 표현을 서로 변환하는 함수들을 설명한다.
잠들기와 타이머 설정하기
여러 시스템 호출과 함수를 통해 프로그램이 일정 시간 동안 잠들 수 (실행을 중지할 수) 있다. nanosleep(2), clock_nanosleep(2), sleep(3) 참고.
여러 시스템 호출을 통해 프로세스에서 타이머를 설정할 수 있다. 그 타이머는 미래 어느 시점에 만료되며 일정 주기로 만료되기도 한다. alarm(2), getitimer(2), timerfd_create(2), timer_create(2) 참고.
타이머 게으름
리눅스 2.6.28부터 스레드별로 "타이머 게으름(slack)" 값을 제어하는 게 가능하다. 타이머 게으름 값이란 타임아웃과 함께 블록 돼 있는 특정 시스템 호출들이 깨어나는 걸 커널에서 지연시킬 수 있는 시간 길이이다. 이런 지연을 허용하면 커널에서 여러 깨우기 이벤트를 병합할 수 있고, 그래서 잘하면 시스템이 깨어나는 횟수를 줄여서 전력을 아낄 수도 있다. 자세한 내용은 prctl(2)의 PR_SET_TIMERSLACK
설명을 보라.
SEE ALSO
date(1)
, time(1)
, timeout(1)
, adjtimex(2), alarm(2), clock_gettime(2), clock_nanosleep(2), getitimer(2), getrlimit(2), getrusage(2), gettimeofday(2), nanosleep(2), stat(2), time(2), timer_create(2), timerfd_create(2), times(2), utime(2), adjtime(3), clock(3), clock_getcpuclockid(3), ctime(3), ntp_adjtime(3), ntp_gettime(3), pthread_getcpuclockid(3), sleep(3), strftime(3), strptime(3), timeradd(3), usleep(3), rtc(4), time_namespaces(7), hwclock(8)
2020-04-11