Learn to optimize Linux workloads by configuring CPU scheduling policies, setting process priorities with nice and ionice commands, and implementing systemd service limits for better resource allocation and performance.
Prerequisites
- Root or sudo access
- Basic understanding of Linux processes
- Familiarity with systemd services
What this solves
Linux CPU scheduling determines which processes get processor time and in what order. By default, Linux uses the Completely Fair Scheduler (CFS) which gives equal CPU time to all processes, but production workloads often need fine-tuned resource allocation. This tutorial shows you how to prioritize critical services, limit resource-hungry applications, and optimize system performance through proper CPU scheduling configuration.
Understanding Linux process scheduling
Process priorities and nice values
Linux uses nice values ranging from -20 (highest priority) to +19 (lowest priority) to determine process scheduling priority. Lower nice values get more CPU time, while higher values get less. Regular users can only increase nice values (lower priority), while root can set any value.
The priority formula is: Priority = 20 + nice_value. A process with nice value -10 has priority 10, while nice value +10 gives priority 30. The kernel scheduler uses these priorities to allocate CPU time slices.
Scheduling policies
Linux supports multiple scheduling policies beyond the default CFS. Real-time policies like SCHED_FIFO and SCHED_RR provide deterministic scheduling for time-critical applications. SCHED_BATCH optimizes for throughput over latency, while SCHED_IDLE runs processes only when the system is otherwise idle.
| Policy | Use case | Priority range |
|---|---|---|
| SCHED_NORMAL (CFS) | Default for most processes | Nice -20 to +19 |
| SCHED_FIFO | Real-time, first-in-first-out | RT priority 1-99 |
| SCHED_RR | Real-time, round-robin | RT priority 1-99 |
| SCHED_BATCH | CPU-intensive batch jobs | Nice -20 to +19 |
| SCHED_IDLE | Background tasks | Nice +19 equivalent |
Step-by-step configuration
Install scheduling tools
Install the utilities needed for process scheduling management and monitoring.
sudo apt update
sudo apt install -y util-linux schedutils htop iotop
Check current process priorities
View current process priorities and scheduling information using standard Linux tools.
# View processes with priority information
ps -eo pid,ppid,ni,pri,psr,comm --sort=-ni | head -20
Check scheduling policy for a specific process
chrt -p 1234
View system load and process priorities
htop
Set process priorities with nice
Use nice to start processes with specific priority levels and renice to adjust running processes.
# Start a process with lower priority (higher nice value)
nice -n 10 find / -name "*.log" 2>/dev/null
Start a CPU-intensive task with lowest priority
nice -n 19 tar czf backup.tar.gz /home/user/documents/
As root, start a high-priority process
sudo nice -n -5 important-service
Adjust priority of running process (find the PID first)
ps aux | grep find
sudo renice -10 -p 12345
Configure I/O scheduling with ionice
Set I/O scheduling classes and priorities to manage disk access priorities alongside CPU scheduling.
# Set I/O class to idle (only when system is idle)
ionice -c 3 -p 12345
Set best-effort I/O with priority 7 (lowest)
ionice -c 2 -n 7 dd if=/dev/zero of=/tmp/testfile bs=1M count=1000
Start process with real-time I/O priority (root only)
sudo ionice -c 1 -n 4 database-backup-script
Check current I/O scheduling
ionice -p 12345
Set scheduling policies with chrt
Use chrt to configure advanced scheduling policies for real-time and batch processing requirements.
# Set SCHED_BATCH policy for CPU-intensive tasks
chrt -b 0 cpu-intensive-calculation
Set SCHED_IDLE for background processes
chrt -i 0 background-maintenance-task
Set real-time FIFO scheduling (root only)
sudo chrt -f 50 time-critical-application
Set real-time round-robin scheduling
sudo chrt -r 30 -p 12345
Check scheduling policy and priority
chrt -p 12345
Configure systemd service CPU limits
Create systemd service configurations with CPU scheduling and resource limits for consistent service management.
[Unit]
Description=High Priority Application
After=network.target
[Service]
Type=simple
User=appuser
Group=appgroup
ExecStart=/opt/myapp/bin/myapp
Restart=always
CPU scheduling configuration
Nice=-10
CPUSchedulingPolicy=2
CPUSchedulingPriority=0
IOSchedulingClass=2
IOSchedulingPriority=2
CPU resource limits
CPUQuota=200%
CPUWeight=200
[Install]
WantedBy=multi-user.target
Create batch processing service
Configure a systemd service optimized for batch processing with appropriate scheduling policies.
[Unit]
Description=Batch Processing Service
After=network.target
[Service]
Type=simple
User=batchuser
Group=batchgroup
ExecStart=/opt/batch/processor
Restart=on-failure
Batch scheduling configuration
Nice=15
CPUSchedulingPolicy=3
IOSchedulingClass=3
Resource constraints
CPUQuota=50%
CPUWeight=50
MemoryMax=2G
[Install]
WantedBy=multi-user.target
Configure CPU affinity and NUMA
Set CPU affinity to bind processes to specific CPU cores for improved performance and cache locality.
# Check system CPU topology
lscpu
numactl --hardware
Bind process to specific CPUs (cores 0-3)
taskset -c 0-3 cpu-bound-application
Bind process to CPU cores using hex mask (cores 0,2,4,6)
taskset 0x55 application
Check current CPU affinity
taskset -p 12345
Set CPU affinity for running process
sudo taskset -cp 0,2,4,6 12345
Configure systemd service with CPU affinity
Add CPU affinity settings to systemd service files for consistent CPU binding.
[Unit]
Description=CPU Bound Application
After=network.target
[Service]
Type=simple
User=appuser
ExecStart=/opt/app/cpu-intensive-app
Restart=always
CPU affinity and scheduling
CPUAffinity=0-3
Nice=0
CPUSchedulingPolicy=0
Resource limits
CPUQuota=400%
CPUWeight=100
[Install]
WantedBy=multi-user.target
Apply and verify systemd configurations
Reload systemd configuration and start services with the new scheduling settings.
# Reload systemd daemon
sudo systemctl daemon-reload
Start and enable services
sudo systemctl enable --now high-priority.service
sudo systemctl enable --now batch-processor.service
sudo systemctl enable --now cpu-bound.service
Check service status
sudo systemctl status high-priority.service
sudo systemctl status batch-processor.service
Configure CPU scheduling limits globally
Set system-wide limits and default scheduling parameters for better resource management.
# Set nice limits for users and groups
@developers soft nice -10
@developers hard nice -5
batchuser soft nice 15
batchuser hard nice 19
Set real-time priority limits
@realtime soft rtprio 50
@realtime hard rtprio 80
Memory limits
@developers soft memlock unlimited
@developers hard memlock unlimited
Configure kernel scheduler parameters
Tune kernel scheduler parameters for your workload requirements using sysctl.
# CFS scheduler tuning
kernel.sched_latency_ns = 6000000
kernel.sched_min_granularity_ns = 750000
kernel.sched_wakeup_granularity_ns = 1000000
Real-time throttling (percentage of CPU time for RT tasks)
kernel.sched_rt_runtime_us = 950000
kernel.sched_rt_period_us = 1000000
NUMA balancing
kernel.numa_balancing = 1
Process scheduling
kernel.sched_autogroup_enabled = 0
Apply kernel parameters
Load the new kernel parameters and verify they are applied correctly.
# Apply sysctl settings
sudo sysctl -p /etc/sysctl.d/99-scheduler.conf
Verify settings
sysctl kernel.sched_latency_ns
sysctl kernel.sched_rt_runtime_us
sysctl kernel.numa_balancing
View all scheduler-related parameters
sysctl -a | grep sched
Monitor and troubleshoot CPU scheduling
Monitor CPU scheduling with top and htop
Use system monitoring tools to observe CPU scheduling behavior and process priorities in real-time.
# Monitor with enhanced top output
top -c -o %CPU
Press 'f' to add fields, 'V' to show process hierarchy
Use htop for better visualization
htop
Press F6 to sort by CPU%, F4 to filter, F5 for tree view
Monitor specific processes
watch -n 1 'ps -eo pid,ppid,ni,pri,psr,pcpu,comm --sort=-pcpu | head -15'
Use systemd tools for monitoring
Leverage systemd's built-in monitoring capabilities to track service resource usage and scheduling.
# Show service CPU usage
systemctl show high-priority.service --property=CPUUsageNSec
systemctl show batch-processor.service --property=CPUUsageNSec
Monitor service resource usage
sudo systemd-cgtop
View detailed service status
systemctl status high-priority.service
systemctl status batch-processor.service
Check service resource limits
systemctl show high-priority.service | grep -E '(CPU|Memory|IO)'
Use performance monitoring tools
Deploy advanced monitoring tools to analyze CPU scheduling performance and identify bottlenecks.
# Monitor system performance with SAR
sar -u 1 10
sar -q 1 10
Use iostat for I/O and CPU statistics
iostat -x 1 5
Monitor process scheduling with pidstat
pidstat -u -p ALL 1 5
pidstat -w -p ALL 1 5
Check CPU topology and usage
lscpu
mpstat -P ALL 1 5
Verify your setup
# Check service status and resource usage
sudo systemctl status high-priority.service batch-processor.service cpu-bound.service
Verify process priorities and scheduling
ps -eo pid,ni,pri,psr,cls,comm --sort=-ni | head -20
Check CPU affinity settings
taskset -p $(pgrep cpu-intensive-app)
Monitor real-time CPU scheduling
htop
Verify kernel scheduler parameters
sysctl kernel.sched_latency_ns kernel.sched_rt_runtime_us
Check system load and CPU usage
uptime
sar -u 1 3
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| High-priority process not getting CPU time | Real-time throttling limits | Adjust kernel.sched_rt_runtime_us or use SCHED_NORMAL with low nice value |
| System becomes unresponsive | Real-time process consuming all CPU | Use sudo pkill -STOP process_name then adjust scheduling policy |
| Permission denied setting priority | User lacks privileges | Configure limits in /etc/security/limits.conf or run as root |
| Batch jobs interfering with interactive tasks | Incorrect scheduling policy | Use SCHED_BATCH or SCHED_IDLE for background processes |
| Poor performance on NUMA systems | Process migrating between NUMA nodes | Set CPU affinity with taskset or CPUAffinity in systemd |
| Systemd service limits not applied | Service configuration error | Check syntax with systemd-analyze verify service.service |
Next steps
- Configure Linux system resource limits with systemd and ulimit for application performance
- Optimize Linux system performance with kernel parameters and system tuning
- Optimize Linux I/O performance with kernel tuning and storage schedulers for high-throughput workloads
- Configure Linux cgroups v2 for advanced container resource management
- Set up Linux performance monitoring with perf tools and flame graphs
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Global variables
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BACKUP_DIR="/tmp/cpu-scheduling-backup-$(date +%s)"
PKG_MGR=""
PKG_INSTALL=""
UPDATE_CMD=""
# Usage function
usage() {
echo -e "${BLUE}Usage: $0 [OPTIONS]${NC}"
echo ""
echo "Configure Linux CPU and process scheduling optimization"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " --skip-update Skip package manager update"
echo ""
echo "Examples:"
echo " $0 # Full installation"
echo " $0 --skip-update # Skip package updates"
}
# Logging functions
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
log_step() {
echo -e "${BLUE}$1${NC}"
}
# Error handling and cleanup
cleanup_on_error() {
local exit_code=$?
log_error "Installation failed with exit code $exit_code"
if [[ -d "$BACKUP_DIR" ]]; then
log_info "Backup directory available at: $BACKUP_DIR"
log_info "You can manually restore configurations if needed"
fi
exit $exit_code
}
trap cleanup_on_error ERR
# Check if running as root or with sudo
check_privileges() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root or with sudo"
exit 1
fi
}
# Detect Linux distribution and set package manager
detect_distro() {
log_step "[1/8] Detecting Linux distribution..."
if [[ ! -f /etc/os-release ]]; then
log_error "/etc/os-release not found. Cannot detect distribution."
exit 1
fi
source /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
UPDATE_CMD="apt update"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
UPDATE_CMD="dnf makecache"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
UPDATE_CMD="yum makecache"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
log_info "Detected distribution: $PRETTY_NAME"
log_info "Using package manager: $PKG_MGR"
}
# Update package manager cache
update_packages() {
log_step "[2/8] Updating package manager cache..."
if [[ "$SKIP_UPDATE" == "false" ]]; then
$UPDATE_CMD
log_info "Package cache updated successfully"
else
log_info "Skipping package cache update"
fi
}
# Install required packages
install_packages() {
log_step "[3/8] Installing scheduling and monitoring tools..."
# Common packages for all distributions
local packages="util-linux htop iotop"
# Add schedutils if available (may not be available on all distros)
if [[ "$PKG_MGR" == "apt" ]]; then
packages="$packages schedutils"
elif [[ "$PKG_MGR" == "dnf" ]] || [[ "$PKG_MGR" == "yum" ]]; then
# schedutils might be part of util-linux or not available
packages="$packages procps-ng"
fi
$PKG_INSTALL $packages
log_info "Required packages installed successfully"
}
# Create backup directory
create_backup() {
log_step "[4/8] Creating backup directory..."
mkdir -p "$BACKUP_DIR"
# Backup system limits
if [[ -f /etc/security/limits.conf ]]; then
cp /etc/security/limits.conf "$BACKUP_DIR/limits.conf.bak"
fi
# Backup sysctl configuration
if [[ -f /etc/sysctl.conf ]]; then
cp /etc/sysctl.conf "$BACKUP_DIR/sysctl.conf.bak"
fi
log_info "Backup created at: $BACKUP_DIR"
}
# Configure system limits
configure_limits() {
log_step "[5/8] Configuring system resource limits..."
local limits_file="/etc/security/limits.conf"
# Add scheduling priority limits
cat >> "$limits_file" << 'EOF'
# CPU Scheduling Configuration - Added by install script
# Allow users in wheel/sudo group to set process priorities
@wheel soft nice -10
@wheel hard nice -10
@sudo soft nice -10
@sudo hard nice -10
# RT scheduling limits for system services
root soft rtprio 99
root hard rtprio 99
EOF
log_info "System limits configured in $limits_file"
}
# Configure kernel parameters
configure_kernel_params() {
log_step "[6/8] Configuring kernel scheduling parameters..."
local sysctl_file="/etc/sysctl.d/99-cpu-scheduling.conf"
cat > "$sysctl_file" << 'EOF'
# CPU Scheduling Optimization Parameters
# Reduce scheduler migration cost
kernel.sched_migration_cost_ns = 5000000
# Increase scheduler latency for better throughput
kernel.sched_min_granularity_ns = 10000000
kernel.sched_wakeup_granularity_ns = 15000000
# RT throttling - prevent RT tasks from starving system
kernel.sched_rt_runtime_us = 950000
kernel.sched_rt_period_us = 1000000
# Process limits
kernel.pid_max = 4194304
kernel.threads-max = 2097152
EOF
# Apply the settings
sysctl -p "$sysctl_file"
log_info "Kernel scheduling parameters configured"
}
# Create scheduling management scripts
create_management_scripts() {
log_step "[7/8] Creating process scheduling management scripts..."
local bin_dir="/usr/local/bin"
# Create priority management script
cat > "$bin_dir/set-process-priority" << 'EOF'
#!/bin/bash
# Process Priority Management Script
usage() {
echo "Usage: $0 <pid|command> <nice_value> [io_class] [io_priority]"
echo "Nice values: -20 (highest) to +19 (lowest priority)"
echo "I/O classes: 0=none, 1=rt, 2=best-effort, 3=idle"
echo "I/O priority: 0-7 (0=highest, 7=lowest)"
exit 1
}
[[ $# -lt 2 ]] && usage
PROCESS=$1
NICE_VAL=$2
IO_CLASS=${3:-2}
IO_PRIO=${4:-4}
# Check if it's a PID or command
if [[ "$PROCESS" =~ ^[0-9]+$ ]]; then
# It's a PID
renice "$NICE_VAL" -p "$PROCESS"
ionice -c "$IO_CLASS" -n "$IO_PRIO" -p "$PROCESS"
echo "Updated priority for PID $PROCESS"
else
# It's a command
nice -n "$NICE_VAL" ionice -c "$IO_CLASS" -n "$IO_PRIO" "$PROCESS" "${@:5}"
fi
EOF
chmod 755 "$bin_dir/set-process-priority"
# Create system monitoring script
cat > "$bin_dir/check-scheduling" << 'EOF'
#!/bin/bash
# System Scheduling Status Checker
echo "=== Process Scheduling Overview ==="
echo
echo "Top processes by CPU usage and priority:"
ps -eo pid,ppid,ni,pri,psr,pcpu,comm --sort=-pcpu | head -15
echo
echo "=== Real-time processes ==="
chrt -m
echo
echo "Processes using real-time scheduling:"
ps -eo pid,ni,pri,rtprio,cls,comm | grep -v ' - '
echo
echo "=== System load ==="
uptime
echo
echo "=== I/O scheduling classes ==="
echo "PID Class Priority Command"
for pid in $(ps -eo pid --no-headers | head -10); do
io_info=$(ionice -p $pid 2>/dev/null || echo "none: prio 4")
cmd=$(ps -p $pid -o comm --no-headers 2>/dev/null || echo "unknown")
printf "%-6s %-8s %s\n" "$pid" "$io_info" "$cmd"
done
EOF
chmod 755 "$bin_dir/check-scheduling"
log_info "Management scripts created in $bin_dir"
}
# Verify installation
verify_installation() {
log_step "[8/8] Verifying installation..."
local errors=0
# Check if tools are available
for tool in nice renice chrt ionice htop; do
if ! command -v "$tool" >/dev/null 2>&1; then
log_error "Tool not found: $tool"
((errors++))
fi
done
# Check if management scripts exist and are executable
for script in set-process-priority check-scheduling; do
if [[ ! -x "/usr/local/bin/$script" ]]; then
log_error "Management script not executable: $script"
((errors++))
fi
done
# Check if kernel parameters were applied
if ! sysctl kernel.sched_migration_cost_ns >/dev/null 2>&1; then
log_error "Kernel scheduling parameters not applied"
((errors++))
fi
if [[ $errors -eq 0 ]]; then
log_info "Installation verified successfully"
echo
echo -e "${GREEN}=== CPU Scheduling Configuration Complete ===${NC}"
echo
echo "Available commands:"
echo " set-process-priority <pid> <nice_value> - Set process priority"
echo " check-scheduling - View scheduling status"
echo " htop - Interactive process viewer"
echo
echo "Examples:"
echo " set-process-priority 1234 10 - Lower priority for PID 1234"
echo " nice -n 19 find / -name '*.log' - Run find with lowest priority"
echo " sudo chrt -f 50 critical-app - Run app with RT FIFO priority"
echo
echo -e "${YELLOW}Warning:${NC} Use real-time scheduling policies carefully to avoid system lockup"
else
log_error "Installation completed with $errors errors"
exit 1
fi
}
# Main function
main() {
local SKIP_UPDATE=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
exit 0
;;
--skip-update)
SKIP_UPDATE=true
shift
;;
*)
log_error "Unknown option: $1"
usage
exit 1
;;
esac
done
echo -e "${GREEN}CPU and Process Scheduling Configuration${NC}"
echo "========================================"
check_privileges
detect_distro
update_packages
install_packages
create_backup
configure_limits
configure_kernel_params
create_management_scripts
verify_installation
}
# Run main function
main "$@"
Review the script before running. Execute with: bash install.sh