Loading content...
A process is more than just executing code. It is a complex entity with numerous characteristics that the operating system must track and manage. These characteristics define everything about the process: who owns it, what resources it has, how it should be scheduled, what it can access, and what state it's currently in.
Understanding process characteristics is essential for:
In this page, we systematically examine every significant characteristic that defines a process.
By the end of this page, you will understand the complete set of characteristics that define a process, how each characteristic affects process behavior, where this information is stored in the kernel, and how to observe these characteristics on a running system.
Every process has a unique identity that distinguishes it from all other processes in the system. These identity characteristics are assigned at process creation and remain stable throughout the process's lifetime.
| Characteristic | Description | Example Value |
|---|---|---|
| Process ID (PID) | Unique integer identifying the process | 12345 |
| Parent Process ID (PPID) | PID of the parent process | 1200 |
| Process Group ID (PGID) | ID of the process group (for job control) | 12345 |
| Session ID (SID) | ID of the controlling session | 1100 |
| User ID (UID) | Owner's user ID (who started the process) | 1000 |
| Effective UID (EUID) | User ID used for permission checks | 1000 or 0 for setuid |
| Saved Set-UID | UID saved for privilege switching | 0 |
| Group ID (GID) | Primary group of the process | 100 |
| Effective GID (EGID) | Group used for permission checks | 100 |
| Supplementary Groups | Additional groups the process belongs to | [100, 101, 102] |
Real vs Effective IDs:
The distinction between real and effective IDs is crucial for security:
Normally, real and effective IDs are the same. But setuid/setgid programs have different effective IDs, allowing them to temporarily gain elevated privileges.
12345678910111213141516171819
# View identity of current shell$ iduid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),100(users) # Detailed view of a specific process$ cat /proc/self/status | grep -E "^(Pid|PPid|Uid|Gid|Groups)"Pid: 12345PPid: 1200Uid: 1000 1000 1000 1000 # Real, Effective, Saved, FSGid: 1000 1000 1000 1000Groups: 27 100 1000 # The passwd command is setuid root$ ls -l /usr/bin/passwd-rwsr-xr-x 1 root root 68208 Mar 14 2023 /usr/bin/passwd# ^ 's' indicates setuid bit # When running passwd, effective UID is 0 (root) but real UID is 1000# This allows the program to modify /etc/shadowModern Linux extends the UID/GID model with capabilities - fine-grained privileges like CAP_NET_BIND_SERVICE (bind to ports < 1024) or CAP_SYS_ADMIN (various admin operations). A process can have specific capabilities without full root access, following the principle of least privilege.
A process's state describes what it's currently doing. The state determines whether the process is eligible to run, waiting for something, or has finished executing.
| State | Symbol | Meaning |
|---|---|---|
| Running | R | Currently executing on a CPU or ready to run |
| Sleeping (Interruptible) | S | Waiting for an event; can be woken by signals |
| Sleeping (Uninterruptible) | D | Waiting for I/O; cannot be interrupted (not even by kill) |
| Stopped | T | Execution suspended (e.g., by SIGSTOP or debugger) |
| Zombie | Z | Terminated but parent hasn't collected exit status |
| Dead | X | Should never be seen; process is being removed |
| Idle | I | Kernel thread waiting for work |
1234567891011121314151617181920212223242526
# View process states$ ps aux | head -5USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDroot 1 0.0 0.1 168956 12520 ? Ss Jan01 10:23 /sbin/initroot 2 0.0 0.0 0 0 ? S Jan01 0:01 [kthreadd]alice 12345 0.5 1.2 512320 48000 pts/0 Sl 10:00 0:30 python3 app.py # STAT column decoded:# S = Sleeping (interruptible)# s = Session leader# l = Multi-threaded (using CLONE_THREAD)# + = In the foreground process group # Create processes in different states$ sleep 100 & # S (sleeping, waiting for timer)$ cat < /dev/zero & # R (running, reading continuously)$ sudo dd if=/dev/sda ... # D (uninterruptible, waiting for disk I/O) # Stop a process$ kill -STOP 12345 # Process enters T (stopped) state$ ps -p 12345 -o statSTATT # Resume the process$ kill -CONT 12345 # Process returns to S or R stateProcesses in uninterruptible sleep (D state) cannot be killed, even with SIGKILL. This state is typically used during disk I/O where interruption would cause data corruption. If a process is stuck in D state (e.g., due to an unresponsive NFS mount), it cannot be terminated until the I/O completes or times out. This is intentional but can be frustrating when things go wrong.
The operating system uses scheduling characteristics to decide which process runs next and for how long. These characteristics influence process responsiveness and CPU allocation.
| Characteristic | Description | Range/Values |
|---|---|---|
| Scheduling Policy | Algorithm used to schedule this process | SCHED_OTHER, SCHED_FIFO, SCHED_RR |
| Nice Value | User-adjustable priority (-20=highest, 19=lowest) | -20 to +19 |
| Static Priority | Base priority derived from nice value | 100 to 139 (for SCHED_OTHER) |
| Dynamic Priority | Adjusted by scheduler based on behavior | Varies dynamically |
| CPU Affinity | Which CPUs the process can run on | Bitmask of allowed CPUs |
| Time Slice | How long before preemption (RR/FIFO) | Milliseconds |
| vruntime | Virtual runtime (CFS scheduler) | Nanoseconds of 'fair' time |
Understanding Nice Values:
The 'nice' value represents how 'nice' a process is to others. A positive nice value means the process is willing to give up CPU time; a negative value means it wants more CPU time.
Only root can set negative nice values (increase priority).
12345678910111213141516171819202122232425262728293031
# View nice value and priority$ ps -eo pid,ni,pri,comm | head -5 PID NI PRI COMMAND 1 0 20 systemd 123 -20 40 important # High priority (low nice) 456 10 10 background # Low priority (high nice) 789 0 20 normal # Start a process with specific nice value$ nice -n 10 ./slow_backup.sh & # Lower priority$ nice -n -5 ./critical_service & # Higher priority (needs root) # Change nice value of running process$ renice -n 5 -p 12345 # Make process nicer$ renice -n -10 -p 12345 # Make less nice (needs root) # View scheduling policy$ chrt -p 12345pid 12345's current scheduling policy: SCHED_OTHERpid 12345's current scheduling priority: 0 # Set real-time scheduling (needs root)$ sudo chrt -f -p 50 12345 # FIFO priority 50$ sudo chrt -r -p 25 12345 # Round-Robin priority 25 # View and set CPU affinity$ taskset -p 12345pid 12345's current affinity mask: f # All 4 CPUs (0xf = 1111) $ taskset -p 0x3 12345 # Restrict to CPUs 0,1 only$ taskset -c 0,1 ./myprogram # Start on CPUs 0,1SCHED_FIFO and SCHED_RR are real-time policies. Processes with these policies have priority over all normal (SCHED_OTHER) processes. A runaway real-time process can starve the entire system, including the shell you'd use to kill it. Use with extreme caution.
Each process has a rich set of memory characteristics describing its address space, memory usage, and memory limits.
| Characteristic | Description | Typical Value |
|---|---|---|
| Virtual Size (VSZ) | Total virtual address space mapped | 256 MB - 10+ GB |
| Resident Set Size (RSS) | Physical memory actually in use | 10 MB - 1+ GB |
| Shared Memory | Memory shared with other processes | Libraries, shared segments |
| Code Size (Text) | Size of executable code | 1 KB - 100+ MB |
| Data Size | Size of data segment (heap + data) | Varies greatly |
| Stack Size | Current stack usage | 100 KB typical |
| Page Faults (Minor) | Pages allocated without disk I/O | Count since start |
| Page Faults (Major) | Pages requiring disk I/O | Count since start |
Understanding VSZ vs RSS:
This distinction is crucial for understanding memory usage:
VSZ (Virtual Size): Everything mapped into the process's address space, including:
RSS (Resident Set Size): Physical RAM currently allocated, including:
A process might have VSZ=10GB but RSS=100MB if most pages aren't actually used.
1234567891011121314151617181920212223242526272829303132333435363738
# Basic memory view$ ps aux | grep pythonUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDalice 12345 2.3 1.5 825432 61440 pts/0 Sl 10:00 1:30 python3 app.py# ^^^^^^ ^^^^^# VSZ(KB) RSS(KB) # Detailed memory breakdown from /proc$ cat /proc/12345/status | grep -E "^(Vm|Rss)"VmPeak: 850000 kB # Peak virtual memory sizeVmSize: 825432 kB # Current virtual memory sizeVmLck: 0 kB # Locked memory (cannot be swapped)VmPin: 0 kB # Pinned memoryVmHWM: 64000 kB # Peak RSS ("high water mark")VmRSS: 61440 kB # Current RSSRssAnon: 51200 kB # Anonymous (non-file) pagesRssFile: 9240 kB # File-backed pagesRssShmem: 1000 kB # Shared memory pagesVmData: 102400 kB # Data segment sizeVmStk: 1024 kB # Stack sizeVmExe: 2048 kB # Executable code sizeVmLib: 32768 kB # Shared library size # View memory mappings$ cat /proc/12345/maps | head -1000400000-00600000 r--p 00000000 08:01 12345 /usr/bin/python300600000-00800000 r-xp 00200000 08:01 12345 /usr/bin/python300800000-00900000 r--p 00400000 08:01 12345 /usr/bin/python300a00000-00a10000 rw-p 00500000 08:01 12345 /usr/bin/python301000000-01500000 rw-p 00000000 00:00 0 [heap]7f0000000000-7f0000100000 rw-p 00000000 00:00 0 [stack thread 2]... # View proportional set size (more accurate for shared memory)$ sudo cat /proc/12345/smaps_rollupRss: 61440 kBPss: 45000 kB # Proportional: shared pages divided by sharers...Proportional Set Size (PSS) divides shared memory by the number of processes sharing it. If 5 processes share a 10 MB library, each is credited with 2 MB in PSS. This gives a more accurate picture of 'true' memory usage per process.
Every process has file system characteristics that define its view of the file system and the files it has open.
| Characteristic | Description | Example |
|---|---|---|
| Current Working Directory | Default directory for relative paths | /home/alice/project |
| Root Directory | Process's view of root (for chroot) | / or /container/root |
| Umask | Default permission mask for new files | 022 (creates 755/644) |
| Open File Descriptors | List of open files, pipes, sockets | 0=stdin, 1=stdout, 3=file |
| File Descriptor Limit | Maximum number of open files | 1024 (soft), 1048576 (hard) |
| File Locks | Advisory or mandatory locks held | Varies |
The File Descriptor Table:
Each process has a file descriptor table mapping small integers (file descriptors) to kernel file objects. The first three are conventionally:
Additional file descriptors are assigned as files are opened.
12345678910111213141516171819202122232425262728293031
# View current working directory and root$ ls -la /proc/12345/cwd # Symlink to current directorylrwxrwxrwx 1 alice alice 0 Jan 15 10:00 /proc/12345/cwd -> /home/alice/project $ ls -la /proc/12345/root # Symlink to root directorylrwxrwxrwx 1 alice alice 0 Jan 15 10:00 /proc/12345/root -> / # View open file descriptors$ ls -la /proc/12345/fd/total 0lrwx------ 1 alice alice 64 Jan 15 10:00 0 -> /dev/pts/0lrwx------ 1 alice alice 64 Jan 15 10:00 1 -> /dev/pts/0lrwx------ 1 alice alice 64 Jan 15 10:00 2 -> /dev/pts/0lr-x------ 1 alice alice 64 Jan 15 10:00 3 -> /home/alice/data.csvl-wx------ 1 alice alice 64 Jan 15 10:00 4 -> /home/alice/output.loglrwx------ 1 alice alice 64 Jan 15 10:00 5 -> socket:[123456]lrwx------ 1 alice alice 64 Jan 15 10:00 6 -> pipe:[789012] # View more details about file descriptors$ ls -la /proc/12345/fdinfo/3pos: 1024 # Current file position (seek offset)flags: 0100000 # Open flags (O_RDONLY, O_LARGEFILE)mnt_id: 30 # Mount namespace ID # View file descriptor limits$ cat /proc/12345/limits | grep "open files"Max open files 1024 1048576 files # Count open file descriptors$ ls /proc/12345/fd | wc -l7Opening files without closing them causes file descriptor leaks. When a process hits its limit, further open() calls fail with EMFILE ('too many open files'). This is a common bug in long-running servers. Always close files explicitly, or use resource managers (with/using blocks) that close automatically.
The operating system tracks time and resource usage for each process. This information is used for accounting, billing, debugging, and performance analysis.
| Characteristic | Description | Unit |
|---|---|---|
| Start Time | When the process was created | Timestamp |
| User CPU Time | Time spent running user code | Clock ticks or seconds |
| System CPU Time | Time spent in kernel on behalf of process | Clock ticks or seconds |
| Children User Time | User time of terminated children | Clock ticks |
| Children System Time | System time of terminated children | Clock ticks |
| Wall-Clock Time | Real time since process started | Seconds |
| Voluntary Context Switches | Times process yielded CPU voluntarily | Count |
| Involuntary Context Switches | Times process was preempted | Count |
| I/O Bytes Read/Written | Data read from and written to storage | Bytes |
CPU Time Breakdown:
When we say a process used '10 seconds of CPU time,' we're usually referring to the sum of:
A process that makes many system calls (file I/O, network) will have higher system time. A process that computes intensively will have higher user time.
12345678910111213141516171819202122232425262728293031323334353637
# View CPU time (time command)$ /usr/bin/time -v ./my_programCommand being timed: "./my_program"User time (seconds): 5.23System time (seconds): 0.45Percent of CPU this job got: 98%Elapsed (wall clock) time: 0:05.78Maximum resident set size (kbytes): 54320Voluntary context switches: 142Involuntary context switches: 23 # View from /proc$ cat /proc/12345/stat | awk '{print "User:", $14/100, "sec, System:", $15/100, "sec"}'User: 5.23 sec, System: 0.45 sec # Detailed timing$ cat /proc/12345/status | grep -E "^(S|P)"... # View I/O accounting$ cat /proc/12345/iorchar: 123456789 # Characters read (includes cache hits)wchar: 987654321 # Characters writtensyscr: 5000 # Read system callssyscw: 3000 # Write system calls read_bytes: 50000000 # Actual bytes read from storagewrite_bytes: 30000000 # Actual bytes written to storage # View start time$ stat /proc/12345 | grep AccessAccess: 2024-01-15 10:00:35.123456789 +0000 # ps with elapsed and cumulative time$ ps -o pid,etime,time,comm -p 12345 PID ELAPSED TIME COMMAND12345 01:30:45 00:05:03 python3# wall time CPU timeThe ratio of voluntary to involuntary context switches reveals process behavior. High voluntary switches indicate I/O-bound work (process often waits for data). High involuntary switches indicate CPU-bound work competing for the processor. This insight helps with performance tuning.
Processes have signal characteristics that define how they respond to asynchronous notifications from the kernel or other processes.
| Characteristic | Description | Format |
|---|---|---|
| Signal Handlers | Custom handlers registered for each signal | Function pointers |
| Pending Signals | Signals delivered but not yet handled | Bitmask |
| Blocked Signals | Signals temporarily blocked (masked) | Bitmask |
| Ignored Signals | Signals with handler set to SIG_IGN | Handler registration |
| Signal Queue | Pending real-time signals (can queue) | Queue of siginfo |
Common Signals:
1234567891011121314151617181920212223242526
# View signal state from /proc$ cat /proc/12345/status | grep SigSigQ: 0/63432 # Queued signals / maxSigPnd: 0000000000000000 # Pending signals (per-thread)ShdPnd: 0000000000000000 # Shared pending (per-process)SigBlk: 0000000000010000 # Blocked signalsSigIgn: 0000000000001000 # Ignored signals SigCgt: 0000000180014003 # Caught signals (has handler) # Decode the bitmask (bit position = signal number)# SigBlk: 0x10000 = bit 17 set = SIGCHLD blocked# SigIgn: 0x1000 = bit 13 set = SIGPIPE ignored# SigCgt: multiple bits = multiple handlers registered # See what signals a process catches$ gdb -p 12345 -batch -ex 'info signals'Signal Stop Print Pass to program DescriptionSIGHUP Yes Yes Yes HangupSIGINT Yes Yes No Interrupt (caught, not passed)SIGQUIT Yes Yes Yes Quit... # Send a signal and observe$ kill -SIGUSR1 12345 # Send signal$ cat /proc/12345/status | grep SigPndShdPnd: 0000000000000400 # Bit 10 set = SIGUSR1 pending (if blocked)Traditional signals don't queue—if SIGUSR1 is delivered twice before handled, it appears once. Real-time signals (SIGRTMIN+n) do queue and carry additional data. They're useful for inter-process communication where every signal matters.
Every process operates under resource limits—boundaries that constrain its consumption of system resources. These limits prevent runaway processes from destabilizing the system.
| Resource | Description | Typical Default |
|---|---|---|
| RLIMIT_NOFILE | Maximum open file descriptors | 1024 / 1048576 |
| RLIMIT_NPROC | Maximum number of processes for user | 63432 |
| RLIMIT_AS | Maximum address space (virtual memory) | Unlimited |
| RLIMIT_DATA | Maximum data segment size | Unlimited |
| RLIMIT_STACK | Maximum stack size | 8 MB |
| RLIMIT_CORE | Maximum core dump file size | 0 (disabled) |
| RLIMIT_CPU | Maximum CPU time in seconds | Unlimited |
| RLIMIT_FSIZE | Maximum file size creatable | Unlimited |
| RLIMIT_MEMLOCK | Maximum locked memory | 64 KB |
Soft vs Hard Limits:
Each resource has two limits:
This allows processes to self-limit while still having room to increase if needed.
12345678910111213141516171819202122232425262728293031323334353637383940
# View limits for current shell$ ulimit -acore file size (blocks, -c) 0data seg size (kbytes, -d) unlimitedscheduling priority (-e) 0file size (blocks, -f) unlimitedpending signals (-i) 63432max locked memory (kbytes, -l) 64max memory size (kbytes, -m) unlimitedopen files (-n) 1024pipe size (512 bytes, -p) 8POSIX message queues (bytes, -q) 819200real-time priority (-r) 0stack size (kbytes, -s) 8192cpu time (seconds, -t) unlimitedmax user processes (-u) 63432virtual memory (kbytes, -v) unlimitedfile locks (-x) unlimited # View limits for a specific process$ cat /proc/12345/limitsLimit Soft Limit Hard Limit UnitsMax cpu time unlimited unlimited secondsMax file size unlimited unlimited bytesMax data size unlimited unlimited bytesMax stack size 8388608 unlimited bytesMax core file size 0 unlimited bytesMax open files 1024 1048576 filesMax processes 63432 63432 processes # Increase soft limit (within hard limit)$ ulimit -n 65536 # Increase open files to 65536 # Set limit for a command$ ulimit -c unlimited # Enable core dumps$ ./program_that_might_crash # Permanently change limits (/etc/security/limits.conf)# alice soft nofile 65536# alice hard nofile 1048576While rlimits apply to individual processes, cgroups (control groups) can limit resource usage for groups of processes—the foundation of container resource management. Docker uses cgroups to enforce CPU and memory limits on containers.
We have surveyed the complete landscape of process characteristics—the attributes that define every running process. Let's consolidate these insights:
What's Next:
Now that we understand what characteristics define a process, we'll examine the process lifecycle—how processes are created, how they transition between states, and how they terminate. This dynamic view complements our static understanding of process characteristics.
You now have a comprehensive understanding of process characteristics. This knowledge is essential for debugging, performance tuning, security analysis, and systems programming. You can now inspect any running process and understand its complete state.