cpusets & CPU Isolation
cpusets는 태스크를 지정 CPU와 메모리 노드에 고정해 성능 예측성을 높이는 핵심 제어 수단입니다. 이 문서는 cgroup v2의 cpuset.cpus/cpuset.mems 의미, isolcpus/nohz_full/rcu_nocbs 조합, IRQ affinity와 housekeeping 분리, NUMA 배치, HPC 및 실시간 워크로드의 지터 최소화 튜닝과 검증 절차를 상세히 다룹니다.
역할 분리 안내: cpuset은 cgroups의 하위 컨트롤러입니다.
cgroup 트리 모델, delegation, 메모리/IO 정책은 cgroups 문서를 먼저 확인한 뒤 이 문서를 읽는 것을 권장합니다.
전제 조건: 프로세스 스케줄러와 프로세스 문서를 먼저 읽으세요.
실행 단위 관리 주제는 태스크 상태 전이와 큐 정책이 핵심이므로, 스케줄링 기준과 wakeup 경로를 먼저 이해해야 합니다.
일상 비유: 이 주제는 작업장 인력 배치와 호출 순서 관리와 비슷합니다.
긴급 작업, 대기열, 우선순위를 함께 조정하듯이 커널도 태스크 분류와 깨우기 정책이 전체 반응성을 결정합니다.
핵심 요약
- cpuset.cpus — 작업이 실행 가능한 CPU 집합
- cpuset.mems — 메모리 할당 가능한 NUMA 노드 집합
- isolcpus — 부팅 시점의 정적 격리
- nohz_full — 주기 tick 인터럽트를 줄여 지터 완화
- rcu_nocbs — RCU 콜백을 격리 CPU 밖으로 오프로드
단계별 이해
- 대상 워크로드 선정
지연 민감 태스크와 일반 태스크를 먼저 분류합니다. - CPU/NUMA 매핑 설계
코어와 메모리 노드를 짝지어 cpuset 경계를 정의합니다. - 커널 파라미터 결합
isolcpus,nohz_full,rcu_nocbs를 함께 조정해 간섭원을 제거합니다. - 성능 측정 반복
latency/throughput를 측정하며 cpuset 경계와 밸런싱 정책을 미세 조정합니다.
개요
CPU Isolation은 특정 CPU 코어를 일반 커널 작업으로부터 격리하여 중요한 워크로드가 방해받지 않도록 보장하는 기술입니다.
격리 이유
- 성능 예측성 — 스케줄러 노이즈 제거, 일정한 응답 시간
- 캐시 친화성 — 독점 CPU로 L1/L2/L3 캐시 오염 방지
- 실시간 요구사항 — 금융 거래, 로봇 제어, 통신 장비
- HPC 워크로드 — 과학 시뮬레이션, 렌더링 팜
- 컨테이너 격리 — 보안 민감 컨테이너 전용 CPU
격리 방법
| 방법 | 설명 | 적용 시점 |
|---|---|---|
| isolcpus | 부팅 시 CPU를 스케줄러에서 제외 | 커널 파라미터 (정적) |
| cpusets | cgroup으로 프로세스 CPU 제한 | 런타임 (동적) |
| nohz_full | 틱리스 모드 (타이머 인터럽트 제거) | 커널 파라미터 (정적) |
| rcu_nocbs | RCU 콜백을 다른 CPU로 오프로드 | 커널 파라미터 (정적) |
| taskset | 프로세스 CPU affinity 설정 | 런타임 (명령줄) |
cpusets 서브시스템
개념
cpusets는 프로세스 그룹에 CPU와 메모리 노드를 할당하는 cgroup v1/v2 서브시스템입니다.
cpuset 제어 파일
| 파일 | 설명 |
|---|---|
cpuset.cpus |
허용된 CPU 목록 (예: 0-3,8-11) |
cpuset.mems |
허용된 메모리 노드 (NUMA) |
cpuset.cpu_exclusive |
독점 모드 (1 = 다른 cpuset과 CPU 중복 불가) |
cpuset.mem_exclusive |
메모리 노드 독점 |
cpuset.sched_load_balance |
스케줄러 로드 밸런싱 (0 = 비활성화) |
cpuset.memory_migrate |
메모리 마이그레이션 허용 |
cpuset.memory_pressure |
메모리 압력 통계 (읽기 전용) |
cpuset 생성 (cgroup v1)
# cpuset cgroup 마운트
$ sudo mount -t cgroup -o cpuset cpuset /sys/fs/cgroup/cpuset
# 격리된 cpuset 생성
$ sudo mkdir /sys/fs/cgroup/cpuset/isolated
# CPU 4-7 할당
$ echo 4-7 | sudo tee /sys/fs/cgroup/cpuset/isolated/cpuset.cpus
# NUMA 노드 0 할당
$ echo 0 | sudo tee /sys/fs/cgroup/cpuset/isolated/cpuset.mems
# 독점 모드 활성화
$ echo 1 | sudo tee /sys/fs/cgroup/cpuset/isolated/cpuset.cpu_exclusive
# 로드 밸런싱 비활성화 (지터 최소화)
$ echo 0 | sudo tee /sys/fs/cgroup/cpuset/isolated/cpuset.sched_load_balance
# 프로세스 추가
$ echo $$ | sudo tee /sys/fs/cgroup/cpuset/isolated/tasks
$ ./my_rt_app
cpuset in cgroup v2
isolcpus 커널 파라미터
개요
isolcpus는 부팅 시 지정된 CPU를 기본 스케줄러 도메인에서 제외합니다. 이로 인해 일반 프로세스는 해당 CPU에 스케줄링되지 않습니다.
문법
# GRUB 설정 (/etc/default/grub)
GRUB_CMDLINE_LINUX="isolcpus=4-7"
# 또는 도메인별 격리
GRUB_CMDLINE_LINUX="isolcpus=domain,4-7"
# 옵션
isolcpus=4-7 # CPU 4~7 격리 (기본)
isolcpus=domain,4-7 # 스케줄러 도메인에서 제외
isolcpus=managed_irq,4-7 # managed IRQ도 격리
# GRUB 업데이트
$ sudo update-grub
$ sudo reboot
격리 확인
# isolcpus 설정 확인
$ cat /proc/cmdline | grep isolcpus
# 스케줄러 도메인 확인
$ cat /proc/sched_debug | grep -A 10 "cpu#4"
# 프로세스 CPU affinity 확인
$ taskset -p $$
pid 1234's current affinity mask: f # 0-3만 허용 (4-7 제외)
nohz_full (틱리스 모드)
개념
nohz_full은 지정된 CPU에서 주기적인 타이머 틱(timer tick)을 제거하여 실시간 워크로드의 지터를 최소화합니다.
요구사항
CONFIG_NO_HZ_FULL=y커널 빌드 옵션- 최소 1개 이상의 housekeeping CPU (nohz_full에서 제외)
- 단일 스레드 워크로드 (CPU당 1개 프로세스)
설정
# GRUB 설정
GRUB_CMDLINE_LINUX="nohz_full=4-7"
# isolcpus와 함께 사용 (권장)
GRUB_CMDLINE_LINUX="isolcpus=4-7 nohz_full=4-7"
# 확인
$ cat /sys/devices/system/cpu/nohz_full
4-7
$ cat /sys/devices/system/cpu/isolated
4-7
동작 방식
/* nohz_full CPU에서 */
- 단일 runnable 태스크만 있을 때 → 타이머 틱 중단
- 2개 이상 runnable 태스크 → 타이머 틱 재개 (스케줄링 필요)
- 시스템 콜/인터럽트 진입 시 → 일시적으로 틱 재개
rcu_nocbs (RCU 콜백 오프로드)
개념
RCU(Read-Copy-Update) 콜백 처리를 별도 CPU로 오프로드하여 격리된 CPU의 지터를 줄입니다.
설정
# GRUB 설정
GRUB_CMDLINE_LINUX="rcu_nocbs=4-7"
# 완전한 CPU 격리 설정
GRUB_CMDLINE_LINUX="isolcpus=domain,managed_irq,4-7 nohz_full=4-7 rcu_nocbs=4-7"
# 확인
$ cat /sys/devices/system/cpu/nohz_full
4-7
# RCU 스레드 확인
$ ps aux | grep rcuo
root 12 0.0 0.0 0 0 ? S 12:00 0:00 [rcuo/4]
root 13 0.0 0.0 0 0 ? S 12:00 0:00 [rcuo/5]
...
taskset (CPU Affinity)
사용법
# CPU 4에 프로세스 바인딩
$ taskset -c 4 ./my_app
# CPU 4-7에 바인딩
$ taskset -c 4-7 ./my_app
# 비트마스크 사용 (CPU 0,2,4,6)
$ taskset 0x55 ./my_app
# 실행 중인 프로세스 affinity 변경
$ taskset -cp 4 1234
pid 1234's current affinity list: 0-7
pid 1234's new affinity list: 4
# 현재 affinity 확인
$ taskset -p $$
pid 5678's current affinity mask: f0 # CPU 4-7
sched_setaffinity() 시스템 콜
#define _GNU_SOURCE
#include <sched.h>
int main(void)
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(4, &cpuset); /* CPU 4 */
if (sched_setaffinity(0, sizeof(cpuset), &cpuset) == -1) {
perror("sched_setaffinity");
return 1;
}
/* 이제 CPU 4에서만 실행됨 */
while (1) {
/* compute-intensive task */
}
}
IRQ Affinity (인터럽트 격리)
인터럽트 관리
격리된 CPU에서 인터럽트도 제거해야 완전한 격리가 가능합니다.
# 모든 IRQ 확인
$ cat /proc/interrupts
# IRQ 0의 affinity 확인
$ cat /proc/irq/0/smp_affinity
ff # 모든 CPU
# IRQ 0을 CPU 0-3으로 제한 (4-7 격리)
$ echo 0f | sudo tee /proc/irq/0/smp_affinity
0f
# 모든 IRQ를 CPU 0-3으로 이동 (스크립트)
$ for irq in $(ls /proc/irq/ | grep -E '^[0-9]+$'); do
echo 0f | sudo tee /proc/irq/$irq/smp_affinity 2>/dev/null
done
irqbalance 비활성화
# irqbalance 데몬이 IRQ affinity를 자동 조정하므로 비활성화
$ sudo systemctl stop irqbalance
$ sudo systemctl disable irqbalance
# 또는 특정 CPU 제외
$ sudo irqbalance --banirq=4,5,6,7
실전 예제
HPC 워크로드 설정
#!/bin/bash
# HPC CPU 격리 스크립트
# 1. GRUB 설정 확인
grep -q "isolcpus" /proc/cmdline || {
echo "Error: isolcpus not set"
exit 1
}
# 2. cpuset 생성
sudo mkdir -p /sys/fs/cgroup/cpuset/hpc
echo 4-7 | sudo tee /sys/fs/cgroup/cpuset/hpc/cpuset.cpus
echo 0 | sudo tee /sys/fs/cgroup/cpuset/hpc/cpuset.mems
echo 1 | sudo tee /sys/fs/cgroup/cpuset/hpc/cpuset.cpu_exclusive
echo 0 | sudo tee /sys/fs/cgroup/cpuset/hpc/cpuset.sched_load_balance
# 3. IRQ affinity 설정 (CPU 0-3만)
for irq in $(ls /proc/irq/ | grep -E '^[0-9]+$'); do
echo 0f | sudo tee /proc/irq/$irq/smp_affinity 2>/dev/null
done
# 4. HPC 애플리케이션 실행
echo $$ | sudo tee /sys/fs/cgroup/cpuset/hpc/tasks
exec taskset -c 4 chrt -f 99 ./hpc_simulation
실시간 애플리케이션
#define _GNU_SOURCE
#include <sched.h>
#include <pthread.h>
#include <stdio.h>
void setup_rt_thread(int cpu_id)
{
struct sched_param param;
cpu_set_t cpuset;
/* CPU affinity 설정 */
CPU_ZERO(&cpuset);
CPU_SET(cpu_id, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
/* 실시간 우선순위 설정 */
param.sched_priority = 99;
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
/* 메모리 락 (페이지 폴트 방지) */
mlockall(MCL_CURRENT | MCL_FUTURE);
printf("RT thread on CPU %d\\n", cpu_id);
}
void *rt_worker(void *arg)
{
int cpu_id = *(int *)arg;
setup_rt_thread(cpu_id);
while (1) {
/* Critical real-time work */
}
}
모니터링
CPU 격리 모니터링
#!/bin/bash
# CPU 격리 상태 모니터링
echo "===== CPU Isolation Status ====="
echo "Kernel Parameters:"
grep -o 'isolcpus=[^ ]*' /proc/cmdline
grep -o 'nohz_full=[^ ]*' /proc/cmdline
grep -o 'rcu_nocbs=[^ ]*' /proc/cmdline
echo
echo "Isolated CPUs:"
cat /sys/devices/system/cpu/isolated
echo
echo "Tasks on Isolated CPUs:"
for cpu in 4 5 6 7; do
echo " CPU $cpu:"
ps -eLo psr,comm | grep "^ *$cpu" | wc -l
done
echo
echo "IRQ Distribution:"
cat /proc/interrupts | head -1
cat /proc/interrupts | grep -E "^ *[0-9]+:" | head -5
커널 설정
CONFIG_CPUSETS=y # cpuset 서브시스템
CONFIG_PROC_PID_CPUSET=y # /proc/[pid]/cpuset
CONFIG_NO_HZ_FULL=y # Full tickless mode
CONFIG_RCU_NOCB_CPU=y # RCU callback offload
CONFIG_IRQ_FORCED_THREADING=y # IRQ 스레딩
# 실시간 커널 (선택)
CONFIG_PREEMPT_RT=y # PREEMPT_RT 패치
CONFIG_HIGH_RES_TIMERS=y # 고해상도 타이머
Best Practices
권장사항:
- Housekeeping CPU 확보 — 최소 1~2개 CPU는 일반 작업용으로 남겨둘 것
- NUMA 고려 — 격리된 CPU와 메모리 노드를 동일하게 설정
- 하이퍼스레딩 비활성화 — SMT 비활성화로 캐시 경합 제거
- 전원 관리 비활성화 — cpufreq governor를 performance로 설정
- 투명 거대 페이지 비활성화 — THP가 지터를 유발할 수 있음
참고자료
- Linux Kernel Parameters
- cpusets Documentation
- NO_HZ (Tickless Kernel)
kernel/sched/isolation.c— CPU 격리 구현kernel/cgroup/cpuset.c— cpuset 서브시스템
다음 학습:
- cgroups — 리소스 제어
- Process Scheduler — 스케줄링 정책
- Real-time — 실시간 커널
- NUMA — 메모리 배치 최적화
관련 문서
- CPU 캐시 (CPU Cache) — L1/L2/L3·TLB 상호작용, VIPT/PIPT alias 이슈, MESI/MOESI