Fundamentals 10 min read

Understanding Linux Processes, Threads, and Container Limits

This article explains Linux processes and threads, their relationship, key kernel parameters like ulimit, threads‑max, and pid_max, and how to limit thread and PID usage in containers using cgroups, Docker, and Kubernetes to prevent resource exhaustion.

Ops Development Stories
Ops Development Stories
Ops Development Stories
Understanding Linux Processes, Threads, and Container Limits

Process

Process is a program with a certain independent function that runs on a data set. It is the basic unit of execution in an operating system. In traditional OS, a process is both the basic allocation unit and the basic execution unit. The concept includes two points: first, a process is an entity with its own address space, usually consisting of a text region (code), data region (variables and dynamically allocated memory), and stack region (call instructions and local variables). Second, a process is a “program in execution”. A program is lifeless until the CPU gives it life, at which point it becomes a process.

Thread

Thread is the smallest unit of scheduling that an operating system can perform. It resides within a process and is the actual execution unit. A thread represents a single sequential control flow in a process; a process can have multiple concurrent threads, each executing different tasks. In Unix System V and SunOS it is also called a lightweight process, but lightweight processes usually refer to kernel threads, while user threads are called threads. Relationship between process and thread: threads in the same process share all system resources such as virtual address space, file descriptors, and signal handling, but each thread has its own call stack, register context, and thread‑local storage.

Linux Thread and Process

In the Linux kernel, processes and threads are both tasks but should be distinguished. pid is the process ID, tgid is the thread group ID. If a process has only the main thread, its pid and tgid are the same and group_leader points to itself. When a process creates additional threads, each thread gets its own pid, while tgid remains the pid of the main thread, and group_leader points to the main thread. Thus tgid tells whether a task_struct represents a process or a thread.

Process and thread relationship diagram
Process and thread relationship diagram

Kernel Parameters for Threads and Processes

ulimit limits: run

ulimit -a

on Linux to see resource limits. The “max user processes” entry shows the maximum number of threads a process can create; it can be changed:

<code>ulimit -u 66535</code>

2. sys.kernel.threads-max limits the total number of threads in the system. View it with:

<code>cat /proc/sys/kernel/threads-max
32768</code>

Modify it with:

<code># Method 1, temporary (lost after reboot)
echo 65535 > /proc/sys/kernel/threads-max
# Method 2, permanent
echo "kernel.threads-max = 65535" >> /etc/sysctl.conf
</code>

3. sys.kernel.pid_max limits the total number of PIDs. On 32‑bit systems the maximum is 32768 and cannot be changed; on 64‑bit systems the maximum is 2^22. The kernel sets pid_max based on CPU count (≤32 CPUs → 32768, otherwise N*1024). View it with:

<code>cat /proc/sys/kernel/pid_max
32768</code>

Modify it similarly:

<code># Method 1, temporary
echo 65535 > /proc/sys/kernel/pid_max
# Method 2, permanent
echo "kernel.pid_max = 65535" >> /etc/sysctl.conf
</code>

Note: each thread consumes a PID, so threads‑max must be ≤ pid_max.

Container Thread Limits

In Linux, a container is a collection of processes. If a container creates too many processes or has bugs (e.g., a Java task that spawns a thread without proper cleanup), it can behave like a fork bomb, exhausting the host’s PID table and causing “java.lang.OutOfMemoryError: Unable to create native threads” and “Resource temporarily unavailable” errors. Besides fixing the application bug, the system must also limit the number of threads per container.

cgroup

cgroup isolates PIDs; by adjusting Docker or Kubelet configuration, the total PID count can be limited, thereby limiting thread count.

Docker: set

--pids-limit

when starting a container to limit container‑level PID total.

Kubelet: enable the SupportPodPidsLimit feature and set

--pod-max-pids

to limit the PID total per pod.

When a container is created, the service that creates it makes a subdirectory under

/sys/fs/cgroup/pids

. The key file

pids.max

holds the maximum allowed processes. Docker or Kubelet writes a value to this file. In Kubernetes, each node runs a Kubelet service that manages container lifecycle; the official documentation “Process ID Limits And Reservations” describes how to configure the

--pod-max-pids

option, after which all containers on that node will have their process count limited by the cgroup PID controller.

Summary

Linux uses ulimit to restrict resource usage such as file descriptors, thread count, and memory. In containerized environments, similar limits are needed. Since PID is a critical resource, it must be constrained to ensure reasonable resource utilization. Docker has no default PID limit; Kubernetes can limit threads by enabling SupportPodPidsLimit and setting a pod‑level PID limit.

linuxthreadcontainerProcesscgroupulimit
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.