When attempting to limit CPU usage for a process via systemd's CPUQuota parameter, many administrators encounter unexpected behavior where processes exceed their allocated CPU percentage. Let's examine why the original implementation failed:
# Original unit file example
[Unit]
Description=Virtual Distributed Ethernet
[Service]
ExecStart=/usr/bin/ddcommand
CPUQuota=10%
[Install]
WantedBy=multi-user.target
CPUQuota actually specifies the maximum CPU time per second (in percentage) that a service can use across all CPU cores. The key points about its implementation:
- Works at the cgroup level (specifically cpu.cfs_quota_us)
- 10% means 100ms of CPU time per second
- Requires CPUAccounting=true for proper measurement
- Operates within the CPU CFS scheduler
Here's the proper way to configure CPU limiting for your dd process:
[Unit]
Description=CPU Limited dd Process
[Service]
CPUAccounting=true
CPUQuota=10%
ExecStart=/bin/sh -c "dd if=/dev/zero of=/dev/null bs=1024k"
# Optional but recommended for better control
MemoryAccounting=true
BlockIOAccounting=true
[Install]
WantedBy=multi-user.target
After applying these changes, verify with:
# Reload systemd
sudo systemctl daemon-reload
# Check current CPU usage
systemd-cgtop
# View detailed CPU metrics
systemctl show your-service-name | grep CPU
For more granular control, consider combining CPUQuota with:
[Service]
CPUAccounting=true
CPUQuota=10%
CPUShares=512
CPUWeight=50
AllowedCPUs=0-1 # Limit to specific cores
If CPU limits still aren't working:
- Ensure systemd version > 231 (older versions have incomplete cgroupv2 support)
- Check if cgroups are properly mounted:
mount | grep cgroup
- Verify kernel support:
grep CGROUP /boot/config-$(uname -r)
- Test with simpler commands first (e.g.,
yes > /dev/null
)
When attempting to limit CPU usage for a dd
process through systemd's CPUQuota parameter, you might encounter unexpected behavior where the process continues consuming 70-75% CPU despite being configured with CPUQuota=10%
. This contradicts the expected behavior documented in systemd.resource-control.
The original unit file contains proper CPUQuota syntax:
[Unit]
Description=Virtual Distributed Ethernet
[Service]
ExecStart=/usr/bin/ddcommand
CPUQuota=10%
[Install]
WantedBy=multi-user.target
With the corresponding shell script:
#!/bin/sh
dd if=/dev/zero of=/dev/null bs=1024k
The systemctl show
output reveals important details:
CPUShares=18446744073709551615
StartupCPUShares=18446744073709551615
CPUQuotaPerSecUSec=100ms
LimitCPU=18446744073709551615
The 100ms quota (equivalent to 10% of 1 second) is correctly set, but the unlimited LimitCPU
value suggests potential conflicts with system-wide limits.
To properly investigate, check the actual cgroup settings:
systemd-cgls
cat /sys/fs/cgroup/cpu/system.slice/dd.service/cpu.cfs_quota_us
cat /sys/fs/cgroup/cpu/system.slice/dd.service/cpu.cfs_period_us
Try these modifications to ensure proper CPU limiting:
Option 1: Combine with CPUAccounting
[Service]
ExecStart=/usr/bin/ddcommand
CPUQuota=10%
CPUAccounting=yes
Option 2: Explicit Period Configuration
[Service]
ExecStart=/usr/bin/ddcommand
CPUQuota=10%
CPUQuotaPeriodSec=1s
Option 3: Alternative Limiting via cgroups
systemctl set-property dd.service CPUQuota=10% CPUAccounting=yes
systemctl daemon-reload
systemctl restart dd.service
If issues persist, consider these deeper investigations:
# Check for conflicts with other controllers
systemd-analyze dump | grep -A5 "dd.service"
# Verify kernel support
grep CFS /boot/config-$(uname -r)
# Monitor real-time CPU usage
systemd-cgtop -p -n 10
Here's a more robust test script to validate CPU limiting:
#!/bin/bash
# cpu_stress_test.sh
while true; do
dd if=/dev/urandom of=/dev/null bs=1M count=1000
sleep 0.1
done
Configure the unit file with explicit accounting:
[Service]
ExecStart=/usr/local/bin/cpu_stress_test.sh
CPUQuota=15%
CPUAccounting=yes
MemoryAccounting=yes
TasksAccounting=yes