Connection timeouts, credential expiration after lid closure, and cross-machine log ordering do not require a single timeline.
Objective
Section
Selecting clocks and unit conversion
Sections 1–2
Corrections and time scales
Sections 3–4
Hardware synchronization, reading, and observation
Subsequent sections
Time management answers different questions: what date an event occurred, how much actual time elapsed, whether system sleep counts toward timeouts, and whether timestamps from two machines can be compared. Clock selection should first address these semantic questions, and then consider reading costs.
1. Selecting Clocks by Semantics
Clock
Timeline & Correction
Suspend
Typical Use
CLOCK_REALTIME
Calendar time; subject to step or gradual correction
Counts elapsed sleep time
Logging, human-readable dates, calendar deadlines
CLOCK_MONOTONIC
Not affected by calendar clock jumps, but subject to gradual frequency adjustments
Does not count
Timeouts and intervals during single-machine uptime
CLOCK_MONOTONIC_RAW
Does not apply NTP or similar frequency adjustments
Does not count
Analyzing timer frequency/correction impacts
CLOCK_BOOTTIME
Similar to MONOTONIC, but also accumulates suspend time
Counts
Durations that must span across suspend
CLOCK_TAI
Forms the corresponding time scale using kernel-configured TAI offsets
Counts
Specific time markers under correct synchronization and configuration
PROCESS/THREAD_CPUTIME_ID
Accumulates corresponding CPU execution time
Does not count sleep as execution
CPU consumption analysis
The monotonicity of MONOTONIC usually allows adjacent reads to be equal; it does not guarantee a strict increase with every read. It is not "immune to NTP adjustments," nor does it imply that counters are free from frequency errors indefinitely. RAW is also not a UTC scale comparable across machines, nor can it unconditionally be called the most precise business clock. See clock_gettime for API semantics.
Two Deadline Examples
A connection establishment timeout of up to 5 seconds typically requires a MONOTONIC deadline to avoid waiting an extra minute if an administrator rolls back the calendar clock. If authentication credentials should expire based on elapsed time even after the laptop lid is closed, the design should combine BOOTTIME or correctly synchronized absolute deadlines.
Saving a MONOTONIC value to disk and continuing to compare it after a reboot is usually meaningless. Subtracting a MONOTONIC value from Machine A from that of Machine B does not yield network latency. Distributed logs require calendar time, error assessment, and causal identifiers for joint interpretation.
Advance awake time first, then cross a suspend or wall-clock jump. Below, we separate "the clock has passed the deadline" from "the callback has been executed" to avoid misinterpreting expiration as immediate execution.
Preparing the visual
For the Same Five-Second Deadline, Who Changes with Suspend and Correction?
MONOTONIC does not accumulate suspend; BOOTTIME does; REALTIME is also affected by calendar jumps. The condition for expiration and the execution of the callback are different states.
{"id":"clock-deadline-semantics","title":"For the Same Five-Second Deadline, Who Changes with Suspend and Correction?","summary":"MONOTONIC does not accumulate suspend; BOOTTIME does; REALTIME is also affected by calendar jumps. The condition for expiration and the execution of the callback are different states.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"actions\"><button id=\"awake\" data-i18n=\"awake\"></button><button id=\"suspend\" data-i18n=\"suspend\"></button></div><div class=\"control\"><label for=\"step\" data-i18n=\"step\"></label><output for=\"step\" id=\"step-value\"></output><input id=\"step\" type=\"range\" min=\"-10\" max=\"10\" value=\"-4\" step=\"1\"></div><div class=\"actions\"><button id=\"apply\" data-i18n=\"apply\"></button></div><div class=\"clock\"><header>MONOTONIC<strong id=\"now-M\"></strong></header><div class=\"deadline\"><i id=\"bar-M\"></i></div><div class=\"clock-result\"><span id=\"remaining-M\"></span><span id=\"status-M\"></span></div></div><div class=\"clock\"><header>BOOTTIME<strong id=\"now-B\"></strong></header><div class=\"deadline\"><i id=\"bar-B\"></i></div><div class=\"clock-result\"><span id=\"remaining-B\"></span><span id=\"status-B\"></span></div></div><div class=\"clock\"><header>REALTIME<strong id=\"now-R\"></strong></header><div class=\"deadline\"><i id=\"bar-R\"></i></div><div class=\"clock-result\"><span id=\"remaining-R\"></span><span id=\"status-R\"></span></div></div><div class=\"actions\"><button id=\"dispatch\" data-i18n=\"dispatch\"></button></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"note\"></p></div><div class=\"actions\"><button id=\"reset\" data-i18n=\"reset\"></button></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.clock{margin:18px 0;padding:14px;border:1px solid var(--rule);border-radius:8px}.clock header{display:flex;justify-content:space-between;gap:8px;font:12px var(--viz-font-ui)}.clock header strong{font-size:16px}.deadline{height:20px;width:100%;position:relative;background:var(--surface);border-right:2px solid var(--second);margin:12px 0}.deadline i{height:100%;display:block;background:var(--accent);transition:width .25s}.clock-result{display:grid;grid-template-columns:1fr 1fr;gap:8px;min-height:5em;font-size:12px}.clock-result span:last-child{text-align:right;color:var(--muted)}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}let now=[0,0,1000],fired=[false,false,false];const due=[5,5,1005],keys=['M','B','R'];function draw(){set('step-value',$('#step').value+' s');keys.forEach((k,i)=>{set('now-'+k,now[i]+' s');set('remaining-'+k,t('remaining')+'\\n'+(due[i]-now[i]));set('status-'+k,t(fired[i]?'fired':now[i]>=due[i]?'due':'waiting'));$('#bar-'+k).style.width=Math.max(0,Math.min(100,(now[i]-(i===2?1000:0))/5*100))+'%';});$('#dispatch').disabled=!now.some((v,i)=>v>=due[i]&&!fired[i]);set('explanation',t('note'));}$('#awake').onclick=()=>{now=now.map(v=>v+2);draw();};$('#suspend').onclick=()=>{now[1]+=4;now[2]+=4;draw();};$('#apply').onclick=()=>{now[2]+=+$('#step').value;draw();};$('#dispatch').onclick=()=>{fired=fired.map((v,i)=>v||now[i]>=due[i]);draw();};$('#step').oninput=draw;$('#reset').onclick=()=>{now=[0,0,1000];fired=[false,false,false];draw();};draw();","audio":false,"strings":{"apply":"Apply this correction","awake":"Run for 2 seconds","dispatch":"Process expired callbacks","due":"Expired, not yet processed","fired":"Callback executed","heading":"For the Same Five-Second Deadline, Who Changes with Suspend and Correction?","intro":"Three clocks each set an absolute deadline of \"current value + 5 seconds\". Advance uptime, pass through suspend, or adjust calendar time, then explicitly handle expired callbacks.","note":"Vertical lines represent the original deadlines. Progress is drawn only up to the deadline; numbers retain signed remaining seconds; callbacks must still be processed after the deadline, avoiding the pretense that system sleep is CPU execution.","remaining":"Remaining seconds to original deadline","reset":"Reset","scope":"Illustrates clocks starting at M=0, B=0, R=1000 seconds, with deadlines fixed at 5, 5, and 1005 respectively. The suspend operation directly returns the post-wakeup state without executing callbacks. NTP gradual adjustment, alarm wakeup capabilities, TAI, and timerfd CANCEL_ON_SET are omitted; executed callbacks will not re-execute due to rollback.","scopeLabel":"Model Scope and Conventions","step":"Calendar clock adjustment / second","suspend":"Suspend for 4 seconds then wake up","waiting":"Waiting"}}
2. How Timekeeping Converts Counts to Time
The core maintains the most recently updated count and base time, calculating the elapsed amount after reading a new count. A simplified integer conversion is:
The mask handles finite-width counter wraparound, while mult/shift reduce expensive calculations. However, real implementations must also avoid intermediate overflows, handle different bases and corrections, and cannot treat the formula as complete driver code. The maintained frequency must be sufficient to distinguish wraparounds and cannot remain unupdated indefinitely.
Reading time does not require waiting for the next tick: it can deduce how much time has passed since the base using the current count difference. The actual structure of the timekeeper varies by version, and maintaining its update path is not equivalent to "all hrtimer interrupts completely recalculating all clocks." See timekeeping for underlying responsibilities.
Why Consistent Snapshots Are Needed
Fields such as the base, mult, and shift must be interpreted together. If you read a pre-update base and post-update conversion parameters, the result may be incorrect. Therefore, the read path uses sequence validation mechanisms; if a concurrent update is detected, it retries. It is not a matter of reading the counter once and being done.
3. NTP: Time Servers, User-Space Control, and Kernel Corrections
User-space services like chronyd/ntpd handle network time sources and estimation, while the kernel performs corresponding frequency and phase adjustments via interfaces such as adjtimex/clock_adjtime. The kernel's ntp.c is not an independent, network-connected "NTP daemon."
Correction Method
How the Timeline Changes
Impact on Measurements
step
Directly changes calendar time
REALTIME can jump; MONOTONIC typically does not go backward
slew
Adjusts rate to gradually eliminate error
MONOTONIC reflects corresponding frequency gradients
The long-term difference between RAW and MONOTONIC can provide clues about corrections, but measurement noise and clock selection must still be considered.
The units of struct timex fields depend on flags/mode; for example, frequency uses specified scaling units, and the microsecond/nanosecond semantics of offset depend on the state. Do not annotate all adjtimex fields as plain nanoseconds and ppm. See adjtimex.
Synchronization Status and Error Ranges Must Be Read Together
A sync service reporting synchronization does not mean two machines have identical times. Network path asymmetry, clock drift, sampling, and hardware all introduce errors. When making cross-machine judgments for intervals below a millisecond, error ranges must be included in the conclusion.
4. TAI, UTC, and Leap Seconds
The offset between TAI and UTC is determined by historical leap seconds and should not be written as a permanent constant in long-term knowledge articles. Linux CLOCK_TAI also relies on the system having the correct offset configured; if the offset is not configured or is incorrect, a successful call does not guarantee a trustworthy TAI return.
The TAI time scale does not have the same discontinuous handling as UTC leap second insertions, but kernel time corrections and configuration changes can still affect actual observations. Time services using leap smear will intentionally deviate from strict UTC within the window; machines mixing different smear strategies cannot assume timestamps are directly consistent.
Protocols should explicitly define the time scale, units, and leap second/correction strategies; passing a single integer named timestamp is insufficient.
5. PTP, PHC, and Hardware Timestamps
PHC is the PTP Hardware Clock on the device; Linux provides access via /dev/ptpN. Network cards timestamp closer to the transmit/receive hardware, reducing uncertainty caused by software queuing; the PTP protocol and user-space services then use these measurements to synchronize corresponding clocks.
The network card PHC and system REALTIME are different clock objects; installing ptp4l does not guarantee they will automatically become fully consistent. Tools like phc2sys are responsible for synchronizing between corresponding clocks, and configurations must specify which is the reference and which is adjusted.
The software, conversion, and raw hardware timestamp fields and flags of SO_TIMESTAMPING must be interpreted according to the interface. Nanosecond storage units do not guarantee nanosecond synchronization accuracy; actual performance is also affected by the network card, switch, link, configuration, and temperature. See PTP clock and Network Timestamping.
6. vDSO is a Fast Read Path, Not a New Time Scale
The kernel maps the vDSO ELF to user processes, allowing libc to call clock functions within it. Coupled with kernel-published data pages and count sources readable in user space, common clock queries can avoid kernel traps every time.
This still relies on time parameters and consistency protocols maintained by the kernel. If the target architecture, clocksource, or clock ID is not supported by the fast path, it may fall back to a syscall. Not seeing a clock_gettime syscall in strace does not mean the program is not reading time.
Syscall entry into the kernel involves privilege level transitions and does not necessarily cause a task context switch. Reading costs must be measured; one cannot fixate on promises of 20 ns for vDSO and 100 ns for syscalls. The x86 path should not be pseudonymously written as calling ARM's __arch_counter_get_cntvct(). See vDSO.
7. Hardware Capabilities Do Not Equal Final Time Quality
TSC, HPET, ACPI PM timer, ARM Generic Timer, and virtualization paravirtual clocks have different access, frequency, and stability attributes. TSC's invariant capabilities help stabilize timing, but cross-CPU synchronization, firmware, virtualization, and migration still require kernel judgment; one cannot directly use the inverse of CPU frequency as a measure of accuracy.
The clocksource watchdog and kernel validation can detect unreliable sources and switch. Forcing a choice of a "theoretically faster" source may reduce reliability; read-only observation is the first step to understanding the current configuration.
These clocks are read sequentially; one cannot directly treat the difference between adjacent printed values as a precise offset. The example also does not modify clocks or trigger suspend. clock_getres() provides resolution information but does not prove current UTC error or actual wakeup jitter.
8. Order of Checks for Time Measurement
First, confirm that you are comparing the same time scale and the same machine/boot cycle; then confirm whether suspend, correction, and virtualization pauses should be counted. When periodic execution is required, check absolute/relative deadlines and catch-up rules; when cross-machine comparison is needed, confirm synchronization errors; finally, analyze vDSO and hardware reading overhead.