Linux Process Management for Developers

Linux Process Management for Developers

Understanding Linux process management is essential for debugging performance issues, troubleshooting stuck applications, and optimizing resource usage. Every running program on Linux is a process, and the kernel provides a rich set of tools to inspect, monitor, and control them. This article covers the essential commands and concepts every developer should know.

Inspecting Running Processes

The ps command provides a snapshot of currently running processes. With different options, you can customize the output to show the information most relevant to you. The combination ps aux shows all processes from all users in a detailed format including CPU and memory usage. For real-time monitoring, htop provides an interactive, color-coded interface with tree view, filtering, and the ability to send signals to processes directly. The --sort flag lets you order processes by memory or CPU usage to quickly identify resource hogs.

# List all processes with custom output format
ps -eo pid,ppid,cmd,%mem,%cpu,user --sort=-%mem | head -20

# Show process tree
ps auxf

# Real-time monitoring
htop
# F5: tree view | F4: filter by name | F9: kill process

# Find a specific process by name
pgrep -fl nginx

# Detailed information about a specific PID
ps -fp 1234
ls -l /proc/1234/

The /proc filesystem is a virtual filesystem that exposes kernel data structures as files. Each running process has a directory at /proc/PID/ containing information about its command line (cmdline), environment variables (environ), open file descriptors (fd/), memory mappings (maps), and status (status). Reading these files programmatically is often faster and more flexible than parsing command output.

Systemd Service Management

Modern Linux distributions use systemd as the init system. Services are defined by unit files (.service files in /etc/systemd/system/) and managed with the systemctl command. Systemd handles starting services at boot, restarting them on failure, and collecting their logs through journald. Always check the status of a service before debugging other issues — the status output shows whether the service is running, the last few log lines, and whether it is enabled to start at boot.

# Manage services
systemctl status nginx          # check status and recent logs
systemctl start nginx           # start a service
systemctl stop nginx            # stop a service
systemctl restart nginx         # restart (reload config)
systemctl enable --now docker   # enable at boot and start now

# View logs for a service
journalctl -u nginx -f          # follow logs in real time
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx -p err      # only error level and above

# Create a custom service unit
cat > /etc/systemd/system/myapp.service << 'EOF'
[Unit]
Description=My Python Application
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 /opt/myapp/main.py
Restart=on-failure
RestartSec=5
EnvironmentFile=/opt/myapp/.env

[Install]
WantedBy=multi-user.target
EOF

Control Groups (cgroups)

Cgroups allow you to limit and account for the resource usage of process groups. Systemd exposes cgroup v2 through service unit properties. You can set CPU quotas, memory limits, and I/O weights to prevent one service from starving others. This is the foundation of container resource limits in Docker and Kubernetes.

# Set resource limits on a systemd service
systemctl set-property myapp.service CPUQuota=50%     # max 50% of one CPU
systemctl set-property myapp.service MemoryMax=512M    # max 512 MB RAM
systemctl set-property myapp.service IOWeight=100      # I/O priority

# View cgroup limits and current usage
systemctl show myapp.service | grep -E 'CPUQuota|MemoryMax|IOWeight'
cat /sys/fs/cgroup/system.slice/myapp.service/memory.current
cat /sys/fs/cgroup/system.slice/myapp.service/cpu.stat

# Check OOM kills
dmesg | grep -i "killed process"

Signals and Killing Processes

Signals are the Linux mechanism for notifying processes of events. The most commonly used signals are SIGTERM (15) — a polite request to terminate, allowing graceful cleanup — and SIGKILL (9) — an immediate forced termination that the process cannot catch or ignore. Always try SIGTERM first and only use SIGKILL as a last resort. The kill command sends signals by PID, while killall and pkill send signals by process name. The SIGHUP signal (1) is often used to tell daemons to reload their configuration without restarting. For background jobs, jobs, fg, and bg let you manage running and suspended processes interactively.

# Send signals
kill -15 1234            # SIGTERM — graceful shutdown
kill -9 1234             # SIGKILL — force kill (last resort)
killall -15 nginx        # SIGTERM all nginx processes
pkill -f "python.*server" # kill by command pattern

# Reload configuration (SIGHUP)
kill -1 1234
systemctl reload nginx   # equivalent for systemd services

# Manage background jobs
sleep 100 &              # start in background
jobs                     # list background jobs
fg %1                    # bring job 1 to foreground
Ctrl+Z                   # suspend foreground job
bg %1                    # resume suspended job in background

The nohup command runs a process that ignores SIGHUP (so it survives terminal closure) and redirects output to nohup.out. This is useful for long-running SSH sessions where you want a process to continue after disconnecting. For more robust background execution, use tmux or screen terminal multiplexers — they create persistent sessions that survive network interruptions and can be reattached later.

Monitoring process health is an ongoing task. Use htop for interactive debugging, systemctl for service management, journalctl for log analysis, and cgroups for resource limits. Combining these tools gives you full visibility into what your system is running and how resources are being consumed.

Leave a Reply

Your email address will not be published. Required fields are marked *