---
title: Linux Process Management
description: Comprehensive technical documentation on Linux process management, including fork/exec, process states, signals, nice/renice, cgroups, and namespaces.
keywords: 
  - linux
  - process
  - fork
  - exec
  - signals
  - nice
  - renice
  - cgroups
  - namespaces
category: Linux
tags:
  - process
  - fork
  - exec
  - signals
  - nice
  - renice
  - cgroups
  - namespaces
---

## Linux Process Management

Linux is a multi-tasking operating system, which means it can run multiple processes concurrently. Processes are the fundamental units of execution in Linux, and they are managed by the kernel. This document provides a comprehensive technical overview of Linux process management, including fork/exec, process states, signals, nice/renice, cgroups, and namespaces.

### Fork and Exec

In Linux, the `fork()` system call is used to create a new process, which is a copy of the calling process. The new process is called the child process, and the original process is called the parent process. The child process inherits most of the parent's resources, such as memory, open files, and environment variables.

After a process is forked, the child process can execute a different program using the `exec()` system call. The `exec()` function replaces the current process image with a new process image, effectively terminating the old process and starting a new one.

Here's an example of how to use `fork()` and `exec()` in C:

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    pid_t pid;

    // Fork a new process
    pid = fork();

    if (pid == -1) {
        // Fork failed
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // Child process
        printf("Child process (PID: %d)\n", getpid());
        execlp("ls", "ls", "-l", NULL);
        perror("exec");
        exit(1);
    } else {
        // Parent process
        printf("Parent process (PID: %d, child PID: %d)\n", getpid(), pid);
        waitpid(pid, NULL, 0);
        printf("Child process has terminated.\n");
    }

    return 0;
}
```

In this example, the parent process forks a new child process. The child process then executes the `ls -l` command using the `exec()` function. The parent process waits for the child process to terminate using the `waitpid()` system call.

### Process States

Processes in Linux can be in one of several states:

1. **Running**: The process is currently executing on the CPU.
2. **Waiting**: The process is waiting for an event, such as I/O or a signal, to occur.
3. **Stopped**: The process has been temporarily stopped, usually by a signal.
4. **Zombie**: The process has terminated, but its parent process has not yet collected its exit status.

You can view the current state of processes using the `ps` command:

```
$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Oct10 ?        00:00:05 /sbin/init
root         2     0  0 Oct10 ?        00:00:00 [kthreadd]
root         3     2  0 Oct10 ?        00:00:00 [rcu_gp]
...
```

In this example, the processes are in the "Running" state, as indicated by the absence of any state information in the output.

### Signals

Signals are a mechanism in Linux for sending notifications to processes. Signals can be used to interrupt a process, terminate a process, or request specific actions from a process.

Some common signals in Linux include:

- `SIGINT`: Interrupt signal, usually generated by Ctrl+C.
- `SIGTERM`: Termination signal, used to request a process to terminate.
- `SIGKILL`: Kill signal, used to forcibly terminate a process.
- `SIGCHLD`: Child process signal, sent to a parent process when a child process terminates or stops.

You can send signals to a process using the `kill` command:

```
$ kill -s SIGTERM 1234
```

This will send the `SIGTERM` signal to the process with PID 1234.

Processes can also handle signals by registering signal handlers, which are functions that are called when a signal is received. Here's an example of how to handle the `SIGINT` signal in C:

```c
#include <stdio.h>
#include <signal.h>

void signal_handler(int signum) {
    printf("Received signal %d\n", signum);
}

int main() {
    signal(SIGINT, signal_handler);
    printf("Press Ctrl+C to generate a SIGINT signal.\n");
    while (1) {
        // Do nothing
    }
    return 0;
}
```

In this example, the `signal_handler()` function is registered to handle the `SIGINT` signal. When the user presses Ctrl+C, the `signal_handler()` function is called, and a message is printed to the console.

### Nice and Renice

In Linux, the `nice` and `renice` commands are used to adjust the scheduling priority of a process. The scheduling priority, or "niceness", of a process ranges from -20 (highest priority) to 19 (lowest priority). By default, new processes inherit the niceness of their parent process.

You can set the niceness of a new process using the `nice` command:

```
$ nice -n 10 ./my_program
```

This will start the `my_program` process with a niceness of 10.

You can also change the niceness of an existing process using the `renice` command:

```
$ renice -n 5 -p 1234
```

This will change the niceness of the process with PID 1234 to 5.

Adjusting the niceness of a process can be useful for managing system resources and ensuring that important processes receive the necessary CPU time.

### Cgroups

Control groups (cgroups) is a Linux kernel feature that allows you to allocate resources (such as CPU, memory, and disk I/O) among hierarchical groups of processes. Cgroups can be used for a variety of purposes, such as resource management, process isolation, and performance monitoring.

Here's an example of how to create a new cgroup and assign a process to it:

1. Create a new cgroup for CPU resource management:

   ```
   $ sudo mkdir /sys/fs/cgroup/cpu/my_cgroup
   ```

2. Set the CPU shares for the new cgroup:

   ```
   $ sudo sh -c "echo 512 > /sys/fs/cgroup/cpu/my_cgroup/cpu.shares"
   ```

   This sets the CPU shares of the new cgroup to 512, which is half the default value of 1024.

3. Assign a process to the new cgroup:

   ```
   $ sudo sh -c "echo $$ > /sys/fs/cgroup/cpu/my_cgroup/tasks"
   ```

   This assigns the current process (with PID $$) to the new cgroup.

You can verify that the process has been assigned to the new cgroup by checking the contents of the `tasks` file:

```
$ cat /sys/fs/cgroup/cpu/my_cgroup/tasks
1234
```

Cgroups can be a powerful tool for managing system resources and ensuring that critical processes receive the necessary resources.

### Namespaces

Namespaces are a Linux kernel feature that allow the isolation of system resources, such as process IDs, network interfaces, and mount points, among different groups of processes. Namespaces provide a way to create virtual environments within a single Linux system, which can be useful for containerization, virtualization, and other system-level isolation tasks.

Here's an example of how to create a new PID namespace using the `unshare` command:

1. Create a new PID namespace:

   ```
   $ sudo unshare --fork --pid /bin/bash
   ```

   This creates a new PID namespace and starts a new shell process within the namespace.

2. Verify the new PID namespace:

   ```
   $ ps -ef
   UID        PID  PPID  C STIME TTY          TIME CMD
   root         1     0  0 15:42 pts/0    00:00:00 /bin/bash
   root         2     1  0 15:42 pts/0    00:00:00 ps -ef
   ```

   In this example, the process with PID 1 is the new shell process, and the process with PID 2 is the `ps` command that we just ran.

3. Create a new process in the new namespace:

   ```
   $ sleep 1000 &
   [1] 3
   ```

   The new `sleep` process has been created within the new PID namespace, and its PID is 3.

Namespaces can be used in combination with other Linux features, such as cgroups and containers, to provide a powerful and flexible system for managing system resources and isolating processes.