What is a Jiffy?
All the time-related syscalls we’ve seen so far are built on top of the kernel’s internal time unit: the jiffy. A jiffy is the period of one kernel timer interrupt — the heartbeat of the Linux scheduler. Understanding jiffies explains why some timers have minimum granularities and why process time has limited precision.
Keywords in This Post
The kernel maintains an internal counter called jiffies, which increments once on every timer interrupt. The frequency of these interrupts is defined by the kernel constant HZ.
Analogy: Think of a metronome. Each tick is a jiffy. If you set the metronome to 250 beats per minute (HZ=250), each beat is 4 ms long. The kernel can only notice “time has passed” at tick boundaries — events that happen between ticks are invisible to the scheduler.
| Kernel Version | Default HZ (x86) | Jiffy size |
|---|---|---|
| 2.4 and earlier | 100 | 10 ms |
| 2.6.0 to 2.6.12 | 1000 | 1 ms |
| 2.6.13+ (configurable) | 250 (default) | 4 ms |
| 2.6.20+ (additional option) | 300 (optional) | 3.33 ms — evenly divides 25 fps (PAL) and 30 fps (NTSC) |
Since kernel 2.6.13, you can choose HZ = 100, 250, or 1000 as a kernel compile option (under “Processor type and features → Timer frequency”). Higher HZ means finer timer granularity but more CPU overhead — each interrupt consumes a small amount of CPU time that cannot be used by user processes.
| Higher HZ (e.g. 1000) | Lower HZ (e.g. 100) |
|---|---|
| ✓ Finer timer resolution (1 ms sleeps) | ✓ Less interrupt overhead, better for high-load servers |
| ✓ More responsive interactive systems | ✓ Better for embedded systems with tight power budgets |
| ✗ More CPU time spent on interrupt handling | ✗ Coarser sleep granularity (10 ms minimum) |
Process time is broken into two components — not just one total number. Understanding the split helps diagnose performance bottlenecks.
| Component | What it measures | Performance implication |
|---|---|---|
| User CPU time | CPU cycles executing your code in user space | High user time → CPU-bound algorithm; consider better data structures or SIMD |
| System CPU time | CPU cycles the kernel spends on behalf of your process (syscalls, page faults, I/O) | High system time → too many syscalls or excessive I/O; consider batching or buffering |
From the shell, the time command shows all three values at once:
$ time ./my_program
real 0m4.84s <-- Wall clock time (includes waiting, sleeping)
user 0m1.03s <-- User CPU time consumed by the process
sys 0m3.43s <-- System CPU time (kernel doing work for the process)
# In this case, sys > user suggests lots of file I/O or many system calls.
# Total CPU = user + sys = 4.46s, but real time = 4.84s
# The remaining 0.38s was the process blocked waiting (not using CPU).
times() fills a struct tms with the user and system CPU times of the calling process and its waited-for children. It also returns the elapsed real time as its return value.
#include <sys/times.h>
clock_t times(struct tms *buf);
/* Returns elapsed clock ticks since some arbitrary past point, or (clock_t)-1 on error */
struct tms {
clock_t tms_utime; /* User CPU time used by this process */
clock_t tms_stime; /* System CPU time used by this process */
clock_t tms_cutime; /* User CPU time of waited-for children */
clock_t tms_cstime; /* System CPU time of waited-for children */
};
The values in struct tms are in clock ticks. To convert to seconds, divide by sysconf(_SC_CLK_TCK) (which returns 100 on most Linux systems — not the same as CLOCKS_PER_SEC!):
#include <sys/times.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
struct tms t;
long ticks_per_sec = sysconf(_SC_CLK_TCK); /* Usually 100 on Linux */
/* ... do some work ... */
volatile long sum = 0;
for (long i = 0; i < 50000000L; i++)
sum += i;
/* ... end of work ... */
if (times(&t) == (clock_t)-1) {
perror("times");
return 1;
}
printf("User CPU : %.2f s\n", (double)t.tms_utime / ticks_per_sec);
printf("System CPU: %.2f s\n", (double)t.tms_stime / ticks_per_sec);
return 0;
}
Don’t use times() return value for measuring elapsed time reliably. The returned tick count can overflow a clock_t and wrap back to zero on long-running systems. For measuring elapsed wall-clock time, always use gettimeofday() instead.
clock() is the simpler alternative to times(). It returns the total CPU time used (user + system combined), measured in CLOCKS_PER_SEC units.
#include <time.h>
clock_t clock(void);
/* Returns total CPU time in CLOCKS_PER_SEC units, or (clock_t)-1 on error */
/* CLOCKS_PER_SEC is always 1,000,000 (1 million) on POSIX systems */
#include <time.h>
#include <stdio.h>
int main(void)
{
clock_t start = clock();
/* Work to measure */
volatile double x = 1.0;
for (long i = 0; i < 10000000L; i++)
x = x * 1.000001;
clock_t end = clock();
double cpu_seconds = (double)(end - start) / CLOCKS_PER_SEC;
printf("CPU time used: %.4f seconds\n", cpu_seconds);
return 0;
}
| Constant | Value | Used with |
|---|---|---|
| CLOCKS_PER_SEC | 1,000,000 (fixed by POSIX) | clock() |
| sysconf(_SC_CLK_TCK) | Usually 100 on Linux (USER_HZ) | times() |
Key difference: CLOCKS_PER_SEC (used with clock()) is always 1,000,000 regardless of the kernel’s actual clock resolution. sysconf(_SC_CLK_TCK) (used with times()) reflects the kernel’s actual tick rate (USER_HZ). On most Linux systems, USER_HZ = 100, which means times() has 10 ms granularity while clock() reports with finer resolution.
| Feature | times() | clock() |
|---|---|---|
| Header | <sys/times.h> | <time.h> |
| Separates user and system? | ✓ Yes | ✗ No (combined) |
| Reports child process time? | ✓ Yes (tms_cutime/cstime) | ✗ No |
| Simpler to use? | ✗ More setup | ✓ Very simple |
| Also returns elapsed real time? | ✓ (return value) | ✗ No |
| Best for | Detailed process profiling | Simple CPU usage check |
| Function/Concept | What it does | Header |
|---|---|---|
| time() | Get current time as time_t (seconds) | time.h |
| gettimeofday() | Get current time with microsecond precision | sys/time.h |
| ctime() | time_t → fixed-format printable string | time.h |
| gmtime() | time_t → struct tm (UTC) | time.h |
| localtime() | time_t → struct tm (local time) | time.h |
| mktime() | struct tm → time_t (with auto-normalisation) | time.h |
| strftime() | struct tm → custom-format string | time.h |
| strptime() | Date string → struct tm (parse) | time.h |
| settimeofday() | Set system clock immediately (privileged) | sys/time.h |
| adjtime() | Gradually adjust system clock (privileged) | sys/time.h |
| times() | Get user + system CPU time (separate) | sys/times.h |
| clock() | Get total CPU time (simple) | time.h |
| setlocale() | Set/query locale for language and formatting | locale.h |
| tzset() | Read TZ env variable and initialise timezone globals | time.h |
Chapter 10 Complete!
You have covered all topics in Chapter 10 of Linux System Programming — from the Epoch and time_t to Jiffies and process CPU time. Explore more Linux programming topics below.
