๐Ÿ•ฐ๏ธ POSIX Clocks

๐Ÿ•ฐ๏ธ POSIX Clocks
Chapter 23 | Part 5 of 9 โ€” clock_gettime(), clock_settime() & Clock IDs
๐ŸŽฏ Precision
Nanoseconds
๐Ÿ”— Link
-lrt
๐Ÿ“œ Standard
POSIX.1b

Why POSIX Clocks?

The traditional gettimeofday() gives microsecond resolution. POSIX.1b clocks give nanosecond resolution and provide multiple clock types for different use cases โ€” wall time, monotonic time, and per-process/per-thread CPU time.

All programs using this API must be compiled with -lrt (realtime library).

๐Ÿ“‹ POSIX Clock Types
Clock ID What it Measures Settable? Use Case
CLOCK_REALTIME System-wide wall-clock time (since Epoch) โœ… Yes (root) Timestamps, log times
CLOCK_MONOTONIC Time since system boot โ€” never goes backward โŒ No Measuring elapsed time, timeouts
CLOCK_PROCESS_CPUTIME_ID CPU time consumed by calling process Read-only (Linux) Profiling, CPU budgets
CLOCK_THREAD_CPUTIME_ID CPU time consumed by calling thread Read-only (Linux) Per-thread profiling
CLOCK_MONOTONIC_RAW Like MONOTONIC but unaffected by NTP โŒ No Clock synchronization apps
Which to use? For elapsed time measurement: always use CLOCK_MONOTONIC โ€” it never jumps backward due to NTP or manual clock changes.

โš–๏ธ CLOCK_REALTIME vs CLOCK_MONOTONIC
CLOCK_REALTIME problem

T=0: 1000s
T=1: 1001s (normal)
T=2: 999s โ† NTP jump back!
T=3: 1000s (normal)
elapsed = 999-1000 = -1s ???
CLOCK_MONOTONIC safe

T=0: 5000s (since boot)
T=1: 5001s (always increases)
T=2: 5002s (NTP can’t affect it)
T=3: 5003s
elapsed = always correct โœ“

๐Ÿ“‹ Function Signatures
#define _POSIX_C_SOURCE 199309
#include <time.h>

/* Read the current value of a clock */
int clock_gettime(clockid_t clockid,
                  struct timespec *tp);

/* Set a clock value (requires CAP_SYS_TIME for CLOCK_REALTIME) */
int clock_settime(clockid_t clockid,
                  const struct timespec *tp);

/* Get the resolution (granularity) of a clock */
int clock_getres(clockid_t clockid,
                 struct timespec *res);

/* All return 0 on success, -1 on error */

/* Get CPU-time clock ID for a process */
#include <time.h>
int clock_getcpuclockid(pid_t pid, clockid_t *clockid);

/* Get CPU-time clock ID for a thread */
#include <pthread.h>
#include <time.h>
int pthread_getcpuclockid(pthread_t thread,
                          clockid_t *clockid);
/* Both return 0 on success, positive error on failure */

/* Compile with: gcc prog.c -o prog -lrt */

๐Ÿ’ป Example 1: Reading Multiple Clocks
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <time.h>

void print_clock(const char *name, clockid_t cid) {
    struct timespec ts;
    if (clock_gettime(cid, &ts) == -1) {
        perror(name);
        return;
    }
    printf("%-30s : %ld.%09ld s\n", name, ts.tv_sec, ts.tv_nsec);
}

int main(void) {
    print_clock("CLOCK_REALTIME",           CLOCK_REALTIME);
    print_clock("CLOCK_MONOTONIC",          CLOCK_MONOTONIC);
    print_clock("CLOCK_PROCESS_CPUTIME_ID", CLOCK_PROCESS_CPUTIME_ID);
    print_clock("CLOCK_THREAD_CPUTIME_ID",  CLOCK_THREAD_CPUTIME_ID);
    return 0;
}

/* Compile: gcc -o clocks clocks.c -lrt
   Sample output:
   CLOCK_REALTIME               : 1750000000.123456789 s
   CLOCK_MONOTONIC              : 12345.678901234 s
   CLOCK_PROCESS_CPUTIME_ID     : 0.000456789 s
   CLOCK_THREAD_CPUTIME_ID      : 0.000456789 s
*/

๐Ÿ’ป Example 2: Precise Elapsed Time Measurement

Use CLOCK_MONOTONIC to accurately measure how long a function takes.

#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <time.h>
#include <unistd.h>

/* Calculate difference between two timespec values in milliseconds */
double timespec_diff_ms(struct timespec *start,
                        struct timespec *end) {
    return (double)(end->tv_sec  - start->tv_sec)  * 1000.0
         + (double)(end->tv_nsec - start->tv_nsec) / 1000000.0;
}

/* Some work to measure */
void do_work(void) {
    volatile long x = 0;
    for (long i = 0; i < 100000000L; i++)
        x += i;
}

int main(void) {
    struct timespec t_start, t_end;

    /* Start timer */
    clock_gettime(CLOCK_MONOTONIC, &t_start);

    do_work();

    /* Stop timer */
    clock_gettime(CLOCK_MONOTONIC, &t_end);

    printf("do_work() took %.3f ms\n",
           timespec_diff_ms(&t_start, &t_end));

    /* Also measure CPU time specifically */
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t_start);
    sleep(1);   /* Sleep does NOT consume CPU time */
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t_end);

    printf("CPU time during sleep(1): %.6f ms\n",
           timespec_diff_ms(&t_start, &t_end));
    /* Will be near 0 โ€” sleep does not burn CPU */

    return 0;
}

/* Output:
   do_work() took 245.871 ms
   CPU time during sleep(1): 0.042 ms
*/

๐Ÿ’ป Example 3: Per-Process CPU Time via clock_getcpuclockid()

Get the CPU clock of any process by PID.

#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    clockid_t cid;
    struct timespec ts;
    pid_t target_pid;

    if (argc < 2) {
        /* Use own PID if no argument */
        target_pid = getpid();
    } else {
        target_pid = (pid_t)atoi(argv[1]);
    }

    /* Get CPU-time clock ID for the target process */
    if (clock_getcpuclockid(target_pid, &cid) != 0) {
        perror("clock_getcpuclockid");
        return 1;
    }

    /* Read the CPU time */
    if (clock_gettime(cid, &ts) == -1) {
        perror("clock_gettime");
        return 1;
    }

    printf("PID %d has consumed %ld.%09ld s of CPU time\n",
           (int)target_pid, ts.tv_sec, ts.tv_nsec);

    /* Get resolution */
    struct timespec res;
    clock_getres(cid, &res);
    printf("Clock resolution: %ld ns\n", res.tv_nsec);

    return 0;
}

/* Compile: gcc -o cpu_clock cpu_clock.c -lrt
   Usage: ./cpu_clock          (own process)
          ./cpu_clock 1234     (PID 1234)
*/

๐ŸŽ“ Interview Questions
Q1. Why is CLOCK_MONOTONIC preferred over CLOCK_REALTIME for elapsed time?

CLOCK_REALTIME can jump backward (e.g., NTP sync, manual date set), causing negative elapsed times. CLOCK_MONOTONIC only increases โ€” it measures time since boot and is immune to clock adjustments.

Q2. Which clock types require privilege to set?

Only CLOCK_REALTIME can be set, and it requires the CAP_SYS_TIME capability (typically root). CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, and CLOCK_THREAD_CPUTIME_ID are not settable on Linux.

Q3. What is the difference between CLOCK_PROCESS_CPUTIME_ID and CLOCK_MONOTONIC?

CLOCK_MONOTONIC measures wall-clock time (the process is sleeping, it still advances). CLOCK_PROCESS_CPUTIME_ID only advances when the process is actually running on CPU โ€” sleep(1) adds nothing to it.

Q4. How do you compile a program that uses the POSIX clocks API?

Add -lrt to the compile command: gcc prog.c -o prog -lrt. Also define _POSIX_C_SOURCE 199309 before the includes.

Q5. What does clock_getres() return?

The resolution (granularity) of the specified clock in a timespec structure. If it returns tv_nsec=1, the clock has nanosecond resolution. If tv_nsec=1000000 (1ms), it’s millisecond granularity.

Next: clock_nanosleep() โ€” High-Precision Sleeping โ†’

Part 6: clock_nanosleep() โ† Part 4: Sleeping

Leave a Reply

Your email address will not be published. Required fields are marked *