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.
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.
Kernel Parameters for Threads and Processes
ulimit limits: run
ulimit -aon 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-limitwhen starting a container to limit container‑level PID total.
Kubelet: enable the SupportPodPidsLimit feature and set
--pod-max-pidsto 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.maxholds 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-pidsoption, 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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.