프로그래밍/리눅스 프로그래밍
리눅스 타이머 프로그래밍
오늘도 야근
2023. 1. 15. 14:15
리눅스 프로그램에서 시그널(인터럽트) 처리 방식이 아닌 쓰레드 방식의 타이머를 사용할 수 있다.
쓰레드 방식의 타이머를 사용할 경우,
타이머 만기 시에 해당 이벤트를 처리할 수 있는 쓰레드가 생성되므로,
해당 쓰레드(=타이머 만기 처리루틴) 내에서 뮤텍스 등 프로세스 컨텍스트에서만 허용되는 지연 발생 동작을 수행할 수 있다는 장점이 있다.
타이머를 생성하기 위해 timer_create() 호출 시, 인자로 전달하는 sigevent 구조체의 sigeve_notify 변수의 값을 SIGEV_THREAD로 설정함으로써 쓰레드 기반의 타이머를 사용할 수 있다.
이 경우 타이머 만기 시마다,
sigevent 구조체의 sigeve_notify_function 변수에 연결된 쓰레드 함수를 수행하는 쓰레드가 생성되며, 해당 쓰레드 함수 내에서 타이머 만기 시에 수행할 동작을 구현할 수 있다.
사용되는 함수는 timer_create(), timer_settime() 이다.
타이머 구현의 예제는 다음과 같다.
// timer2.c
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#define INITIAL_DELAY (1000) ///< 최초 타이머 만기 지연 (밀리초 단위)
#define INTERVAL (100) ///< 타이머 만기 주기 (밀리초 단위)
/**
* @brief 타이머 만기 시 실행되는 쓰레드
* @param arg 사용되지 않음
*/
static void timer_expired_thread(union sigval arg)
{
(void)arg;
/*
* TODO
* 시그널(인터럽트) 처리 함수가 아닌 쓰레드 함수이므로 뮤텍스 등 프로세스 컨텍스트에서 사용할 수 있는 지연 동작을 사용할 수 있다.
*/
printf("Timer expired\n");
}
/**
* @brief 메인 함수
* @return int
*/
int main(void)
{
struct itimerspec ts;
/*
* 최초 타이머 만기 주기를 설정한다.
*/
ts.it_value.tv_sec = INITIAL_DELAY / 1000;
ts.it_value.tv_nsec = (INITIAL_DELAY % 1000) * 1000000;
/*
* 두번째부터의 타이머 주기를 설정한다.
*/
ts.it_interval.tv_sec = INTERVAL / 1000;
ts.it_interval.tv_nsec = (INTERVAL % 1000) * 1000000;
/*
* 타이머를 생성한다 - 타이머 만기 시 쓰레드가 생성되도록 설정한다 (SIGEV_THREAD).
*/
timer_t timer;
struct sigevent se;
se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_ptr = &timer;
se.sigev_notify_function = timer_expired_thread;
se.sigev_notify_attributes = NULL;
int ret = timer_create(CLOCK_MONOTONIC, &se, &timer);
if (ret < 0) {
perror("timer_create() ");
return -1;
}
/*
* 타이머에 주기를 설정한다.
*/
ret = timer_settime(timer, 0, &ts, NULL);
if (ret < 0) {
perror("timer_settime() ");
return -1;
}
/*
* 프로세스 실행 유지
*/
while (1) {
sleep(1);
}
return 0;
}
다음과 같이 빌드한다.
gcc -o timer2 timer2.c -lrt
빌드된 파일을 실행하면 다음과 같이 주기적으로 출력되는 것을 확인할 수 있다.
root# ./timer2
Timer expired
Timer expired
Timer expired
Timer expired
Timer expired
Timer expired