Signals and Platform-Specific APIs
Operating systems expose process management features beyond portable spawning and exit codes: Unix systems use signals, process groups, uid/gid credentials, and exec image replacement, while Windows uses creation flags, job objects, and raw argument strings. Rust exposes these platform-native capabilities safely and zero-cost through extension traits in std::os::unix::process and std::os::windows::process.
Platform Extension Traits for std::process
| Platform Module | Extension Trait | Target Type | Key Capabilities |
|---|---|---|---|
std::os::unix::process |
CommandExt |
Command |
.uid(), .gid(), .process_group(), .arg0(), .exec(), unsafe { .pre_exec() } |
std::os::unix::process |
ExitStatusExt |
ExitStatus |
.signal(), .core_dumped(), .stopped_signal(), .continued() |
std::os::windows::process |
CommandExt |
Command |
.creation_flags(), .raw_arg() |
std::os::windows::process |
ChildExt |
Child |
.raw_handle() |
Unix Signals and ExitStatusExt
On Unix platforms, a process can be terminated or stopped by asynchronous kernel notifications called signals:
| Signal | Number (Typical) | Default Action | Common Cause |
|---|---|---|---|
SIGINT |
2 |
Terminate | User pressed Ctrl+C in the terminal. |
SIGQUIT |
3 |
Core Dump | User pressed Ctrl+\ in the terminal. |
SIGKILL |
9 |
Terminate (cannot be caught) | Forceful kill (kill -9 or Child::kill()). |
SIGSEGV |
11 |
Core Dump | Invalid memory access (segmentation fault). |
SIGPIPE |
13 |
Terminate (ignored by Rust runtime) | Writing to a pipe or socket whose reader closed. |
SIGTERM |
15 |
Terminate (catchable) | Graceful shutdown request from process manager/systemd. |
Note that the Rust runtime automatically sets SIGPIPE to SIG_IGN (ignore) before fn main() starts so that writing to a closed pipe or socket returns Err(io::ErrorKind::BrokenPipe) rather than silently killing the process.
use std::io;
use std::process::{Command, Stdio};
fn main() -> io::Result<()> {
let mut cmd = Command::new("rustc");
cmd.arg("--version").stdout(Stdio::null());
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
// Put the child into its own process group (PGID = child PID)
// so terminal signals can be managed independently
cmd.process_group(0);
// Customize argv[0] seen by the child process
cmd.arg0("custom-rustc-worker");
}
let status = cmd.status()?;
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
println!(
"Exited cleanly: {}, signal: {:?}, core_dumped: {}",
status.success(),
status.signal(),
status.core_dumped()
);
}
#[cfg(not(unix))]
{
println!("Exited cleanly: {}, signal: None, core_dumped: false", status.success());
}
Ok(())
}
Exited cleanly: true, signal: None, core_dumped: false
Replacing the Current Process Image on Unix (CommandExt::exec)
On Unix, CommandExt::exec(&mut self) -> io::Error invokes the execvp system call, replacing the current process memory image in-place with the new program while keeping the same PID and open file descriptors. Because a successful exec never returns to the caller, its return type is io::Error rather than io::Result<()>.
Windows Creation Flags (Windows CommandExt)
On Windows, GUI applications spawning background CLI tools often want to suppress the flashing console window using the CREATE_NO_WINDOW (0x08000000) flag:
#[cfg(windows)]
fn configure_headless(cmd: &mut std::process::Command) {
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
cmd.creation_flags(CREATE_NO_WINDOW);
}
fn main() {
println!("Platform extension traits configured");
}
Platform extension traits configured