基于系统负载动态调整后台CPU密集型进程CPU使用率的跨Shell实现方案咨询
Got it, let's work through this problem. You need a long-running CPU-heavy task (like ffmpeg video encoding) that automatically ramps up or throttles its CPU usage based on system load, and works even if the background task and monitoring are started in separate shells. Here's a practical, cross-shell solution that gets the job done:
We'll combine three key pieces:
- A load detection script to check if the system is idle or busy
cpulimitto enforce precise CPU usage caps (more reliable thannicefor hard limits)- Cross-shell process management to let the task and monitor run in separate terminals
First, make sure you have cpulimit installed—it's available on most Linux distros and macOS:
- Debian/Ubuntu:
sudo apt update && sudo apt install cpulimit - macOS (via Homebrew):
brew install cpulimit
Save this as cpu_throttle.sh (it's bash-compatible, so it works in bash, zsh, and most other shells):
#!/bin/bash # Configure these values to match your needs TARGET_PROCESS="ffmpeg" BUSY_CPU_LIMIT=10 # % CPU per core when system is busy CHECK_INTERVAL=5 # How often to check load (seconds) # Calculate full CPU capacity (100% per core) FULL_CPU_LIMIT=$(( $(nproc) * 100 )) # Helper function to get current cpulimit for a PID get_current_limit() { local pid=$1 ps aux | grep -E "cpulimit -p $pid" | awk '{for(i=1;i<=NF;i++) if($i=="-l") print $(i+1)}' } while true; do # Get all PIDs for the target process TARGET_PIDS=$(pgrep $TARGET_PROCESS) if [ -z "$TARGET_PIDS" ]; then echo "[$(date)] No $TARGET_PROCESS processes found. Exiting monitor." exit 0 fi # Check system load: Use 1-minute load average vs 70% of CPU core count LOAD_1MIN=$(uptime | awk '{print $(NF-2)}' | sed 's/,//') CPU_CORES=$(nproc) SYSTEM_IDLE=$(echo "$LOAD_1MIN < $CPU_CORES * 0.7" | bc -l) if [ "$SYSTEM_IDLE" -eq 1 ]; then # System is idle: Unleash full CPU echo "[$(date)] System is idle. Setting $TARGET_PROCESS to $FULL_CPU_LIMIT% total CPU." for pid in $TARGET_PIDS; do current_limit=$(get_current_limit $pid) if [ "$current_limit" != "$FULL_CPU_LIMIT" ]; then # Kill existing cpulimit if present, then apply full limit pgrep -f "cpulimit -p $pid" | xargs kill 2>/dev/null cpulimit -p $pid -l $FULL_CPU_LIMIT & fi done else # System is busy: Throttle to 10% per core echo "[$(date)] System is busy (1min load: $LOAD_1MIN). Throttling $TARGET_PROCESS to $BUSY_CPU_LIMIT% per core." for pid in $TARGET_PIDS; do current_limit=$(get_current_limit $pid) if [ "$current_limit" != "$BUSY_CPU_LIMIT" ]; then pgrep -f "cpulimit -p $pid" | xargs kill 2>/dev/null cpulimit -p $pid -l $BUSY_CPU_LIMIT & fi done fi sleep $CHECK_INTERVAL done
This setup lets you start the background task and monitor in completely separate terminals:
Start your CPU-heavy task in one terminal (use
nohupif you want it to keep running when you close the terminal):nohup ffmpeg -i input_video.mp4 -c:v libx265 encoded_output.mp4 &Or just run
ffmpeg ... &if you don't need it to survive terminal closure.Start the monitor in a second terminal:
chmod +x cpu_throttle.sh ./cpu_throttle.shThe monitor will run indefinitely, checking system load every 5 seconds and adjusting the ffmpeg process's CPU limit automatically.
- Multi-core awareness: The script calculates
FULL_CPU_LIMITas 100% per core, so a 4-core system will allow ffmpeg to use up to 400% CPU (full utilization) when idle. - Load detection logic: We use the 1-minute load average vs 70% of core count to determine idle/busy—adjust this threshold in the
SYSTEM_IDLEline if you want stricter/looser triggers. - Cross-shell compatibility: Since the script uses PID-based process control, it doesn't care which shell the target task was started in. The monitor can run in bash, zsh, or any shell that supports basic bash syntax.
- Fallback behavior: If the target process exits, the monitor will detect this and shut itself down cleanly.
内容的提问来源于stack exchange,提问作者Tim Voßhenrich

