How Linux Time Command Measures Execution Time
This article explains how the Linux time command
calculates the runtime of scripts and programs by interfacing directly
with the Linux kernel. It covers the difference between the shell
built-in and external binary, how system calls capture process
statistics, and the technical mechanics behind the real, user, and
system time metrics reported in execution summaries.
The Built-in vs. External Binary
Linux environments generally provide two versions of the
time utility: the shell built-in (standard in Bash, Zsh,
and others) and the external GNU binary located at
/usr/bin/time.
While their output formatting differs—the GNU binary offers detailed
resource profiling via the -v flag—both rely on the same
fundamental kernel facilities to monitor process execution and gather
performance metrics.
Process Spawning and State Tracking
When you execute a script using time ./script.sh, the
command does not simply read a wall clock before and after execution.
Instead, it acts as a parent process that monitors the target script
through standard POSIX process-management workflows:
- Forking: The
timecommand creates a child process using thefork()system call. - Execution: The child process replaces its address
space with the target script or interpreter using the
execve()system call. - Observation: The parent process pauses its own
execution and waits for the child process to terminate using system
calls such as
wait4()orwaitid().
The Role of
wait4() and getrusage()
The primary mechanism for measuring execution duration is the
wait4() system call. This call suspends the calling process
until the specified child process changes state (such as
terminating).
When the child process exits, wait4() collects its
termination status along with a populated rusage (resource
usage) structure. The Linux kernel continuously tracks the resource
metrics of every task in its internal process control block
(task_struct). When wait4() returns, this
accumulated data is delivered directly to the time process,
eliminating the overhead of external polling.
Breaking Down the Output Metrics
The time command aggregates and reports three primary
temporal metrics:
1. Real Time (Wall-Clock Time)
Real time (often labeled real or elapsed)
represents the total elapsed human time from the moment the command is
launched until it exits.
- Mechanism: The utility samples the system's
high-resolution monotonic clock (via
clock_gettime(CLOCK_MONOTONIC)) immediately before forking the child and immediately afterwait4()returns. - Factors: This duration includes CPU execution, time spent waiting for storage or network I/O, and delays caused by system context switching or CPU throttling.
2. User CPU Time
User time (labeled user) reflects the exact duration the
CPU spent executing the target program's instructions in user space.
- Mechanism: The kernel tallies time spent running application code outside of kernel privileges. It excludes time spent waiting for file system operations, sleep calls, or kernel operations.
- Accounting: Multi-threaded processes running across multiple cores will report aggregated user time, which can exceed the elapsed real time.
3. System CPU Time
System time (labeled sys) represents the amount of time
the CPU spent executing code inside the Linux kernel on behalf of the
process.
- Mechanism: Whenever a script triggers a system call
(such as allocating memory with
brk/mmap, reading files viaread, or writing to stdout viawrite), execution switches to kernel mode. The kernel charges these execution ticks to the process's system-time counter.
CPU Tick Accounting and High-Resolution Timers
Internally, the Linux kernel determines user and system times using CPU tick accounting or high-resolution timers:
- Tick-Based Accounting: Traditionally, the kernel inspects the program counter at every periodic timer interrupt (jiffy). If the CPU was executing user code, the tick is added to the process's user counter; if executing a kernel routine, it is added to the system counter.
- Tickless/Dynamic Ticks (
CONFIG_NO_HZ): Modern Linux kernels use precise timestamp capture at context switches. When a thread yields, blocks, or transitions between user space and kernel space via interrupts or system calls, the kernel reads the CPU hardware time-stamp counter (TSC) to record exact nanosecond-level durations, yielding the high-precision results displayed by thetimeutility.