Process Creation and Initialization
Relevant source files
This page documents how processes are created and initialized in the axprocess crate. We'll explore the creation of the init process, the ProcessBuilder pattern for process construction, and how child processes are created through forking. For information about the complete process lifecycle, including termination and cleanup, see Process Lifecycle.
Overview
In axprocess, all processes are created using a builder pattern that ensures proper initialization and establishment of hierarchical relationships. The system supports two primary creation paths:
- Creating the special "init process" (the first process in the system)
- Creating child processes by "forking" from existing parent processes
flowchart TD A["Process Creation"] B["Init Process Creation"] C["Child Process Creation"] D["ProcessBuilder::new_init()"] E["parent.fork()"] F["ProcessBuilder::build()"] G["New Process Instance"] A --> B A --> C B --> D C --> E D --> F E --> F F --> G
Sources: src/process.rs(L262 - L332)
The Init Process
The init process is the first process in the system and serves as the "root" of the process hierarchy. It has no parent and adopts orphaned processes when their parents exit.
Creating the Init Process
The init process is created using the Process::new_init
method and stored in a static variable for system-wide access.
sequenceDiagram participant ClientCode as "Client Code" participant ProcessBuilder as "ProcessBuilder" participant INIT_PROCstatic as "INIT_PROC (static)" participant NewSession as "New Session" participant NewProcessGroup as "New Process Group" ClientCode ->> ProcessBuilder: "Process::new_init(pid)" ClientCode ->> ProcessBuilder: "build()" ProcessBuilder ->> NewSession: "Session::new(pid)" ProcessBuilder ->> NewProcessGroup: "ProcessGroup::new(pid, session)" ProcessBuilder ->> INIT_PROCstatic: "INIT_PROC.init_once(process)" Note over INIT_PROCstatic: "Init process stored for<br>system-wide access"
Sources: src/process.rs(L262 - L272) src/process.rs(L301 - L331) src/process.rs(L334 - L341)
The Init Process Responsibilities
The init process has special responsibilities in the system:
- Cannot be terminated (the system enforces this)
- Adopts orphaned processes when their parents exit
- Provides the foundation for the process hierarchy
The code explicitly prevents the init process from exiting:
#![allow(unused)] fn main() { pub fn exit(self: &Arc<Self>) { if self.is_init() { panic!("init process cannot exit"); } // Exit code continues... } }
Sources: src/process.rs(L207 - L226) src/process.rs(L334 - L341)
The ProcessBuilder Pattern
The ProcessBuilder
struct provides a flexible way to configure and create new processes. It follows the builder pattern, allowing optional configurations before building the actual process.
ProcessBuilder Fields
Field | Type | Description |
---|---|---|
pid | Pid | Process identifier |
parent | Option<Arc | Parent process (None for init) |
data | Box<dyn Any + Send + Sync> | Custom data associated with the process |
Sources: src/process.rs(L285 - L289)
Process Construction Flow
sequenceDiagram participant ClientCode as "Client Code" participant ProcessBuilder as "ProcessBuilder" participant NewProcess as "New Process" participant ProcessGroup as "Process Group" ClientCode ->> ProcessBuilder: "new_init(pid) or fork(pid)" opt Configure Process ClientCode ->> ProcessBuilder: "data(custom_data)" end ClientCode ->> ProcessBuilder: "build()" ProcessBuilder ->> ProcessGroup: "Get parent's group or create new" ProcessBuilder ->> NewProcess: "Create process with necessary fields" ProcessBuilder ->> ProcessGroup: "Add process to group" alt Has Parent ProcessBuilder ->> NewProcess: "Add as child to parent" else No Parent (Init) ProcessBuilder ->> NewProcess: "Store as INIT_PROC" end ProcessBuilder -->> ClientCode: "Return Arc<Process>"
Sources: src/process.rs(L262 - L332)
Child Process Creation (Forking)
Child processes are created by "forking" from an existing parent process. This is done using the fork
method on a parent process.
Fork Process
flowchart TD A["Parent Process"] B["ProcessBuilder"] C["Configured Builder"] D["Child Process"] E["Parent's Process Group"] F["Parent-Child Relationship"] A --> B B --> C C --> D D --> E D --> F
Sources: src/process.rs(L275 - L281) src/process.rs(L301 - L331)
Inheritance During Forking
When a process is forked, the child process inherits several properties from its parent:
- Process Group: The child joins the parent's process group by default
- Parent Reference: The child maintains a reference to its parent
- Children Collection: The parent adds the child to its children collection
The code establishes these relationships during the build
method:
// Set parent-child relationship
if let Some(parent) = parent {
parent.children.lock().insert(pid, process.clone());
}
// Child inherits parent's group or creates new group for init
let group = parent.as_ref().map_or_else(
|| {
let session = Session::new(pid);
ProcessGroup::new(pid, &session)
},
|p| p.group(),
);
Sources: src/process.rs(L303 - L330)
Process Initialization Details
When a new process is created, several key initialization steps occur:
- Process Structure: A new
Process
structure is allocated with the provided PID - Zombie State: Set to
false
initially - Thread Group: Empty thread group is initialized
- Custom Data: Any provided custom data is stored
- Children Map: Empty children map is created
- Parent Reference: Weak reference to parent is stored (if any)
- Process Group: Process joins parent's group or creates a new group
- Registration: Process is registered with its group
Memory Management Strategy
Process creation uses a careful reference counting strategy to prevent memory leaks:
- Strong References (
Arc<Process>
):
- From parent to children
- From process group to processes
- From threads to their process
- Weak References (
Weak<Process>
):
- From child to parent (prevents reference cycles)
- From process group to processes in weak maps
flowchart TD subgraph subGraph0["Reference Relationship"] P1["Parent Process"] C1["Child Process"] PG["Process Group"] T1["Thread"] end C1 --> P1 C1 --> PG P1 --> C1 P1 --> PG PG --> C1 PG --> P1 T1 --> P1
Sources: src/process.rs(L301 - L331)
Practical Example
Here's how a process hierarchy might be created in code:
// Create init process with PID 0
let init = Process::new_init(0).build();
// Create a child process with PID 1
let child1 = init.fork(1).build();
// Create another child with PID 2 and custom data
let child2 = init.fork(2).data(MyCustomData { value: 42 }).build();
// Create a "grandchild" process with PID 3
let grandchild = child1.fork(3).build();
The resulting hierarchy would look like:
flowchart TD Init["Init Process (PID 0)"] Child1["Child Process (PID 1)"] Child2["Child Process (PID 2)with custom data"] Grandchild["Grandchild Process (PID 3)"] Child1 --> Grandchild Init --> Child1 Init --> Child2
Sources: src/process.rs(L262 - L332) tests/common/mod.rs(L15 - L28)
Implementation of ProcessBuilder::build
The build
method is the core of process initialization. It takes the builder's configuration and constructs a fully initialized process with proper relationships.
The method performs these key steps:
- Determines the process group (from parent or creates new one)
- Constructs the Process struct with all fields
- Adds the process to its group
- Establishes parent-child relationship or marks as init process
- Returns the new process wrapped in an Arc
Sources: src/process.rs(L301 - L331)
System Integration
The process creation system integrates with other components of axprocess:
- Thread Creation: After a process is created, threads can be added using
process.new_thread(tid)
- Process Group Management: Processes can create or join process groups after creation
- Session Management: Processes can create new sessions
- Parent-Child Relations: The system maintains a hierarchy for resource inheritance and cleanup
Sources: src/process.rs(L168 - L177) src/process.rs(L100 - L163)
Summary
The axprocess crate provides a robust system for process creation and initialization that:
- Uses the builder pattern for flexible configuration
- Establishes proper hierarchical relationships
- Manages memory safely with appropriate reference counting
- Supports special handling for the init process
- Maintains proper process group and session memberships
This foundation enables the subsequent lifecycle management and inter-process relationships that are essential to an operating system's process subsystem.