LinuxVI · ProcessesThreads
Threads and cgroup views
What you'll learn
- Distinguish a thread from a process
- Use ps -L and /proc/<pid>/task to inspect threads
- Understand how cgroup views expose resource accounting per-process
- Apply thread-level debugging for CPU-saturated processes
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
Many production processes are multithreaded. nginx, mysqld, Java applications, and most modern daemons have many threads sharing one address space. The kernel tracks each thread as a separate schedulable entity with its own TID; the set of threads is the process.
Threads from ps
$ ps -L -p 1234 PID LWP TID STAT TIME COMMAND
12345 12345 12345 Sl 00:00:00 nginx: master process
12345 12346 12346 Sl 00:00:00 nginx: worker process
12345 12347 12347 Sl 00:00:00 nginx: worker process
12345 12348 12348 Sl 00:00:00 nginx: worker processIllustrative output
$ ls /proc/12345/task/ | head12345
12346
12347
12348Illustrative output
Threads vs processes
| Property | Process | Thread |
|---|---|---|
| Address space | Independent | Shared |
| File descriptors | Independent | Shared (in most threads) |
| PID | One per process | One per thread (the TID) |
| Scheduling | Independent | Independent |
| Signal handlers | Independent | Process-wide |
| Resource accounting | Per-process | Per-thread (CPU time) but per-cgroup (memory) |
Resource accounting with cgroups v2
The kernel’s process accounting is split between two layers:
- Per-task (per-thread): CPU time, scheduling state, signals.
Visible in
/proc/<pid>/statandtop -H. - Per-cgroup: memory, I/O, PIDs, network. Visible in
/sys/fs/cgroup/<controller>/<path>/.
For a multi-threaded process, the memory and I/O accounting is shared across all threads (one cgroup). CPU time is per-thread.
$ cat /sys/fs/cgroup/system.slice/sshd.service/memory.current4194304Illustrative output
$ systemd-cgtopControl Group Tasks %CPU Memory
/ 234 12.5 1.2G
/system.slice 168 10.0 900M
/system.slice/sshd.service 4 0.1 16M
/user.slice/alice.service 12 1.2 200M
...Illustrative output
Inspecting thread-level CPU usage
$ top -H -p 1234top - 12:00:00 up 30 days, 3:45, 1 user, load average: 4.20, 3.10, 2.50
Threads: 4 total, 1 running, 3 sleeping
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12345 root 20 0 1234m 456m 123m R 95.0 1.1 0:42.15 nginx: master
12346 www-d 20 0 1234m 456m 123m S 2.0 1.1 0:01.20 nginx: worker
12347 www-d 20 0 1234m 456m 123m S 1.5 1.1 0:01.05 nginx: worker
12348 www-d 20 0 123m 45m 12m S 0.5 0.1 0:00.30 nginx: workerIllustrative output
Setting thread affinity
$ taskset -cp 1234; taskset -cp 0-3 1234pid 1234's current affinity mask: 0-3
pid 1234's current affinity mask: 0-3Illustrative output
Production use of cgroups
cgroups are how systemd enforces resource limits:
[Service]
CPUQuota=200%
MemoryMax=2G
IOWeight=100
These directives write to the cgroup’s cpu.max, memory.max,
and io.weight files. systemd translates the high-level directives
to the underlying kernel interface.
Knowledge check
Knowledge check · 3 questions
Q1. How are threads implemented in the Linux kernel?
Q2. CPU time is accounted per-thread; memory is accounted per-cgroup.
Q3. Which of the following statements about cgroups are correct? Select all that apply.
Passing score: 75%. Answers are checked in this browser.