System calls
You can't talk about time without clocks. A standard definition of clocks is "An instrument to measure time".
Strictly speaking the analogy isn't perfect, since these clocks aren't just different ways of measuring time, they measure some different notions of time, but this is a somewhat philosophical point.
The interface for this in Linux are system calls with a clkid_t
parameter,
though not all system calls are valid for every clock.
- clock_adjtime(2)
- clock_gettime(2)
- clock_getres(2)
- clock_settime(2)
- clock_nanosleep(2)
- timer_create(2)
- timer_settime(2)
- timer_gettime(2)
- timer_getoverrun(2)
- timer_delete(2)
- timerfd_create(2)
- timerfd_gettime(2)
- timerfd_settime(2)
This is not an exhaustive list of system calls, since there are also older system calls for compatibility with POSIX or other UNIX standards, that offer a subset of the functionality of the above ones.
System calls beginning clock_
are about getting the time or waiting,
beginning timer_
are for setting periodic or one-shot timers,
timerfd_
are for timers that can be put in event loops.
For the sake of not introducing too many concepts at once, we're going to start with the simplest clock and work our way up.
CLOCK_MONOTONIC_RAW
The first clock we care about is CLOCK_MONOTONIC_RAW
.
This is the simplest clock.
It it initialised to an arbitrary value on boot and counts up at a rate of one second per second to the best of its ability.
This clock is of limited use on its own since the only clock-related system calls that work with it are clock_gettime(2) and clock_getres(2). It can be used to determine the order of events, if the time of the event was recorded and the relative time difference between when they happened, since we know that the clock increments one second per second.
Example
In this program below,
we time how long it takes to read 4096 bytes from /dev/zero
,
and print the result.
#include <stdio.h> /* fopen, fread, printf */
#include <time.h> /* clock_gettime, CLOCK_MONOTONIC_RAW, struct timespec */
int main(void) {
FILE *devzero;
int ret;
struct timespec start, end;
char buf[4096];
devzero = fopen("/dev/zero", "r");
if (!devzero) {
return 1;
}
/* get start time */
ret = clock_gettime(CLOCK_MONOTONIC_RAW, &start);
if (ret < 0) {
return 2;
}
if (fread(buf, sizeof *buf, sizeof buf, devzero) != sizeof buf) {
return 3;
}
/* get end time */
ret = clock_gettime(CLOCK_MONOTONIC_RAW, &end);
if (ret < 0) {
return 4;
}
end.tv_nsec -= start.tv_nsec;
if (end.tv_nsec < 0) {
end.tv_sec--;
end.tv_nsec += 1000000000l;
}
end.tv_sec -= start.tv_sec;
printf("Reading %zu bytes took %.0f.%09ld seconds\n",
sizeof buf, (double)end.tv_sec, (long)end.tv_nsec);
return 0;
}
You're possibly curious about the naming of the clock called MONOTONIC_RAW
.
In the next article we will talk about CLOCK_MONOTONIC
,
which may help you understand why it's named the way it is.
I suggested the uses of this clock are for the sequencing of events and for calculating the relative period between them. If you can think of another use please send us a comment.
If you like to get hands-on, you may want to try reimplementing the above program in your preferred programming language, or extending it to time arbitrary events.