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 밖으로 오프로드

단계별 이해

  1. 대상 워크로드 선정
    지연 민감 태스크와 일반 태스크를 먼저 분류합니다.
  2. CPU/NUMA 매핑 설계
    코어와 메모리 노드를 짝지어 cpuset 경계를 정의합니다.
  3. 커널 파라미터 결합
    isolcpus, nohz_full, rcu_nocbs를 함께 조정해 간섭원을 제거합니다.
  4. 성능 측정 반복
    latency/throughput를 측정하며 cpuset 경계와 밸런싱 정책을 미세 조정합니다.
관련 문서: cgroups (리소스 제어), Process Scheduler (스케줄링 정책), Real-time (실시간 커널), CPU Topology (CPU 구조)

개요

CPU Isolation은 특정 CPU 코어를 일반 커널 작업으로부터 격리하여 중요한 워크로드가 방해받지 않도록 보장하는 기술입니다.

Boot Params isolcpus/nohz_full/rcu_nocbs (부팅 시 정적 설정) cpuset cgroup cpu mask 분배 (런타임 동적 설정) 격리 워크로드 지터 감소/예측성 향상 isolcpus=nohz_full로 격리된 CPU를 cpuset에서 다시 분배 ex: isolcpus=4-7 설정 시, cpuset.cpus="4-7"로 프로세스 배정

격리 이유

격리 방법

방법 설명 적용 시점
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

cgroup v2 cpuset 설정 (CPU 격리) ① mkdir /sys/fs/cgroup/isolated 새 cgroup 디렉토리 생성 ② echo "+cpuset" → /sys/fs/cgroup/cgroup.subtree_control cpuset 컨트롤러 활성화 (cgroup v2 통합 방식) ③-a CPU 할당 echo "4-7" → .../isolated/cpuset.cpus ③-b 메모리 노드 할당 echo "0" → .../isolated/cpuset.mems ④ 프로세스 이동 echo $$ → .../isolated/cgroup.procs 결과: CPU 4-7에만 스케줄링 / NUMA 0에서만 메모리 할당 / 다른 프로세스와 완전 격리

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)을 제거하여 실시간 워크로드의 지터를 최소화합니다.

요구사항

설정

# 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가 지터를 유발할 수 있음

참고자료

다음 학습: