Skip to content

Instantly share code, notes, and snippets.

@mepcotterell
Last active February 10, 2019 22:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mepcotterell/422169c1d878469fd75755327bb7435b to your computer and use it in GitHub Desktop.
Save mepcotterell/422169c1d878469fd75755327bb7435b to your computer and use it in GitHub Desktop.

Interval Timer Notes

The system provides each process with three interval timers, each decrementing in a distinct time domain. When any timer expires, a signal is sent to the process, and the timer (potentially) restarts.

Timers will never expire before the requested time, but may expire some (short) time afterwards, which depends on the system timer resolution and on the system load; see time(7). Upon expiration, a signal will be generated and the timer reset. If the timer expires while the process is active (always true for ITIMER_VIRTUAL) the signal will be delivered immediately when generated. Otherwise the delivery will be offset by a small time dependent on the system loading.

Relevant Prototypes

#include <sys/time.h>
int getitimer(int which, struct itimerval * curr_value);
int setitimer(int which, const struct itimerval * new_value, struct itimerval * old_value);
which Description
ITIMER_REAL decrements in real time, and delivers SIGALRM upon expiration.
ITIMER_VIRTUAL decrements only when the process is executing, and delivers SIGVTALRM upon expiration.
ITIMER_PROF decrements both when the process executes and when the system is executing on behalf of the process, and delivers SIGPROF upon expiration.

Relevant Structures

struct itimerval {
  struct timeval it_interval; /* next value */
  struct timeval it_value;    /* current value */
};
struct timeval {
  long tv_sec;                /* seconds */
  long tv_usec;               /* microseconds */
};

Example

#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

unsigned int x = 0;

void interrupt(int signum) {
  printf("timer %d!\n", ++x);
} // interrupt

int main() {

  struct sigaction sa;
  struct itimerval timer;

  memset(&sa, 0, sizeof(sa));
  sa.sa_handler = &interrupt;
  sigaction(SIGPROF, &sa, NULL);

  timer.it_value.tv_sec = 0;
  timer.it_value.tv_usec = 5000;
  timer.it_interval.tv_sec = 0;
  timer.it_interval.tv_usec = 5000;
  setitimer(ITIMER_PROF, &timer, NULL);
  
  for (;;); 

  return 0;
  
} // main

C & ASM Example

struct itimerval timer;
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = 5000;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 5000;
setitimer(ITIMER_PROF, &timer, NULL);
pushq	%rbp             ; 
movq	%rsp, %rbp       ; // initialize new call frame

subq	$32, %rsp        ; // move stack pointer down enough for one struct itimerval
movq	$0, -32(%rbp)    ; // it_interval.tv_sec = 0     ;  0(%rsp)
movq	$5000, -24(%rbp) ; // it_interval.tv_usec = 5000 ;  8(%rsp)
movq	$0, -16(%rbp)    ; // it_value.tv_sec = 0        ; 16(%rsp)
movq	$5000, -8(%rbp)  ; // it_value.tv_usec = 5000    ; 24(%rsp)

leaq	-32(%rbp), %rax  ; // compute address of struct itimerval

movl	$2, %edi         ; // ITIMER_PROF
movq	%rax, %rsi       ; // &timer
movq	$0, %rdx         ; // NULL
call	setitimer        ;

Although the calling convention for functions and system calls differ, they do use the same registers for the first three arguments. Since there are only three arguments to setitimer, you can avoid a degree of indirection (function calling a system call) by moving $38 (sys_setitimer) into %rax, then using the syscall instruction directly.

movl	$2, %edi         ; // ITIMER_PROF
movq	%rax, %rsi       ; // &timer
movq	$0, %rdx         ; // NULL
movq	$38, %rax        ; // sys_setitimer
syscall		         ; // invoke system call
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment