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.
#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. |
struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
#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
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