In multi-core systems, Linux by default uses all available CPU cores for process scheduling. However, there are scenarios where you might want to restrict certain processes to specific cores:
- Preventing cache pollution in performance-critical applications
- Isolating workloads for better predictability
- Creating dedicated cores for real-time processes
- Testing multi-core application behavior
Linux provides several ways to control CPU affinity:
1. taskset Command
The most straightforward method is using the taskset utility:
# Launch a process pinned to CPU 0
taskset -c 0 ./my_process &
# Set affinity for existing process (PID 1234) to CPUs 0 and 1
taskset -cp 0,1 1234
2. sched_setaffinity System Call
For programmatic control, use the sched_setaffinity system call:
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
int main() {
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(0, &mask); // Use only CPU 0
if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
perror("sched_setaffinity");
return 1;
}
// Process now runs only on CPU 0
while(1) { /* work */ }
return 0;
}
For more sophisticated control:
Using cgroups
# Create a cgroup
mkdir /sys/fs/cgroup/cpuset/my_group
# Set allowed CPUs
echo 2-3 > /sys/fs/cgroup/cpuset/my_group/cpuset.cpus
# Add process to cgroup
echo <pid> > /sys/fs/cgroup/cpuset/my_group/tasks
Kernel Boot Parameter
For permanent core isolation, add to your kernel boot parameters:
isolcpus=2,3 # Reserve CPUs 2 and 3 for exclusive use
Check process CPU affinity with:
taskset -p <pid>
# Or
cat /proc/<pid>/status | grep Cpus_allowed
Monitor CPU usage with:
top -H -p <pid> # Show thread-level CPU usage
mpstat -P ALL 1 # Monitor per-CPU utilization
- NUMA architecture awareness is crucial for memory-bound processes
- Hyperthreading cores share resources - pinning to physical cores might be preferable
- Over-pinning can lead to underutilization
- Consider process priorities (
nicevalues) alongside affinity
When dealing with multi-core systems, Linux provides robust mechanisms for controlling process scheduling through CPU affinity. This feature allows administrators and developers to bind processes to specific CPU cores, which is particularly useful for:
- Optimizing cache utilization
- Reducing context switching overhead
- Isolating critical processes
- Benchmarking performance
The primary methods for managing CPU affinity include:
1. taskset Command
The simplest way to set CPU affinity is using the taskset utility:
# Start a process bound to CPU 0
taskset -c 0 ./my_process &
# Change affinity of running process (PID 1234) to CPUs 0 and 2
taskset -pc 0,2 1234
2. sched_setaffinity System Call
For programmatic control, use the sched_setaffinity system call:
#define _GNU_SOURCE
#include <sched.h>
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(0, &mask); // Use only CPU 0
if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
perror("sched_setaffinity");
}
Isolating CPU Cores at Boot
For maximum isolation, add to your kernel boot parameters:
isolcpus=1,3 # Isolate CPUs 1 and 3 from general scheduling
Using cgroups v2
For modern systems with cgroups v2:
# Create new cgroup
mkdir /sys/fs/cgroup/cpuset/my_group
# Set allowed CPUs
echo 2 > /sys/fs/cgroup/cpuset/my_group/cpuset.cpus
# Add process
echo $PID > /sys/fs/cgroup/cpuset/my_group/cgroup.procs
When pinning processes:
- Monitor cache hit rates with
perf stat -e cache-misses - Check for load balancing issues using
mpstat -P ALL 1 - Consider NUMA architecture with
numactl --hardware
Here's a complete Python wrapper using ctypes:
import ctypes
import os
libc = ctypes.CDLL('libc.so.6')
mask = (ctypes.c_ulong * 1)(1 << 0) # CPU 0
if libc.sched_setaffinity(0, ctypes.sizeof(mask), mask) != 0:
raise RuntimeError("Failed to set CPU affinity")