본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Computer Science

운영체제의 시간과 타이머

작성 2026. 10. 14.추가
목차9개 항목
출처와 참고

팔만코딩경(42Seoul 커뮤니티 위키)의 「[운영체제] Time의 종류와 Alarm」을 바탕으로 재구성한 노트다.

세 가지 시간

"지금 몇 시인가"와 "얼마나 걸렸나"는 다른 질문이다. 운영체제는 목적에 따라 시간을 세 가지로 나눠 제공한다.

시간뜻바뀔 수 있나쓰는 곳
벽시계 시간(wall time)달력상의 현재 시각. 1970-01-01 00:00 UTC부터 흐른 초(Unix time)바뀐다. 사용자·NTP가 앞뒤로 조정로그 시각, 사용자에게 보여 줄 시간, 만료 일시
단조 시간(monotonic time)부팅 같은 임의 기준점부터 계속 증가만 하는 시간뒤로 가지 않는다경과 시간, 타임아웃, 성능 측정
프로세스 시간(process time)프로세스가 실제로 CPU를 쓴 시간기다리거나 잠든 동안은 흐르지 않는다프로파일링, CPU 사용량

경과 시간을 벽시계로 재면 안 된다. 측정 도중 NTP가 시계를 1초 되돌리면 경과 시간이 음수가 된다. 타임아웃·재시도 간격·벤치마크는 단조 시계로 잰다. 시간대와 달력 계산의 함정은 달력과 시간에 대한 개발자의 착각에 있다.

시간을 재는 하드웨어

장치특징
RTC (Real Time Clock)메인보드의 배터리로 전원이 꺼져도 돈다. 정밀도가 낮아 부팅 때 벽시계 초기값을 읽는 데 쓴다
HPET (High Precision Event Timer)주기적 인터럽트를 낼 수 있는 정밀 타이머. 전원이 꺼지면 멈춘다
TSC (Time Stamp Counter)CPU 사이클마다 증가하는 카운터. 가장 정밀하고 읽기도 빠르지만 인터럽트는 내지 않는다. 최신 CPU는 주파수가 바뀌어도 일정한 속도로 증가(invariant TSC)한다

커널은 이 장치들로 시스템 클록을 유지한다. 전통적으로 커널은 일정 주기(tick)마다 타이머 인터럽트를 받아 시간을 진행시키고 스케줄링을 했다. 이 주기를 HZ라 하며 배포판 설정에 따라 보통 100·250·1000이다(250이면 4ms). 현대 Linux는 할 일이 없을 때 tick을 멈추고(tickless, NO_HZ) 고해상도 타이머(hrtimer)로 나노초 단위 만료를 처리한다.

시계 API

API출처해상도비고
time()C 표준초벽시계
gettimeofday(&tv, NULL)BSD마이크로초 (timeval)벽시계. POSIX에서 구식으로 지정됨
clock_gettime(id, &ts)POSIX나노초 (timespec)권장. 시계 종류를 고른다
clock_getres(id, &ts)POSIX그 시계의 해상도
clock_settime(id, &ts)POSIX루트 권한 필요. 단조 시계는 바꿀 수 없다
clockid_t의미
CLOCK_REALTIME벽시계
CLOCK_MONOTONIC단조 시간. 시스템이 잠든(suspend) 동안은 멈춘다
CLOCK_BOOTTIME (Linux)단조 시간 + 잠든 시간 포함
CLOCK_PROCESS_CPUTIME_ID프로세스(모든 스레드 합계)의 CPU 시간
CLOCK_THREAD_CPUTIME_ID호출한 스레드의 CPU 시간
struct timespec a, b;
clock_gettime(CLOCK_MONOTONIC, &a);
work();
clock_gettime(CLOCK_MONOTONIC, &b);
double ms = (b.tv_sec - a.tv_sec) * 1e3 + (b.tv_nsec - a.tv_nsec) / 1e6;

다른 언어의 대응: JavaScript performance.now(), Python time.monotonic(), Java System.nanoTime()이 단조 시계이고, Date.now()·time.time()·System.currentTimeMillis()는 벽시계다.

잠들기

함수단위중간에 깨면
sleep(sec)초남은 초를 반환
usleep(usec)마이크로초POSIX에서 제거됨. 새 코드는 nanosleep
nanosleep(&req, &rem)나노초-1과 EINTR, 남은 시간을 rem에
clock_nanosleep(id, flags, &req, &rem)나노초시계를 고를 수 있고, TIMER_ABSTIME으로 절대 시각까지 잠든다
  • 잠든 프로세스는 CPU를 받지 않는다. 시그널이 오면 일찍 깬다.
  • 요청한 시간은 최소 시간이다. 스케줄링 때문에 더 늦게 깰 수 있다.
  • 주기 작업을 sleep(간격)으로 반복하면 작업 시간만큼 오차가 누적된다. 다음 깰 절대 시각을 계산해 clock_nanosleep(..., TIMER_ABSTIME, ...)을 쓰면 밀리지 않는다.
  • CPU 시간 시계로는 clock_nanosleep을 쓸 수 없다. 잠든 동안 그 시계가 흐르지 않기 때문이다.

알람과 타이머

alarm

alarm(sec)은 sec초 뒤 자기 자신에게 SIGALRM을 예약하고 바로 반환한다. 잠들지 않는다. 새로 부르면 이전 알람은 취소되고, 0을 주면 알람을 끈다. 시그널 처리는 UNIX 시그널을 본다.

인터벌 타이머 (setitimer)

모드기준 시간만료 시그널
ITIMER_REAL벽시계 경과SIGALRM
ITIMER_VIRTUAL사용자 모드 CPU 시간SIGVTALRM
ITIMER_PROF사용자 + 커널 모드 CPU 시간SIGPROF (프로파일러가 사용)

it_value는 첫 만료까지, it_interval은 이후 반복 간격이다. 간격이 0이면 한 번만 울린다. 프로세스당 모드별 하나뿐이고 오버런(처리하기 전에 다시 만료)을 알려 주지 않는다.

POSIX 타이머

timer_create(clockid, &sigevent, &timerid) → timer_settime → timer_delete. 시계를 고를 수 있고 여러 개 만들 수 있으며, timer_getoverrun으로 놓친 만료 횟수를 알 수 있다. 만료 알림 방식은 고른다.

sigev_notify만료 시
SIGEV_SIGNAL지정한 시그널 전송 (기본은 SIGALRM)
SIGEV_THREAD지정한 함수를 새 스레드에서 실행
SIGEV_THREAD_ID (Linux)특정 스레드에 시그널
SIGEV_NONE알리지 않음. timer_gettime으로 직접 확인

Linux에는 만료를 fd 읽기로 받는 timerfd_create도 있다. 시그널 대신 epoll로 다른 I/O와 함께 기다릴 수 있어 이벤트 루프에 잘 맞는다.

정리

  • 시각은 벽시계, 경과 시간은 단조 시계, CPU 사용량은 프로세스 시계로 잰다.
  • 새 코드는 clock_gettime·clock_nanosleep을 쓴다. gettimeofday·usleep은 구식이다.
  • sleep은 최소 시간만 보장한다. 주기 작업은 절대 시각으로 잠든다.
  • 타이머는 alarm → setitimer → POSIX 타이머·timerfd 순으로 기능이 많아진다.
  • "얼마나 걸렸나"를 Date.now() 차이로 재는 코드는 시계 조정 한 번에 틀린다.

관련 노트: UNIX 시그널, 컴퓨터 시스템 구조와 인터럽트, 달력과 시간에 대한 개발자의 착각

원본 기록3

댓글

GitHub 계정으로 댓글을 남길 수 있어요.