advanced25 min studyLesson 341 in Engineering

Container init: signals, children, and the lifetime of PID 1

Trace namespace init, orphan adoption, wait-based reaping, and shutdown without assuming that a shell wrapper behaves like an init process.

On this page

What you’ll understand

  • Distinguish a host PID from PID 1 inside a new PID namespace.
  • Explain orphan adoption and why exited children still need collection.
  • Design a shutdown argument that includes signal forwarding and namespace-init death.

A container entrypoint can become PID 1 without being designed to supervise anything. It may launch workers, leave orphaned grandchildren, or ignore the termination signal expected by an orchestrator. These behaviors are consequences of Linux process and PID-namespace semantics, not properties of an image label. This lesson focuses on Linux 6.12 PID namespaces with an init process and ordinary wait-based child collection. A container that shares a host PID namespace has a different process view and must not inherit this lesson's PID-1 assumptions blindly.

The namespace's first process has a special role

The first process created in a new PID namespace receives PID 1 there, while it can have a different PID in an ancestor namespace. Entering a PID namespace with the relevant interfaces affects subsequently created children rather than renumbering an already running process in place. Mounting a suitable proc filesystem also matters to the process view: a PID namespace and an inherited proc mount are separate configuration choices. Record which namespace each observed identifier belongs to before interpreting a process listing.

When a process exits, orphaned descendants are adopted by the appropriate surviving subreaper or namespace init according to Linux's parentage rules. A zombie is an exited child whose status has not yet been collected, not a running task still doing useful computation. The responsible parent uses wait-family operations to collect eligible children. One SIGCHLD notification is not a reliable one-child counter because ordinary signals can coalesce. A supervisor must drain available wait results correctly, including interrupted calls and no-more-ready-child conditions.

Zafter=Zbefore+E−RZ_{\rm after}=Z_{\rm before}+E-R
For a finite observation interval, Z counts waitable exited children, E new exits requiring collection, and R collected statuses. Counts are dimensionless integers under the stated wait-based policy, with no automatic-discard behavior assumed. This accounting is not a model of signal delivery.↔ Scroll wide equations horizontally.

Shutdown requires handlers and an explicit forwarding policy

Namespace init has special signal handling rules. Other members cannot simply rely on an unhandled default-action termination signal killing PID 1. An init or supervisor should deliberately install appropriate handlers and define how a requested stop reaches the actual application and its children. A shell that starts a child without replacing itself may remain the signal target. Using exec for a simple single-process entrypoint changes that relationship, but does not automatically teach the application to reap orphaned children or supervise a process tree.

An ancestor PID namespace can forcibly deliver SIGKILL or SIGSTOP to the child namespace's init subject to the documented permission rules. If namespace init dies, Linux kills the remaining processes in that PID namespace; keeping a namespace file descriptor does not make its dead init reusable. This is different from a single worker dying while the supervisor remains alive. A graceful stop policy should allow necessary cleanup before escalation, then collect statuses and report the intended result rather than treating every disappearance as successful completion.

Tstop≥Tforward+Tcleanup+TcollectT_{\rm stop}\ge T_{\rm forward}+T_{\rm cleanup}+T_{\rm collect}
This illustrative sequential stop budget uses nonnegative durations in seconds for signal forwarding, application cleanup, and final status collection. It is a planning lower bound under the assumed sequence, not a guarantee of Linux scheduling or a universal additive runtime implementation.↔ Scroll wide equations horizontally.

Test child lifecycle independently from service readiness

A useful fixture launches a child that forks a short-lived grandchild, then exercises ordinary exit and termination while observing parentage and wait results. Check that intended workers receive the stop request, no unexpected child remains running, and collection does not leave an accumulating zombie count. Include an application that ignores its first stop request to test escalation. A readiness endpoint returning success proves none of those lifecycle properties. Match the runtime's actual signal target and process-group behavior rather than assuming every descendant receives every signal.

The queue lab offers a limited collection-pressure analogy. Reset, explicitly set arrival four and service three, predict growth from four pending statuses, then Play or Step. For a separate drain experiment Reset and explicitly set arrival zero and service two before predicting and playing. A rate edit retains elapsed time and recomputes from the initial four items. This continuous fluid model contains no PIDs, SIGCHLD, wait syscall, process groups, or init-death behavior; use the actual fixture to test those mechanisms.

Collect every ready status after one notification

Assume ordinary wait-based child collection with no SIGCHLD ignore or automatic-discard policy. A supervisor already has four waitable exited children. Seven more exit before its handler runs, and its first collection pass obtains eight statuses. How many remain?

  1. The pending total before collection is four plus seven, or eleven waitable statuses. Seven exits do not imply seven separately queued SIGCHLD deliveries.

  2. Collecting eight statuses leaves eleven minus eight, or three. The supervisor should continue its proper wait loop while further statuses are available rather than assuming one handler invocation represents exactly one child.

  3. After collecting the remaining three, the pending count is zero for this finite trace. A new exit after that observation can create more work, so the event-loop and signal-masking design must avoid losing the transition back to waiting.

The result

Three statuses remain after the first pass. Correct reaping follows wait results and process state, not a count of signal deliveries.

Explore the model

Interactive lab

Interactive lab · queue

Pending collection work is only a fluid analogy

Use separate Reset → explicitly set both rates → predict → Play or Step experiments. Compare (λ,μ)=(4,3) with Reset plus (0,2). Edits retain elapsed t and recompute from four initial items. The model has no child identities, wait calls, signal coalescing, or PID-1 semantics.

Pending collection work is only a fluid analogyFluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Current readings: Fluid backlog: 4; Net rate · λ − μ: −1 /s; M/M/1 mean residence: 1 s. All parameters are adjustable with the labeled controls below.02.557.51001.534.56Elapsed time · sFluid backlogλ < μ
Fluid backlogCurrent time
Fluid backlog
4
Net rate · λ − μ
−1 /s
M/M/1 mean residence
1 s
t = 0 s

Fluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forA collection backlog can grow when processing falls behind, but real reaping correctness requires a process-aware wait protocol.

Check your understanding

Several children exit before one SIGCHLD handler invocation. What should a wait-based supervisor assume?

Try it yourself

A shutdown budget is five seconds. Under an explicitly sequential plan, forwarding takes 0.1 seconds, application cleanup takes 3.8 seconds, and final collection takes 0.4 seconds. What modeled margin remains? Would placing a shell in front of the application prove that forwarding and reaping work?

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Add the durations, then separate the budget arithmetic from the entrypoint's actual process behavior.

Compare with the solution

The modeled total is 4.3 seconds, leaving 0.7 seconds. This margin does not account for unexpected scheduling or blocked cleanup. A shell wrapper does not itself establish the needed handlers, forwarding policy, process-group targeting, or child collection. Inspect the actual process tree and test stop behavior. If namespace init dies, remaining namespace processes are killed; that is a distinct forced lifetime event, not proof that the application's graceful cleanup completed.

Take it with you

  • PID 1 is a namespace-specific supervisory role, not just a low process number.
  • Collect child status from wait results rather than counting SIGCHLD notifications.
  • Signal forwarding, graceful cleanup, and init death have different observable consequences.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.