Linux Programmer

gettimeofday를 대체하는 clock_gettime 함수 본문

컴퓨터 관련/프로그래밍 일반

gettimeofday를 대체하는 clock_gettime 함수

sunyzero 2012. 9. 25. 00:19

gettimeofday는 유닉스, 리눅스 시스템 프로그래밍에서 시간을 구할 때 쓰던 함수이다. 마이크로초(백만분의 1초)단위로 구할 수 있는 기능을 가지고 있어서 많이 사용되는 함수이다.


그러나 앞으로는 gettimeofday는 사용하지 말아야할 함수가 되었다. 왜냐하면 gettimeofday는 2008년도 유닉스 표준인 SUSv4-2008에서 앞으로 제거될 구식(obsolescent) 함수로 지정했기 때문이다. 


물론 하위 호환성을 위해서 gettimeofday 함수는 계속 제공될 것이다. 하지만 기본 시스템 라이브러리에서는 제거되고, 구식 라이브러리를 따로 포함해야 사용할 수 있게 될지도 모르기 때문에 차후에는 gettimeofday는 쓰지 않는 편이 좋다. 참고로 지금까지의 표준안의 행보로 봤을 때 obsolescent 함수로 지정되면 약 10여년의 유예를 거치는 것 같다. 따라서 지금 당장부터 쓰지 말아야 하는 것은 아니다. 물론 기능적인 문제가 생길 수 있는 곳에서는 당장 교체해야 한다. (gettimeofday의 문제점은 아래 monotonic clock의 필요성을 보면 되겠다.)


대신에 새로운 함수인 clock_gettime을 사용하면 된다.


gettimeofday는 1988년도에 SVR4에서 표준에 지정되어 20여년을 넘게 사용되던 함수이다. 아직도 수많은 프로그래밍 서적이나 소스 코드의 예제에서 사용되고 있다. 실제로 최근에 개발되던 몇몇 실무 코드 조차 gettimeofday를 사용하고 있는 실정이다.


하지만 가까운 장래에는 퇴출될 함수이므로 지양하는 편이 좋다. 본인도 가끔 졸린 상태에서 기계적으로 코딩하다보면 gettimeofday를 쓰곤 하는데 지양하도록 주의하는 중이다.

 

그러면 새로운 함수인 clock_gettime의 프로토 타입부터 살펴보자. 비교를 위해 과거 gettimeofday 도 같이 적어두었다.

#include <time.h>
int clock_gettime(clockid_t clock_id, struct timespec *tp);
    struct timespec {
            time_t   tv_sec;        /* seconds */
            long     tv_nsec;       /* nanoseconds */
    };
--------------------------------------------------------------
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
    struct timeval {
            time_t      tv_sec;     /* seconds */
            suseconds_t tv_usec;    /* microseconds */
    };


먼저 clock_gettime은 시간 관련 구조체가 struct timespec으로 바뀌었는데 나노초까지 지정할 수 있다. 물론 모든 시스템이 나노초까지 정밀한 시계를 제공하는 것은 아니다. 각 시스템 시계의 정밀도는 clock_getres로 확인할 수 있다.


또한 clock_gettime은 멀티코어 CPU 시대에 걸맞게 쓰레드 안전(thread safety)을 만족하고 있다.


인수로 쓰이는 clockid_t 타입은 시계의 종류를 나타내는 ID로서 POSIX 시스템은 기본적으로 2가지 타입(CLOCK_REALTIME, CLOCK_MONOTONIC)의 시계를 제공한다. 그리고 추가적으로 몇 개의 시계를 더 제공할 수 있다. 아래 표를 보면 몇몇 시계 ID를 정리해두었다. 그러나 실제 시스템에서는 이보다 더 많은 비표준 시계 ID를 제공하기도 한다.


 CLOCK_REALTIME 

 시스템 전역의 실제 시계(The UNIX Epoch 시간)

 CLOCK_MONOTONIC  단조 시계 (특정 시점에서 흐른 시간, 대체적으로 부팅후 시간)
 CLOCK_PROCESS_CPUTIME_ID  프로세스 단위 CPU 사용 시간
 CLOCK_THREAD_CPUTIME_ID  쓰레드 단위 CPU 사용 시간


여기서 CLOCK_REALTIME을 사용하면 실제 시간을 구하므로 gettimeofday의 기능과 같은 기능을 제공한다. 


CLOCK_MONOTONIC은 단조시계이므로 NTP에 의해서 시스템 시간이 바뀌더라도 영향을 받지 않는다. 따라서 (t1 - t0) 처럼 두 이벤트의 시간차이를 알고자 할 때 유용하다. 과거 gettimeofday의 경우는 NTP나 관련 기능에 의해서 시스템 시계가 보정될 때 오차가 발생하는 문제가 생길 수 있다. 따라서 최근에는 사용을 지양하도록 하는 기능이다. (이는 logging 관련 부분에서 diff. time의 정밀도가 중요할 때에도 나쁜 영향을 미칠 수 있다.)


이제 현재 시각을 버퍼에 저장하는 간단한 예제 코드를 살펴보자. 

main 함수는 생략했으니 적당히 붙여서 코딩하면 될 것이다. 화면에 출력되는 것을 보고 싶다면 printf로 buf 문자열을 출력하면 된다. 다시 말하지만 두 시간의 차(difference)를 구할 경우에는 CLOCK_MONOTONIC을 사용하도록 코딩해야 한다.


#define STR_TIME_FORMAT      "%y-%m-%d/%H:%M:%S"
char  buf[32];
size_t sz_buf = sizeof(buf);
struct timespec tspec;
struct tm   tm_now;

/* 리얼타임 시계로부터 현재 시간을 구한다. */
if (clock_gettime(CLOCK_REALTIME, &tspec) == -1) {  
    /* 에러 처리*/
}

/* timespec 구조체의 초수 필드(tv_sec)를 tm 구조체로 변환 */
localtime_r((time_t *)&tspec.tv_sec, &tm_now); 

/* 문자열로 저장 */
if (strftime(buf, sz_buf, STR_TIME_FORMAT, &tm_now) == 0) { 
    /* 에러 처리*/
}


참고로 gettimeofday 외에 usleep, getitimer, setitimer도 구식(obsolescent) 함수로 지정되어 나중에는 사라질 함수이므로 되도록이면 쓰지 않는 편이 좋다.


반응형
Comments