Timers

 

  • Sleeping
    • Allows a process (or thread) to suspend execution for a period of time
      • sleep(), nanosleep(), clock_nanosleep()
  • Timer
    • Allows a process to schedule a notification for itself to occur at some time in the future
      • alarm() → 주기적인 타이머를 사용하기에는 부적합
      • Interval timer (itimer)
      • POSIX timer

 

Interval Timers

 

  • int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue)
  • Provide more control than alarm()
  • Can automatically rearm themselves
  • Arms a timer of type which with the expiration specified by value
    • ITIMER_REAL
      • Measures real time and sends the process a SIGALRM signal
    • ITIMER_VIRTUAL
      • Decrements only while the process' user-space code is executing and sends the process a SIGVTALRM signal
    • ITIMER_PROF
      • Decrements both while the process is executing, and while the kernel is executing on behalf of the process and sends the process a SIGPROF signal
  • Once the time specified by it_value elapses, the kernel rearms the timer with the time provided by it_interval
    • it_value is the time remaining on the current timer
    • If it_interval is 0, the timer is not rearmed ≒ alarm(it_value)
struct itimerval{
    struct timeval it_interval; /* next value */
    struct timeval it_value;    /* current value */
}

 

  • If ovalue is not NULL, the previous values for the interval timer of type which is returned → alarm()과 마찬가지로 덮어쓴 이전 알람을 얻을 수 있음
  • Return value
    • On success, 0 is returned; on error, -1 is returned

 

Interval Timers

 

  • int getitimer(int which, struct itimerval *value)
  • Returns the current values for the interval timer of type which
  • Return value
    • On success, 0 is returned; on error, -1 is returned

 

Example

 

void alarm_handler(int signo)
{
    char *str = "Timer expired!\n";
    write(1, str, strlen(str));
}

void foo(void)
{
    struct itimerval delay;
    int ret;
    
    signal(SIGALRM, alarm_handler);
    delay.it_value.tv_sec = 5;
    delay.it_value.tv_usec = 0;
    delay.it_interval.tv_sec = 1;
    delay.it_interval.tv_nsec = 0;
    ret = setitimer(ITIMER_REAL, &delay, NULL);
    if(ret){
        perror("setitimer");
        return;
    }
    while(1) pause();
}

 

Interference with alarm()

 

  • On Linux, alarm() and setitimer() share the same per-process real-time timer
    • Setting a timer with one of these functions changes any timer previous set by either of the functions
    • Use only one of setitimer() and alarm() for setting real-timer timers
    • alarm()과 setitimer()에서의 ITIMER_REAL을 사용하는 경우는 같은 CLOCK을 공유하기 때문에 두 개를 동시에 같이 사용하면 오류가 발생할 수 있다.

 

Interval Timers

 

  • If simplicity is a prime motivator, setitimer() is most likely a good choice
  • Classical interval timers suffer a number of limitations
    • We can set only one timer of each of the three type
      • ITIMER_REAL, ITIMER_VIRTUAL, and ITIMER_PROF
    • The only way of being notified of timer expiration is via delivery of a signal
      • Can't change the signal that is generated when the timer expires
    • If an interval timer expires multiple times while the corresponding signal is blocked, then the signal handler is called only once
    • Timers are limited to microsecond resolution

 

 

POSIX Timers

 

Creating a Timer

 

  • int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) → POSIX signal이기 때문에 sigaction()을 통해서 등록해야함
  • Creates a new timer associated with the POSIX clock clockid and stores a unique timer identification in timerid
    • clockid
      • CLOCK_REALTIME
      • CLOCK_MONOTONIC
      • CLOCK_PROCESS_CPUTIME_ID
      • CLOCK_THREAD_CPUTIME_ID, etc.
    • Nothing actually happens until the timer is armed
  • Return value
    • On success, returns 0; on failure, returns -1
  • If evp is not NULL
    • It defines the asynchronous notification that occurs when the timer expires
evp.sigev_notify Notification method
SIGEV_NONE No notification; monitor timer using timer_gettime()
SIGEV_SIGNAL Send signal evp.sigev_signo to process → 정해진 signal을 보내는게 아니라 커스텀이 가능
SIGEV_THREAD Call evp.sigev_notify_function as start function of new thread
SIGEV_THREAD_ID Send signal evp.sigev_signo to thread sigev_notify_thread_id
  • If evp is NULL,
    • It is equivalent to specifying sigev_notify is SIGEV_SIGNAL, sigev_signo is SIGALRM, and sigev_value.sival_ptr is timerid

 

Example

 

struct sigevent evp;
timer_t timer;
int ret;

evp.sigev_value.sival_ptr = &timer;
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR1;

ret = timer_create(CLOCK_REALTIME, &evp, &timer);
if(ret)
    perror("timer_create");

 

Arming a Timer

 

  • int timer_settime(timer_t timerid, int flags, const stuct itimerspec *value, struct itimerspec *ovalue)
  • Arms the timer specified by timerid with the expiration value
struct itimerspec{
    struct timespec it_interval; /* next value */
    struct timespec it_value;    /* current value */
};

 

  • flags
    • 0: it_value is interpreted as relative
    • TIMER_ABSTIME: it_value is interpreted as absolute

 

Obtaining the Expiration of a Timer

 

  • int timer_gettime(timer_t timerid, struct itimerspec *value)
  • Stores the time until next expiration and the interval of the timer specified by timerid in the structure pointed at by value
  • Return value
    • On success, return 0; on failure, returns -1

 

Obtaining the Overrun of a Timer

 

  • int timer_getoverrun(timer_t timerid)
  • Returns the overrun count for the timer specified by its timerid argument
  • On failure, returns -1
  • Async-signal-safe
    • It is safe to call if form within a signal handler
  • The timer overrun count is reset each time we receive the timer signal

 

Deleting a Timer

 

  • int timer_delete(timer_t timerid)
  • Destroys the timer associated with timerid
  • Return value
    • On success, returns 0; on failure, returns 01

Example

 

void alarm_handler(int signo)
{
    char *str = "Timer expired!\n";
    write(1, str, strlen(str));
}

void foo(void)
{
    struct sigaction sa;
    struct sigevent ev;
    timer_t timerid;
    struct itimerspec delay;
    int ret;
    
    sa.sa_handler = alarm_handler;
    ret = sigaction(SIGUSR1, &sa, NULL);
    if(ret){
        perror("sigaction");
        return;
    }
    
    ev.sigev_notify = SIGEV_SIGNAL;
    ev.sigev_signo = SIGUSR1;
    ev.sigev_value.sival_ptr = &timerid;
    ret = timer_create(CLOCK_MONOTONIC, &ev, &timerid);
    if(ret){
        perror("timer_create");
        return;
    }
    
    delay.it_value.tv_sec = 5;
    delay.it_value.tv_nsec = 0;
    delay.it_interval.tv_sec = 1;
    delay.it_interval.tv_nsec = 0;
    ret = timer_settime(timerid, 0, &delay, NULL);
    if(ret){
        perror("timer_settime");
        return;
    }
    while(1) pause();
}