OS는 내부적으로 hardware timers를 사용하여 여러가지 software timer를 구현하여 제공한다.
time-sharing OS에서 process scheduling을 위해서 interval timers를 사용한다.
POSIX:XSI Timers
itimerval 구조체를 사용한다. ( 마이크로 단위로 timer를 세팅한다 )
struct timeval it_value; /* 초기 값 */
struct timeval it_interval; /* 반복해서 실행할 것인지 ( 두번째부터는 언제 만료가 될 것인지 ), 한번만 실행할 것인지 ( 0 ) */
#include <sys/time.h>
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *restrict value, struct itimerval *restrict ovalue);
setitimer()
which
타이머의 종류를 결정할 수 있다.
ITIMER_REAL: 보통 이것을 사용한다. real time ( 실시간 ) 으로 작동.
만료시 프로세스에게 SIGALARM 시그널을 보냄
ITIMER_VIRTUAL: 가상 시간으로 작동하는 timer. 중단되기도 했다가, 감소되기도 했다가..
프로세스가 실행중일 때만 타이머가 돌아간다.
만료시 프로세스에게 SIGVTALARM 시그널을 보냄
ITIMER_PROF: virtual timer와 비슷.
프로세스가 실행중일 때 타이머가 돌아감.
프로세스가 실행중이지는 않더라도, 시스템이 프로세스가 요청한 활동을 할때에도 타이머가 돌아감
만료시 프로세스에게 SIGPROF 시그널을 보냄
ovalue
이전에 세팅되어있던 타이머 값이 있었다면, 그 값을 반환해 줌.
ovalue가 NULL이 아니라면, 이전 값이 저장된다.
value→it_interval이 0이 아니라면, 타이머가 다시 시작할 때 it_interval로 시작이 된다.
value→it_interval이 0이라면, 한번만 시작되고 재시작하지 않는다
value→it_value가 0이라면, 기존의 타이머를 stop한다.
POSIX:TMR interval timers
clock을 이용해서, 프로세스가 필요할 경우 independent하게 객체를 이용해서 사용한다.
XSI 타이머와는 resolution이 차이가 난다.
nanoseconds 단위까지 타이머를 표현할 수 있다.
struct itimerspec structure
struct timespec it_interval; /* timer period */
struct timespec it_value; /* expiration */
timeval 구조체보다 resolution이 좋다.
#include <signal.h>
#include <time.h>
int timer_create(clockid_t clock_id, struct sigevent *restrict evp, timer_t *restrict timerid);
int timer_delete(timer_t timerid)
프로세스 별로 필요한 만큼 타이머 객체를 만들어야 한다.
fork시에 inherit 되지 않는다.
timer_create()
clock_id: clock의 종류
evp: 타이머가 만료가 되었을 때, 시그널을 통지 받을 것인지, 어떠한 시그널로 받을 것인지
NULL 이라면, 기본 signal인 SIGALARM을 받는다 ( CLOCK_REALTIME )
evp→sigev_signo: 시그널 번호
evp→sigev_notify: 시그널로 통지를 받을 것이냐?
SIGEV_SIGNAL: 받을래
SIGEV_NONE: 안받을래
timerid: output 파라미터 / 새로 만들어진 타이머 객체의 id
time_delete: 타이머 객체를 삭제하는 것을 OS에게 요청할 수 있다.
return value
성공했을 시 0
실패했을 시 -1
#include <time.h>
int timer_getoverrun(timer_t timerid);
int timer_gettime(timer_t timerid, struct itimerspec *value);
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue)
timer_settime
타이머를 시작하거나 중지시킬 수 있다.
중지의 경우 value→it_value→tv_sec을 0으로 하면 된다.
timerid: 타이머 아이디 지정
flags: 특정 옵션을 주고 싶을 때 / 옵션을 주지 않을 것이라면 0
value: itimespec 구조체인 value / interval 객체를 줄 수 있다.
ovalue: 이전의 타이머 값을 저장하기 위한 output 파라미터
timer_gettime
value: output 파라미터 / 현재 남아있는 타이머 정보 값을 반환한다.
time_getoverrun
overrun 횟수를 반환받기 위함
타이머가 한번만 실행되고 말 경우에는 아니지만, 반복되는 타이머의 경우에 발생할 수 있음
만료되고 나서 시그널이 발생되는데, 프로세스가 그 시그널을 받아서 처리를 하고, 다시 타이머가 작동하고 …의 반복
타이머가 만료가 되고 나서 시그널을 발생시켜 프로세스에게 전달이 되었는데, 시그널이 프로세스에게 전달되지 못하고 pending된 경우 pending list에 들어가있고, 다시 타이머가 진행되는 경우 다시 또 시그널이 발생될 것이다. 다음 시그널이 발생되어 프로세스에게 전달이 될 것인데 또 pending이 된다면? pending list에는 같은 타입의 시그널이 들어있을 수 없기 때문에 기존 시그널이 덮여쓰여진다.
즉, pending이 되면서 덮여 쓰여진 ( 없어진 ) 시그널의 개수를 overrun 이라고 한다.
Example
#include <stdio.h>
#include <time.h>
#define MILLION 1000000L
#define THOUSAND 1000
void function_to-time(void);
int main(void){
long diftime;
struct itimerspec nvalue, ovalue;
timer _t timeid;
// tmr timer를 사용하기 위해 timer를 만든다.
if (timer_create(CLOCK_REALTIME, NULL, &timeid) == -1){
perror("Failed to create a timer based on CLOCK_REALTIME");
return 1;
}
ovalue.it_interval.tv_sec = 0;
ovalue.it_interval.tv_nsec = 0;
ovalue.it_value.tv_sec = MILLION;
ovalue.it_value.tv_nsec = 0;
if(timer_settime(timeid, O, &ovalue, NULL) == -1){
perror"Failed to set interval timer"i;
return 1;
}
function_to_time();
if(timer_gettime(timeid,&nvalue, == -1){
perror("Failed to get interval timer value");
return 1;
}
diftime = MILLION*(ovalue.it_value.tv_sec - nvalue.it_value.tv_sec) +
(ovalue.it_value.tv._nsec - nvalue.it_value.tv_nsec)/THOUSAND;
printf("The function_to_time took %ld microseconds or %f seconds.\n",
diftime, diftime/ (double}MILLION);
return 0;
}
Timer drift
계속 반복해서 타이머를 사용하는 경우에는 timer drift를 고려해야한다.
만료 시점이 delay가 생겨, 생각보다 나중에 타이머가 만료되는 경우
이유
타이머의 태생적인 이유 ⇒ 완전히 0으로 만들 수는 없다
타이머가 반복해서 실행이 되면, 만료된 이후 다시 시작하는 시점까지의 delay가 미세하게 존재한다.
Example
22ms의 interval을 가진 timer의 resolution이 10ms 라고 하자.
22가 표현이 되지 않기 때문에 8ms의 delay가 각각의 만료 시점마다 생기게 된다.
+ 다음 시점은 44를 원하지만, 30인 시점에 시작을 하면 60인 시점에 만료가 된다.
Solution
interval을 계속 22로 하는 것이 아니라, 다음 시점은 원래의 기대값으로 갈 수 있을 만큼만 ( 14 )
계속 timer drift가 증가되는 문제를 해결할 수는 있다.
Save T = current time + 22 ms
Set the timer to expire in 22 ms
In the signal handler, set the timer to expire in (T – current time + 22ms), and T = T + 22 ms
Solution
POSIX:TMR timer absolute time를 사용해라
timer_settime의 flags에 TIMER_ABSOLUTE를 세팅하면 된다.
*value field의 it_value에 absolute time으로 설정할 수 있다.
현재 시간을 clock_gettime으로 가져온 뒤 22ms를 더하고 이것을 T로 저장한다.
T값을 timer의 만료 시점으로 정하면 된다. ( TIMER_ABSOLUTE 태그를 사용하여 )