리눅스 타이머 프로그래밍

반응형

리눅스에서 타이머 파일 디스크립터를 이용하여 쓰레드 기반의 타이머 이벤트 처리 기능을 구현할 수 있다.

 

타이머에 대한 파일 디스크립터를 생성한 후, 해당 파일 디스크립터를 읽거나 폴링함으로써 타이머 이벤트를 획득하여 처리할 수 있다.

 

사용되는 함수는 timerfd_create(), timerfd_settime(), read()이다.

 

대략적인 절차는 다음과 같다.

 

1. timerfd_create() 함수를 호출하여 타이머 파일 디스크립터를 생성한다.

 

2. timerfd_settime() 함수를 호출하여 타이머 만기 주기를 설정한다.

 

3. 루프를 돌면서 read() 함수를 호출하여 타이머 이벤트를 처리한다. read() 함수 호출 시 블록되며, 타이머 만기 시점이 되면 리턴된다.

 

타이머를 설정할 때, 최초 타이머 주기와 두번째부터의 타이머 주기를 다르게 설정할 수 있다.

이는 각각 struct itimerspec 구조체의 it_value와 it_interval 변수에 설정된다.

 

함수 사용의 예제는 다음과 같다. 예제에서는 밀리초 단위의 타이머를 사용했지만, itimerspec 구조체가 나노초 단위까지 설정할 수 있으므로 나노초 단위의 타이머로도 사용할 수 있다.

// timer.c

#include <sys/timerfd.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>

#define INITIAL_DELAY (1000) ///< 최초 타이머 만기 지연 (밀리초 단위)
#define INTERVAL (100) ///< 타이머 만기 주기 (밀리초 단위)

pthread_t g_thread; ///< 타이머 이벤트 처리 쓰레드


/**
 * @brief 타이머 이벤트 처리 쓰레드
 * @param arg 사용되지 않음
 * @return NULL
 */
static void * timer_thread(void *arg)
{
  (void)arg;

  /*
   * 밀리초 단위 현재시각을 구한다.
   */
  struct timespec current_ts;
  clock_gettime(CLOCK_MONOTONIC, &current_ts);
  int current_msec = (current_ts.tv_sec * 1000) + (current_ts.tv_nsec / 1000000);

  struct itimerspec ts;

  /*
   * 최초 타이머 만기 시점을 설정한다. 만기시점 = 현재시각 + 지연
   */
  int first_timer_exp_msec = current_msec + INITIAL_DELAY;
  ts.it_value.tv_sec = first_timer_exp_msec / 1000;
  ts.it_value.tv_nsec = (first_timer_exp_msec % 1000) * 1000000;

  /*
   * 타이머 주기를 설정한다.
   */
  ts.it_interval.tv_sec = INTERVAL / 1000;
  ts.it_interval.tv_nsec = (INTERVAL % 1000) * 1000000;

  /*
   * 타이머를 생성한다.
   */
  int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
  if (timer_fd < 0) {
    perror("timerfd_create() ");
    return NULL;
  }

  /*
   * 타이머에 주기를 설정한다.
   */
  int ret = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &ts, NULL);
  if (ret < 0) {
    perror("timerfd_settime() ");
    return NULL;
  }

  /*
   * 타이머 만기 이벤트를 처리한다.
   */
  ssize_t s;
  uint64_t exp;
  while (1)  {
    s = read(timer_fd, &exp, sizeof(exp));
    if (s == sizeof(exp)) {
      /* TODO */
      printf("Timer expired\n");
    }
  }

  return NULL;
}


/**
 * @brief 메인 함수 
 * @return int 
 */
int main(void) 
{
  /*
   * 타이머 처리 쓰레드를 생성한다.
   */
  if (pthread_create(&g_thread, NULL, timer_thread, NULL) != 0) {
    perror("pthread_create() ");
    return -1;
  }

  /*
   * 쓰레드 종료될 때까지 대기한다 (= 프로세스 종료 방지용)
   */
  pthread_join(g_thread, NULL);
  return 0;
}

 

다음과 같이 빌드한다.

gcc -o timer timer.c -lpthread

 

빌드된 파일을 실행하면 다음과 같이 주기적으로 출력되는 것을 확인할 수 있다.

root# ./timer
Timer expired
Timer expired
Timer expired
Timer expired
Timer expired
Timer expired

댓글

Designed by JB FACTORY