RTLA / timerlat / osnoise 레이턴시 분석

RTLA는 커널 트레이싱 인프라 위에서 동작하는 레이턴시 분석 도구 묶음입니다. 이 문서는 timerlatosnoise를 중심으로 IRQ·스레드(Thread) 지터, CPU 간섭, PREEMPT_RT 환경의 지연(Latency) 시간 분석 흐름을 정리하고, cyclictest와의 역할 차이, tracefs/ftrace 연계, 격리(Isolation) CPU 운영 팁까지 실전 기준으로 설명합니다.

전제 조건: 프로세스(Process) 스케줄러(Scheduler)ftrace / Tracepoints 문서를 먼저 읽으세요. 레이턴시 분석은 단순한 수치 수집이 아니라 스케줄링 지연, IRQ 간섭, CPU 격리 정책을 함께 읽는 작업이므로, 스케줄러와 tracing 기본기를 먼저 고정해야 해석이 흔들리지 않습니다.
일상 비유: 이 개념은 응급차 전용 차선의 방해 요소 측정과 비슷합니다. 응급차가 늦는 이유가 엔진 출력 부족인지, 교차로 신호 때문인지, 일반 차량이 끼어들었기 때문인지 구분해야 하듯이, 레이턴시도 "어떤 경로에서 얼마나 오래 막혔는지"를 분리해 봐야 원인을 잡을 수 있습니다.

핵심 요약

  • RTLA — tracefs와 ftrace 위에서 동작하는 레이턴시 분석 프론트엔드입니다.
  • timerlat — 타이머(Timer) 만료 시점부터 IRQ 핸들러(Handler)와 측정 스레드가 실제로 실행되기까지의 지연을 분리해 보여줍니다.
  • osnoise — 특정 CPU에서 커널이 사용자 작업을 얼마나 자주 끊는지 간섭 시간을 집계합니다.
  • cyclictest와 차이 — cyclictest는 결과 측정용 기준점이고, RTLA는 원인 추적용 계측 도구에 가깝습니다.
  • 운영 핵심 — 격리 CPU, IRQ affinity, threadirqs, nohz_full, rcu_nocbs 같은 주변 설정을 함께 봐야 의미가 생깁니다.

단계별 이해

  1. 증상 확인
    최대 지연 시간인지, 장기적인 잡음인지 먼저 구분합니다.
  2. 측정 대상 CPU 고정
    격리 CPU와 일반 CPU를 분리하고 IRQ 배치를 먼저 확인합니다.
  3. RTLA 실행
    timerlat 또는 osnoise로 어떤 종류의 간섭인지 좁힙니다.
  4. ftrace 교차 분석
    문제가 잡힌 순간의 IRQ, softirq, 스레드 전환을 trace로 확인합니다.
  5. 정책 수정 후 재측정
    IRQ affinity, 커널 스레드(Kernel Thread) 배치, CPU 격리 정책을 바꾼 뒤 같은 조건에서 다시 측정합니다.
문서 역할: 이 문서는 "레이턴시가 왜 튀는가"를 추적하는 전용 문서입니다. perf 서브시스템은 PMU/샘플링/프로파일링(Profiling) 자체를, 성능 최적화는 전체 튜닝 워크플로를, 디버깅(Debugging) & 트러블슈팅은 증상별 진입 전략을 맡습니다.

개요

RTLA(Real-Time Linux Analysis)는 리눅스 커널 tracing 인프라를 이용해 지연 시간과 잡음을 추적하는 도구 모음입니다. 일반적인 CPU 프로파일링 도구가 "어디에서 시간을 많이 썼는가"를 찾는다면, RTLA는 "왜 지금 당장 실행되어야 할 작업이 제때 깨어나지 못했는가"를 추적합니다.

특히 timerlat은 타이머 이벤트를 기준으로 인터럽트(Interrupt) 컨텍스트와 스레드 컨텍스트의 지연을 분리해 보여주고, osnoise는 격리 CPU에서 발생하는 커널 간섭 시간을 누적해 보여줍니다. 따라서 PREEMPT_RT 환경, 저지연 오디오, 산업 제어, 고빈도 트레이딩, 실시간(Real-time) 네트워크 패킷(Packet) 처리처럼 지연 시간 상한이 중요한 워크로드에서 유용합니다.

RTLA는 커널 트리의 tools/tracing/rtla/ 디렉터리에 소스가 있으며, 커널 빌드 시 함께 컴파일됩니다. 별도의 패키지 설치 없이 make -C tools/tracing/rtla/로 빌드할 수 있고, 배포판에 따라 rtla 패키지로 제공되기도 합니다. 커널 6.1부터 공식 통합되었으며, 이전 버전에서는 tracefs를 직접 사용해야 합니다.

# RTLA 설치 확인
which rtla
rtla --help

# 커널 소스에서 직접 빌드
cd /usr/src/linux
make -C tools/tracing/rtla/
sudo make -C tools/tracing/rtla/ install

# tracefs 마운트 확인 (RTLA 동작 전제)
mount | grep tracefs
# tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)

# 필수 커널 tracer 확인
cat /sys/kernel/tracing/available_tracers | tr ' ' '\n' | grep -E 'timerlat|osnoise|hwlat'
# timerlat
# osnoise
# hwlat
도구무엇을 측정하는가잘 잡는 문제한계
cyclictest주기 스레드의 wakeup 지연최대 지연 시간 확인, 회귀 비교왜 지연이 생겼는지는 직접 설명하지 못함
rtla timerlat타이머 만료 후 IRQ 지연 + 스레드 지연IRQ 폭주, softirq, 스케줄링 지연애플리케이션 전체 경로 지연 자체는 직접 측정하지 않음
rtla osnoiseCPU를 방해한 커널 간섭 시간격리 CPU 오염, background noise, 관리 스레드 간섭간섭 원인 식별을 위해 추가 trace가 필요함
perf sched스케줄링 이벤트 전반런큐(Runqueue) 대기, 문맥 전환(Context Switch) 패턴지연 상한 추적보다 일반 분석에 적합

레이턴시를 읽는 기준 모델

레이턴시 숫자만 보면 원인 분리가 안 됩니다. 실제로는 타이머 인터럽트가 늦게 도착했는지, 인터럽트는 빨랐지만 측정 스레드가 런큐에서 밀렸는지, 또는 격리 CPU에 원하지 않는 커널 작업이 끼어들었는지를 따로 읽어야 합니다.

타이머 만료 기준 시각 IRQ 실행 하드 IRQ 지연 측정 스레드 실행 스케줄링 지연 최종 레이턴시 최대값 기록 IRQ 지연 원인 인터럽트 비활성 구간 NMI 이외 경합 긴 하드 IRQ 실행 스레드 지연 원인 runqueue 대기 우선순위 역전 softirq / kthread 간섭 osnoise가 보는 것 커널이 CPU를 점유한 총 시간 IRQ, softirq, 스레드 간섭 격리 CPU 오염 여부

실전에서는 보통 다음 순서로 읽습니다. 먼저 timerlat으로 지연이 IRQ 단계인지 스레드 단계인지 나누고, 그 다음 osnoise로 해당 CPU가 전체적으로 얼마나 자주 간섭받는지 봅니다. 마지막으로 ftrace 이벤트나 trace-cmd로 문제 순간의 호출 경로를 복원합니다.

이 3단계 접근법의 핵심은 측정 계층을 분리하는 것입니다. 같은 "50μs 지연"이라도 IRQ 단계에서 40μs가 소모되었다면 인터럽트 관련 설정을 바꿔야 하고, 스레드 단계에서 45μs가 소모되었다면 스케줄링 정책을 재설계해야 합니다. 이 구분 없이 "지연이 크다"는 사실만 가지고 튜닝하면 잘못된 레이어를 건드리게 되어 시간을 낭비하거나 오히려 상황을 악화시킬 수 있습니다.

tracefs 경로: RTLA의 모든 기능은 /sys/kernel/tracing/(또는 레거시 경로 /sys/kernel/debug/tracing/)의 tracefs 인터페이스를 통해 동작합니다. rtla 명령은 이 인터페이스의 편의 프론트엔드입니다. tracefs가 마운트(Mount)되어 있지 않으면 mount -t tracefs tracefs /sys/kernel/tracing으로 수동 마운트할 수 있습니다.

RTLA 도구 구성과 출력 해석

RTLA는 커널 tracing 기반의 여러 모드를 제공합니다. 이 문서에서는 실무에서 가장 많이 쓰이는 timerlatosnoise에 집중합니다.

# timerlat: 순간 지연의 원인 분리
sudo rtla timerlat top
sudo rtla timerlat hist
sudo rtla timerlat trace

# osnoise: CPU 간섭 누적량 분석
sudo rtla osnoise top
sudo rtla osnoise hist
sudo rtla osnoise trace
모드주요 용도언제 먼저 쓰는가
top지금 가장 문제가 큰 CPU와 최대값 확인문제가 재현되는지 빠르게 보고 싶을 때
hist지연 시간 분포 확인평균보다 꼬리 지연 분포를 보고 싶을 때
trace최대 지연이 발생한 순간의 trace 확보원인 함수와 이벤트를 추적하고 싶을 때
# timerlat top 출력 예시:
#                  Timer Latency
# CPU  ID       IRQ         Thread
#                cur    max    cur    max
#  0   #42       1      5      3     12
#  1   #42       1      8      2     15
#  2   #42       0      2      1      4
#  3   #42       0      1      1      3
# ─────────────────────────────────────
# 해석:
# - cur: 현재 주기의 지연 (μs)
# - max: 측정 시작 이후 최대 지연 (μs)
# - CPU 2,3: 격리 CPU → IRQ/Thread max가 낮음 → 양호
# - CPU 0,1: 일반 CPU → IRQ max 높음 → 디바이스 IRQ 영향
해석 원칙: 한 번의 큰 스파이크와 지속적인 잔잡음은 원인이 다를 수 있습니다. 단발성 최대값은 긴 IRQ, SMI, 드라이버 경합(Contention)일 가능성이 높고, 지속적인 작은 잡음은 격리 CPU에 남아 있는 timer tick, workqueue, ksoftirqd, housekeeping 태스크(Task)일 가능성이 높습니다.

timerlat top의 출력에서 IRQ 열은 타이머 인터럽트 핸들러가 실행되기까지의 지연이고, Thread 열은 타이머 만료부터 측정 스레드가 실행되기까지의 총 지연입니다. 따라서 Thread 값에서 IRQ 값을 빼면 순수 스케줄링 지연을 알 수 있습니다. 이 두 값의 비율이 튜닝 방향의 핵심 지표입니다.

timerlat 실전 워크플로

timerlat은 타이머 이벤트를 이용해 "예정된 시각"과 "실제 실행된 시각"의 차이를 측정합니다. 이때 IRQ 지연과 스레드 지연을 분리해 주기 때문에, 단순 wakeup 지연만 보여 주는 도구보다 원인 구분이 빠릅니다.

# 1. 먼저 현재 시스템의 최악 지연을 빠르게 확인
sudo rtla timerlat top

# 2. 분포가 필요하면 히스토그램으로 확인
sudo rtla timerlat hist

# 3. 최대값이 튀는 순간의 trace 확보
sudo rtla timerlat trace

# 4. 보조 지표: cyclictest로 회귀 비교
sudo cyclictest -m -S -p 95 -i 1000 -h 100 -q

일반적인 해석 흐름은 다음과 같습니다.

  1. IRQ 지연이 크다
    하드 IRQ가 늦게 실행되었다는 뜻입니다. 인터럽트 비활성 구간, 긴 다른 IRQ 처리, 펌웨어성 잡음, 특정 드라이버의 장시간 IRQ 핸들러를 의심합니다.
  2. 스레드 지연이 크다
    IRQ는 빨랐지만 측정 스레드가 제때 스케줄되지 못했다는 뜻입니다. 런큐 혼잡, 우선순위(Priority), RT 스로틀링, softirq 후속 처리, 원치 않는 커널 스레드 간섭을 확인합니다.
  3. 둘 다 크다
    CPU 격리 정책이나 시스템 전체 잡음이 부족할 가능성이 큽니다. housekeeping CPU 분리, IRQ affinity, threadirqs, nohz_full, rcu_nocbs 설정을 다시 점검합니다.
주의: timerlat 수치가 곧바로 애플리케이션 응답 시간은 아닙니다. 이 수치는 커널 레벨에서 "언제 깨웠어야 했고, 실제로 언제 실행됐는가"를 보는 값이므로, 유저 공간 큐잉 지연, 잠금(Lock) 대기, I/O 대기까지 포함한 종단 간 지연과는 구분해서 읽어야 합니다.

timerlat trace 모드는 threshold를 초과한 지연이 발생하면 해당 시점의 커널 trace를 자동으로 저장합니다. 이 trace에는 IRQ 진입/종료, 스케줄러 전환, softirq 처리 등이 기록되어 있어 "왜 그 순간에 지연이 발생했는지"를 추적할 수 있습니다.

# threshold 50μs 초과 시 trace 저장
sudo rtla timerlat trace -T 50 -c 2,3

# trace 파일은 현재 디렉터리에 저장됨
# timerlat_trace_cpu2_20260318_143025.txt

# stop threshold 사용: 값 초과 시 즉시 tracing 중단
# 첫 번째 큰 지연만 잡고 싶을 때 유용
sudo rtla timerlat trace -T 100 -c 2 --stop-tracing

# trace 출력을 직접 파일로 저장
sudo rtla timerlat trace -T 50 -c 2 -o /tmp/rtla-trace.txt

# 측정 시간 제한과 함께 사용
sudo rtla timerlat trace -T 50 -c 2,3 -d 300s

trace 출력을 분석할 때는 다음 패턴을 주로 찾습니다.

패턴trace에서 보이는 증상대응
긴 IRQ 핸들러irq_handler_entryexit 사이 시간이 큼해당 IRQ를 다른 CPU로 이동
softirq 후속 처리softirq_entry(NET_RX 등)가 오래 실행ksoftirqd 배치 변경, RPS 설정
스케줄러 전환 대기sched_switch까지 대기 시간(Latency)이 큼우선순위 조정, CPU 격리 강화
RCU 콜백(Callback)rcu_callback 이벤트가 격리 CPU에서 발생rcu_nocbs 설정 확인
워크큐workqueue_execute_start가 격리 CPU에서 발생workqueue CPU affinity 확인

osnoise와 격리 CPU 잡음 분석

osnoise는 특정 CPU에서 커널이 사용자 작업을 얼마나 방해했는지를 시간 단위로 집계합니다. 실시간 스레드가 "왜 이유 없이 흔들리는가"를 볼 때 매우 유용합니다. 특히 격리 CPU를 운영 중인데도 지연이 계속 튈 때 원인 범위를 좁히는 데 적합합니다.

# 격리 CPU의 간섭 누적량 확인
sudo rtla osnoise top

# 분포 확인
sudo rtla osnoise hist

# 큰 간섭 순간 trace 확보
sudo rtla osnoise trace

# 격리 정책 확인용 보조 정보
cat /proc/cmdline
grep . /sys/devices/system/cpu/isolated 2>/dev/null

osnoise에서 자주 문제로 드러나는 항목은 다음과 같습니다.

이 도구는 "왜 느렸는지"를 바로 한 줄로 답하지는 않지만, "그 CPU가 실제로 조용했는가"를 판단하는 가장 빠른 기준점이 됩니다. 따라서 PREEMPT_RT 튜닝 초기에 반드시 한 번은 확인할 가치가 있습니다.

# osnoise top 상세 옵션
# -r: 측정 구간(runtime) 설정
sudo rtla osnoise top -r 5s -c 2,3

# -T: threshold 초과 시만 표시
sudo rtla osnoise top -T 10 -c 2,3

# osnoise trace: 간섭 발생 시 trace 확보
sudo rtla osnoise trace -T 20 -c 2,3 -d 60s

# trace 출력에서 간섭 원인 확인
# osnoise가 기록하는 tracepoint:
# - osnoise:irq_noise (IRQ 간섭)
# - osnoise:softirq_noise (softirq 간섭)
# - osnoise:thread_noise (스레드 간섭)
# - osnoise:nmi_noise (NMI 간섭)
# - osnoise:sample_threshold (threshold 초과)

osnoise의 출력에서 %CPU 열은 측정 기간 동안 측정 스레드가 실제로 CPU를 사용한 비율입니다. 이 값이 99.99%에 가까우면 해당 CPU가 매우 조용하다는 뜻이고, 99%보다 낮으면 상당한 간섭이 있다는 뜻입니다. 격리 CPU에서 이 값이 99.99% 미만이면 원인을 추적해야 합니다.

osnoise와 timerlat의 관계: osnoise는 "이 CPU가 전반적으로 얼마나 시끄러운가"를 보여주고, timerlat은 "특정 순간에 어떤 종류의 지연이 발생했는가"를 보여줍니다. 둘을 함께 사용하면 가장 효과적입니다. osnoise가 높으면 해당 CPU의 격리 설정을 먼저 점검하고, timerlat으로 구체적인 지연 분리를 합니다. 자세한 tracing 기법은 ftrace / Tracepoints 문서를 참고하세요.

실전 시나리오

증상우선 도구주요 원인 후보다음 확인
주기 태스크의 최대 지연이 드물게 크게 튐rtla timerlat trace긴 IRQ, 드라이버 ISR, 펌웨어(Firmware) 간섭ftrace로 IRQ 경로 추적
격리 CPU가 계속 잔잔하게 흔들림rtla osnoise histhousekeeping 태스크, softirq, kworkerIRQ affinity, workqueue 배치 확인
cyclictest 최댓값은 큰데 CPU 사용률은 낮음timerlat + osnoise스케줄링 지연, 주파수 전환, C-stateperf, Intel PCM 보조 분석
PREEMPT_RT 적용 후에도 지연 개선이 미미함osnoiseIRQ 스레드화 미흡, RT 우선순위 설계 문제스케줄러, RT Mutex 점검
권장 조합: 회귀 비교는 cyclictest, 원인 분리는 rtla timerlat, 격리 CPU 청결도 확인은 rtla osnoise, 세부 함수 추적은 ftrace/trace-cmd로 나누면 역할이 깔끔합니다.

cyclictest와의 비교

cyclictest는 리눅스 실시간 커뮤니티에서 가장 오래 사용된 지연 측정 도구이며, RTLA가 등장한 이후에도 여전히 중요한 역할을 합니다. 두 도구의 관계를 정확히 이해하면 효율적으로 조합할 수 있습니다.

cyclictest사용자 공간(User Space) 스레드clock_nanosleep()을 호출하고, 실제로 깨어난 시각과 예정된 시각의 차이를 기록합니다. 이 방식은 커널 → 사용자 공간 전체 경로의 지연을 포함하므로 "최종 사용자가 체감하는 지연"에 가깝지만, 어디에서 지연이 발생했는지는 알 수 없습니다.

반면 timerlat커널 내부에서 hrtimer 콜백과 커널 스레드를 사용하여 IRQ 지연과 스레드 지연을 분리합니다. 따라서 원인 분석에 강하지만, 사용자 공간까지의 전체 경로 지연은 직접 측정하지 않습니다 (커널 6.3+ user-space tracing 제외).

비교 항목cyclictestrtla timerlat
측정 위치사용자 공간커널 내부 (6.3+: 사용자 공간 옵션)
지연 분리불가 (총 지연만)IRQ / 스레드 분리
원인 추적불가auto_analysis, trace 모드
회귀 비교적합 (오래된 기준값 축적)가능하지만 기준값 축적 부족
CI/CD 통합널리 사용가능 (JSON 출력 등)
오버헤드(Overhead)낮음약간 높음 (커널 tracer)
커널 요구없음 (사용자 공간)timerlat tracer 활성화
# cyclictest로 기준값 설정 (5분 측정)
sudo cyclictest -m -S -p 95 -i 1000 -D 300s -h 100 -q -a 2,3

# 출력 예시:
# T: 0 (   42) P:95 I:1000 C: 300000 Min:      1 Act:    2 Avg:    2 Max:      11
# T: 1 (   43) P:95 I:1500 C: 200000 Min:      1 Act:    2 Avg:    2 Max:       9

# 같은 조건에서 timerlat으로 원인 분석
sudo rtla timerlat hist -c 2,3 -P 95 -d 300s -b 1 -e 50

# cyclictest max가 50μs 이상이면 timerlat trace로 원인 추적
sudo rtla timerlat trace -T 50 -c 2,3 -P 95
실전 패턴: CI 파이프라인(Pipeline)에서는 cyclictest로 회귀를 감지하고, 회귀가 감지되면 rtla timerlat trace로 원인을 추적하는 2단계 접근이 효과적입니다. cyclictest의 최대값이 이전 빌드보다 유의미하게 증가하면 알람을 발생시키고, 해당 빌드에서 timerlat으로 분석하는 식입니다.

커널 설정과 운영 체크리스트

RTLA를 제대로 활용하려면 도구만 실행해서는 부족합니다. tracing 기능과 CPU 격리 설정이 같이 갖춰져야 합니다.

# tracing / 레이턴시 분석에 필요한 대표 CONFIG
CONFIG_FTRACE=y
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_TRACEPOINTS=y
CONFIG_TRACING=y
CONFIG_HIST_TRIGGERS=y
CONFIG_PREEMPT=y
CONFIG_PREEMPT_RT=y              # RT 커널 환경이면

# 부트 파라미터 예시 (워크로드에 맞게 조정)
threadirqs
nohz_full=2-3
rcu_nocbs=2-3
irqaffinity=0-1
isolcpus=domain,managed_irq,2-3

tracefs 직접 사용

rtla 명령이 없는 환경(커널 6.1 이전 또는 도구 미설치)에서도 tracefs를 직접 사용하여 timerlat/osnoise tracer를 동작시킬 수 있습니다. 이 방법은 임베디드 환경이나 최소 설치 서버에서 유용합니다.

# === timerlat tracer 직접 사용 ===

# 1. 현재 tracer 확인
cat /sys/kernel/tracing/current_tracer
# nop

# 2. timerlat tracer 활성화
echo timerlat > /sys/kernel/tracing/current_tracer

# 3. 타이머 주기 설정 (μs)
echo 1000 > /sys/kernel/tracing/osnoise/timerlat_period_us

# 4. threshold 설정 (μs) — 이 값 초과 시 trace 기록
echo 50 > /sys/kernel/tracing/tracing_thresh

# 5. 측정 대상 CPU 설정
echo 2-3 > /sys/kernel/tracing/osnoise/cpus

# 6. tracing 시작
echo 1 > /sys/kernel/tracing/tracing_on

# 7. 일정 시간 대기
sleep 10

# 8. tracing 중단
echo 0 > /sys/kernel/tracing/tracing_on

# 9. trace 결과 확인
cat /sys/kernel/tracing/trace

# 10. tracer 비활성화
echo nop > /sys/kernel/tracing/current_tracer

# === osnoise tracer 직접 사용 ===
echo osnoise > /sys/kernel/tracing/current_tracer
echo 2-3 > /sys/kernel/tracing/osnoise/cpus
echo 1000000 > /sys/kernel/tracing/osnoise/sample_period  # 1초
echo 1000000 > /sys/kernel/tracing/osnoise/sample_runtime  # 1초
echo 1 > /sys/kernel/tracing/tracing_on
sleep 10
echo 0 > /sys/kernel/tracing/tracing_on
cat /sys/kernel/tracing/trace
tracefs 경로설명기본값
osnoise/timerlat_period_ustimerlat 타이머 주기1000
osnoise/cpus측정 대상 CPU 마스크모든 CPU
osnoise/sample_periodosnoise 샘플 기간 (μs)1000000
osnoise/sample_runtimeosnoise 샘플 실행 시간 (μs)1000000
tracing_threshtrace 기록 threshold (μs)0 (모두 기록)
osnoise/print_stackthreshold 초과 시 스택 출력0
동시 사용 주의: tracefs는 한 번에 하나의 tracer만 활성화할 수 있습니다. timerlat과 osnoise를 동시에 실행할 수 없고, function_graph 등 다른 tracer와도 동시 사용이 불가합니다. rtla가 실행 중일 때 tracefs를 직접 조작하면 충돌할 수 있으므로, 둘 중 하나만 사용해야 합니다.

tracefs 파라미터 상세 참조

tracefs를 직접 사용할 때 자주 참조하는 파라미터와 설정 순서를 정리합니다.

# === tracefs 파라미터 참조 ===

# 공통 파라미터
echo 0 > /sys/kernel/tracing/tracing_on       # tracing 중단
echo 1 > /sys/kernel/tracing/tracing_on       # tracing 시작
echo > /sys/kernel/tracing/trace               # trace 버퍼 초기화
cat /sys/kernel/tracing/current_tracer         # 현재 tracer
cat /sys/kernel/tracing/available_tracers      # 사용 가능한 tracer

# trace 버퍼 크기 조절 (per-CPU)
echo 4096 > /sys/kernel/tracing/buffer_size_kb  # CPU당 4MB

# osnoise 전용 파라미터
cat /sys/kernel/tracing/osnoise/cpus             # 측정 CPU 목록
cat /sys/kernel/tracing/osnoise/period_us        # osnoise 주기
cat /sys/kernel/tracing/osnoise/runtime_us       # osnoise 런타임
cat /sys/kernel/tracing/osnoise/stop_tracing_us  # stop threshold
cat /sys/kernel/tracing/osnoise/timerlat_period_us  # timerlat 주기

# timerlat 전용 파라미터 (6.3+)
cat /sys/kernel/tracing/osnoise/timerlat_fd       # user-space fd

# print_stack: threshold 초과 시 커널 스택 출력
echo 1 > /sys/kernel/tracing/osnoise/print_stack
# 지연 원인의 커널 함수 스택을 보여줌

print_stack 옵션은 rtla의 auto_analysis가 없는 환경에서 특히 유용합니다. threshold를 초과한 지연이 발생하면 해당 시점의 커널 스택 트레이스를 trace 버퍼(Buffer)에 기록하여, 어떤 커널 함수가 CPU를 잡고 있었는지를 직접 보여줍니다. 커널 심볼(Kernel Symbol) 정보가 있으면 함수 이름이 해석되어 더 읽기 쉬워집니다.

timerlat 내부 아키텍처

timerlathrtimer(고해상도 타이머(hrtimer))를 기반으로 동작하는 커널 내장 tracer입니다. 단순히 사용자 공간에서 타이머를 돌리는 cyclictest와 달리, 커널 안에서 직접 타이머 콜백을 설치하고 IRQ 컨텍스트에서 즉시 시간을 기록하기 때문에 인터럽트 지연과 스레드 지연을 정밀하게 분리할 수 있습니다.

timerlat가 활성화되면 각 측정 대상 CPU마다 per-CPU 커널 스레드(timerlat/N)가 생성됩니다. 이 스레드는 FIFO 정책의 높은 우선순위로 실행되며, hrtimer 콜백이 깨울 때까지 대기합니다. 타이머가 만료되면 다음 두 단계가 순서대로 실행됩니다.

  1. IRQ 컨텍스트 단계 — hrtimer 콜백 함수 timerlat_irq()가 인터럽트 컨텍스트에서 호출됩니다. 이 함수는 "타이머가 만료된 시각"과 "실제 IRQ 핸들러가 실행된 시각"의 차이를 기록하고, 대기 중인 timerlat/N 스레드를 wake_up_process()로 깨웁니다.
  2. 스레드 컨텍스트 단계 — 깨어난 timerlat/N 스레드의 timerlat_main() 함수가 실행됩니다. 이 함수는 "wake_up이 호출된 시각"과 "스레드가 실제로 CPU를 얻은 시각"의 차이를 기록합니다.
hrtimer 만료 기준 시각 T₀ 기록 timerlat_irq() IRQ 지연 = now - T₀ wake_up_process() T₁ 시각 기록 timerlat_main() 스레드 지연 기록 per-CPU 스레드: timerlat/0, timerlat/1, timerlat/2, ... SCHED_FIFO 우선순위 (기본 95) · set_current_state(TASK_INTERRUPTIBLE) 대기 루프 TASK_COMM_LEN=16이므로 "timerlat/NNN" 형태 (CPU 번호 3자리까지) struct timerlat_sample u64 timer_latency; ← IRQ 지연 (ns) u64 thread_latency; ← 스레드 지연 (ns) unsigned int seqnum; ← 시퀀스 번호 int context; ← IRQ(0) / Thread(1) unsigned int counter; ← CPU별 카운터 측정 흐름 요약 1. hrtimer_start() → 주기 설정 2. 콜백 진입 → ktime_get() 차분 3. trace_timerlat_sample() 이벤트 4. wake_up → 스레드 깨우기 5. 스레드 실행 → 스레드 지연 기록

timerlat의 타이머 주기는 기본 1ms(1000μs)이며, -p 옵션으로 조절할 수 있습니다. 주기가 짧을수록 측정 정밀도가 높지만 오버헤드도 커지므로, 보통 500μs~2000μs 사이에서 목적에 맞게 선택합니다.

/* kernel/trace/trace_osnoise.c — timerlat_irq() 핵심 경로 (간략화) */
static enum hrtimer_restart timerlat_irq(struct hrtimer *timer)
{
    struct osnoise_variables *osn_var = this_cpu_osn_var();
    struct timerlat_variables *tlat;
    struct timerlat_sample s;
    u64 now, diff;

    tlat = this_cpu_tmr_var();
    now = ktime_to_ns(hrtimer_cb_get_time(timer));

    /* IRQ 지연 = 현재 시각 - 예정된 만료 시각 */
    diff = now - tlat->abs_period;
    s.timer_latency = diff;
    s.context = 0;  /* IRQ context */

    trace_timerlat_sample(&s);

    /* 측정 스레드를 깨움 */
    if (tlat->kthread)
        wake_up_process(tlat->kthread);

    /* 다음 주기 설정 */
    hrtimer_forward_now(timer, ns_to_ktime(tlat->timer_period));
    return HRTIMER_RESTART;
}
TASK_COMM_LEN 제한: 커널에서 태스크 이름은 TASK_COMM_LEN(보통 16바이트)으로 제한됩니다. 따라서 timerlat/ 접두사(9바이트)를 빼면 CPU 번호가 6자리까지 가능합니다. 실제로는 수백 CPU 시스템에서도 문제가 없지만, 커스텀 트레이싱 스크립트에서 이름 파싱 시 이 제한을 알고 있으면 유용합니다.

timerlat 내부 동작 상세

timerlat tracer가 활성화되면 내부적으로 다음 과정이 일어납니다.

  1. 스레드 생성 — 각 측정 대상 CPU마다 kthread_create()로 커널 스레드를 생성하고, kthread_bind()로 해당 CPU에 고정합니다.
  2. 우선순위 설정sched_setscheduler()로 SCHED_FIFO 정책과 지정된 우선순위를 설정합니다.
  3. hrtimer 등록hrtimer_init()hrtimer_start()로 주기적 고해상도 타이머를 시작합니다.
  4. 대기 루프 진입 — 스레드는 set_current_state(TASK_INTERRUPTIBLE)로 대기 상태에 진입합니다.
  5. 콜백 실행 — 타이머 만료 시 timerlat_irq()가 IRQ 컨텍스트에서 호출되어 IRQ 지연을 기록하고 스레드를 깨웁니다.
  6. 스레드 실행 — 깨어난 스레드의 timerlat_main()이 스레드 지연을 기록합니다.
  7. 반복 — 다음 주기의 타이머가 이미 설정되어 있으므로, 스레드는 다시 대기 상태로 돌아갑니다.
/* timerlat_main() — 스레드 컨텍스트 핵심 경로 (간략화) */
static int timerlat_main(void *data)
{
    struct timerlat_variables *tlat = this_cpu_tmr_var();
    struct timerlat_sample s;
    u64 now, diff;

    while (!kthread_should_stop()) {
        set_current_state(TASK_INTERRUPTIBLE);
        schedule();  /* wake_up_process()까지 대기 */

        /* 스레드가 깨어남 — 스레드 지연 측정 */
        now = ktime_get_ns();
        diff = now - tlat->abs_period;

        s.thread_latency = diff;
        s.context = 1;  /* Thread context */
        s.seqnum = tlat->count;

        trace_timerlat_sample(&s);

        /* 통계 갱신 */
        tlat->count++;
    }
    return 0;
}

이 구조의 핵심 장점은 측정 경로가 매우 짧다는 것입니다. timerlat_irq()와 timerlat_main()은 각각 시간 차분 계산과 trace 이벤트 발생만 하므로, 측정 자체의 오버헤드가 최소화됩니다. 이 점이 cyclictest(사용자 공간에서 시스템 콜(System Call)을 통해 측정)와의 근본적인 차이입니다.

timerlat tracepoint 이벤트

timerlat은 tracefs의 tracepoint 시스템을 사용하여 이벤트를 기록합니다. 이 이벤트들은 trace-cmd, perf record, BPF 프로그램에서도 사용할 수 있습니다.

# 사용 가능한 timerlat/osnoise 이벤트 확인
ls /sys/kernel/tracing/events/osnoise/
# irq_noise
# nmi_noise
# sample_threshold
# softirq_noise
# thread_noise

ls /sys/kernel/tracing/events/timerlat/ 2>/dev/null
# timerlat_sample

# 이벤트 형식 확인
cat /sys/kernel/tracing/events/osnoise/irq_noise/format
# name: irq_noise
# ID: 1234
# format:
#   field:unsigned long long duration;  offset:8; size:8;
#   field:unsigned long long start;     offset:16; size:8;
#   field:unsigned int irq;             offset:24; size:4;
#   field:char desc[20];                offset:28; size:20;

# perf에서 osnoise 이벤트 사용
sudo perf record -e osnoise:irq_noise -e osnoise:softirq_noise \
  -e osnoise:thread_noise -a -C 2,3 sleep 10
sudo perf script | head -20

이 tracepoint들은 BPF(eBPF) 프로그램에서도 접근 가능합니다. bpftrace를 사용하면 실시간 통계 수집이나 커스텀 필터링을 할 수 있습니다.

# bpftrace로 IRQ 간섭 모니터링 (간단 예제)
sudo bpftrace -e '
tracepoint:osnoise:irq_noise {
    @irq_duration[args->desc] = hist(args->duration / 1000);
}
interval:s:10 { print(@irq_duration); clear(@irq_duration); }
'

timerlat 스레드의 우선순위는 기본값이 SCHED_FIFO:95입니다. 이보다 높은 우선순위의 RT 스레드가 있으면 스레드 지연에 포함되므로, 측정 목적에 따라 -P 옵션으로 우선순위를 조절해야 합니다. 실제 워크로드가 SCHED_FIFO:80에서 동작한다면, 측정 스레드도 같은 우선순위로 놓아야 현실적인 지연을 볼 수 있습니다.

# 측정 스레드 우선순위를 80으로 설정
sudo rtla timerlat top -P 80

# 특정 CPU만 측정 (CPU 2,3)
sudo rtla timerlat top -c 2,3

# 타이머 주기를 500μs로 변경
sudo rtla timerlat top -p 500

# 측정 시간 제한 (60초)
sudo rtla timerlat top -d 60s
옵션설명기본값실전 팁
-p <μs>타이머 주기1000μs500μs 미만은 오버헤드 주의
-P <prio>측정 스레드 우선순위95실제 RT 태스크와 동일하게 맞추면 현실적
-c <cpus>측정 대상 CPU모든 CPU격리 CPU만 지정하면 잡음이 줄어듦
-d <time>측정 시간 제한무한CI/CD에서는 반드시 설정
-t <μs>threshold (trace 모드)0이 값 초과 시만 trace 기록
-T <μs>stop threshold0이 값 초과 시 tracing 중단 후 저장

osnoise 간섭 소스 분류

osnoise는 "측정 기간 동안 이 CPU에서 커널이 얼마나 개입했는가"를 집계합니다. 타이머 기반으로 특정 순간을 보는 timerlat과 달리, osnoise는 일정 시간 구간 전체를 스캔하면서 누적 간섭량을 보고합니다.

osnoise의 커널 구현은 루프 기반 측정을 사용합니다. 측정 스레드가 CPU를 점유한 채 타이트 루프를 돌면서, 루프 반복 사이의 시간 차이가 예상보다 크면 "무언가가 이 CPU를 가져갔다"고 판단합니다. 가져간 주체를 NMI, IRQ, softirq, thread의 네 가지로 분류하여 각각의 누적 시간과 횟수를 기록합니다.

osnoise 측정 구간 (1초 기본) NMI IRQ softirq thread IRQ 간섭 유형별 분류 NMI — 마스크 불가, SMI 포함. 펌웨어/하드웨어 원인 IRQ — 하드웨어 인터럽트. NIC, 타이머, 스토리지 컨트롤러 등 softirq — 네트워크 수신(NET_RX), 블록 I/O, 타이머, RCU 등 thread — 커널 스레드 (kworker, ksoftirqd, migration, watchdog 등) osnoise 출력 핵심 필드 runtime ← 측정 스레드 실행 시간 (us) noise ← 총 간섭 시간 (us) %CPU ← runtime / period × 100 max_single ← 단일 최대 간섭 시간 (us) 핵심 비율 해석 noise / runtime < 0.1% → 매우 깨끗 noise / runtime 0.1~1% → 보통 noise / runtime > 1% → 격리 재점검 필요 max_single > 목표 지연 → 즉시 원인 추적
# osnoise top 출력 예시 해석
# CPU  runtime(us)  noise(us)  %CPU   max_single(us)  NMI  IRQ  SOFTIRQ  THREAD
#  0    999800       12         99.99  5               0    3    1        0
#  1    999750       45         99.99  18              0    8    2        1
#  2    999990       3          99.99  2               0    1    0        0

# 해석:
# - CPU 2: noise 3μs → 매우 깨끗한 격리 CPU
# - CPU 1: max_single 18μs, IRQ 8회 → NIC 또는 타이머 IRQ 확인 필요
# - CPU 0: noise 12μs, SOFTIRQ 1회 → softirq 후속 처리 확인

측정 기간(-r, runtime)과 간섭 threshold(-T)를 조합하면 특정 수준 이상의 간섭만 추적할 수 있습니다.

# 측정 구간 5초, threshold 10μs 초과 시만 기록
sudo rtla osnoise top -r 5s -T 10

# 격리 CPU만 측정
sudo rtla osnoise top -c 2,3

# 간섭 발생 시 trace 확보
sudo rtla osnoise trace -c 2,3 -T 20
측정 자체의 간섭: osnoise 측정 스레드 자체가 CPU를 점유하므로, 실제 워크로드와 동시에 같은 CPU에서 실행하면 서로 영향을 줍니다. 격리 CPU의 청결도를 보려면 워크로드 없이 측정하는 것이 원칙이며, 워크로드와 함께 보려면 별도 CPU를 사용해야 합니다.

간섭 유형별 대응 전략

osnoise 출력의 NMI, IRQ, SOFTIRQ, THREAD 열을 보면 어떤 유형의 간섭이 주된 원인인지 알 수 있습니다. 각 유형에 따라 대응 방법이 다릅니다.

간섭 유형주요 원인대응 방법확인 명령
NMIwatchdog, perf PMU, SMInmi_watchdog=0, perf 중지cat /proc/sys/kernel/nmi_watchdog
IRQNIC, 타이머, 스토리지, USBIRQ affinity 이동cat /proc/interrupts
SOFTIRQNET_RX, BLOCK, TIMER, RCUIRQ 원천 이동, RPS, rcu_nocbscat /proc/softirqs
THREADkworker, ksoftirqd, migrationcgroup, WQ affinity, isolcpusps -eo psr,pid,comm
# 간섭 유형별 원인 추적

# NMI 간섭이 높을 때
echo 0 | sudo tee /proc/sys/kernel/nmi_watchdog  # NMI watchdog 비활성화
sudo perf stat --no-fork -e nmi:nmi_handler -a sleep 10  # NMI 횟수 확인

# IRQ 간섭이 높을 때
# 격리 CPU(2,3)에 배치된 IRQ 찾기
for irq in /proc/irq/*/smp_affinity_list; do
    affinity=$(cat $irq 2>/dev/null)
    # CPU 2 또는 3이 포함되어 있는지 확인
    if echo "$affinity" | grep -qE '(^|,)(2|3)(,|$|-)'; then
        irq_num=$(echo $irq | grep -oP '/proc/irq/\K[0-9]+')
        echo "IRQ $irq_num on CPUs: $affinity"
    fi
done

# SOFTIRQ 간섭이 높을 때
watch -n 1 'cat /proc/softirqs | head -5'
# NET_RX가 격리 CPU에서 증가하면 NIC IRQ를 먼저 이동

# THREAD 간섭이 높을 때
# 격리 CPU에서 실행 중인 커널 스레드 확인
ps -eo psr,pid,cls,rtprio,comm --sort=psr | awk '$1==2 || $1==3'
# kworker가 보이면 workqueue affinity 확인
cat /sys/devices/virtual/workqueue/cpumask
격리 CPU의 이상적인 osnoise 결과: 완벽하게 격리된 CPU에서 osnoise를 실행하면, noise가 0에 가깝고 NMI/IRQ/SOFTIRQ/THREAD 열이 모두 0이어야 합니다. 현실적으로는 noise < 5μs, max_single < 3μs 수준이면 매우 양호한 격리입니다. 이 수준에 도달하지 못하면 위 표의 대응 방법을 순서대로 적용합니다.

IRQ/thread 레이턴시 분리

timerlat의 핵심 가치는 레이턴시를 IRQ 컨텍스트스레드 컨텍스트로 분리해 보여주는 것입니다. 이 분리가 없으면 "지연이 있다"는 사실만 알 수 있지, "어느 단계에서 막혔는지"를 알 수 없습니다.

timerlat trace 출력에서 가장 중요한 두 줄은 다음과 같습니다.

# timerlat trace 출력 예시
# --------
#                                _-----=> irqs-off/BH-disabled
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| / _-=> migrate-disable
#                              |||| /     delay
#  TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#     | |         |   |||||     |         |
  <idle>-0      [002] d.h..  1234.567890: timerlat_irq: #42 context irq timer_latency 3 us
  <idle>-0      [002] .....  1234.567895: timerlat_main: #42 context thread timer_latency 8 us

위 출력에서 timerlat_irq 행의 3 us는 타이머 만료 시점부터 IRQ 핸들러가 실행되기까지의 지연이고, timerlat_main 행의 8 us는 타이머 만료 시점부터 스레드가 실행되기까지의 총 지연입니다. 따라서 순수 스레드 스케줄링 지연은 8 - 3 = 5μs입니다.

시간 흐름 → T₀ 타이머 만료 T₁ IRQ 실행 T₂ wake_up T₃ 스레드 실행 IRQ 지연 (T₁ - T₀) 스레드 스케줄링 지연 (T₃ - T₁) 총 지연 (T₃ - T₀) timerlat_context (IRQ) • irqs_disabled가 길면 IRQ 지연 ↑ • 다른 IRQ 선점, NMI, SMI 간섭 timerlat_context (Thread) • 런큐 대기, 우선순위 역전 • softirq, 다른 RT 스레드 선점

커널 6.3부터 추가된 auto_analysis 기능은 threshold를 초과한 지연이 발생하면 자동으로 원인을 분석해 trace 출력에 요약을 추가합니다. 이전에는 trace를 직접 읽고 해석해야 했지만, auto_analysis가 활성화되면 가장 가능성 높은 원인(어떤 IRQ가 오래 걸렸는지, 어떤 스레드가 CPU를 잡았는지)을 자동으로 지목합니다.

# auto_analysis 활성화 (커널 6.3+, 기본 활성)
sudo rtla timerlat trace -T 50 -c 2

# auto_analysis 비활성화 (raw trace만 원하는 경우)
sudo rtla timerlat trace -T 50 -c 2 --no-aa

# 출력 예시 (auto_analysis가 추가한 요약):
# ## CPU 2 hit stop tracing, analyzing it ##
# IRQ handler delay: (irq/18-eth0) 15.2 us
# IRQ latency:       18.5 us
# Timerlat IRQ duration: 0.8 us
# Blocking thread:   (kworker/2:1) 12.3 us
# Thread latency:    52.1 us
auto_analysis 해석 팁: "IRQ handler delay"가 크면 특정 하드 IRQ가 오래 실행된 것이고, "Blocking thread"가 크면 해당 커널 스레드가 CPU를 점유하고 있었다는 뜻입니다. 이 정보를 바탕으로 IRQ affinity를 조정하거나 해당 커널 스레드의 CPU 배치를 변경하는 식으로 대응합니다.
trace 필드의미높을 때 의심 대상
timer_latency (irq)타이머 만료 → IRQ 핸들러 실행까지인터럽트 비활성 구간, NMI, SMI, 다른 IRQ
timer_latency (thread)타이머 만료 → 스레드 실행까지 (총 지연)런큐 혼잡, softirq, kthread, RT 스로틀링
IRQ handler delayauto_analysis: 특정 IRQ 핸들러 실행 시간NIC, 스토리지, GPU 드라이버
Blocking threadauto_analysis: CPU를 점유한 스레드kworker, ksoftirqd, migration

IRQ 지연과 스레드 지연의 비율은 튜닝 방향을 결정짓는 중요한 지표입니다. IRQ 지연이 총 지연의 대부분을 차지하면 인터럽트 관련 문제(IRQ affinity, 드라이버 ISR 길이, 펌웨어 간섭)를 먼저 해결해야 하고, 스레드 지연이 대부분이면 스케줄링 관련 문제(우선순위, CPU 격리, softirq 배치)를 먼저 해결해야 합니다.

trace 출력 상세 분석 예제

실제 timerlat trace 출력을 해석하는 과정을 단계별로 살펴봅니다.

# timerlat trace 출력의 전체 예제
# 이 예제에서 CPU 2의 스레드 지연이 52μs로 threshold(50μs)를 초과

#                                _-----=> irqs-off/BH-disabled
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| / _-=> migrate-disable
#                              |||| /     delay
#  TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#     | |         |   |||||     |         |

# 1. 타이머 만료 전: CPU 2에서 kworker가 실행 중
 kworker/2:1-156  [002] d..1.  5765.047891: sched_switch: kworker/2:1 => swapper/2
     <idle>-0      [002] d..1.  5765.047893: sched_switch: swapper/2 => swapper/2

# 2. 네트워크 IRQ가 CPU 2에 진입 (이것이 간섭 원인)
     <idle>-0      [002] d.h1.  5765.047950: irq_handler_entry: irq=18 name=eth0
     <idle>-0      [002] d.h1.  5765.047965: irq_handler_exit: irq=18 ret=handled

# 3. timerlat IRQ 콜백 실행 — IRQ 지연 3μs
     <idle>-0      [002] d.h..  5765.047968: timerlat_irq: #5765 context irq timer_latency 3 us

# 4. softirq 후속 처리 (NET_RX) — 이것이 스레드 지연의 주 원인
     <idle>-0      [002] ..s..  5765.047970: softirq_entry: vec=3 [action=NET_RX]
     <idle>-0      [002] ..s..  5765.048010: softirq_exit: vec=3 [action=NET_RX]

# 5. timerlat 스레드 실행 — 총 지연 52μs
 timerlat/2-456  [002] .....  5765.048017: timerlat_main: #5765 context thread timer_latency 52 us

# 분석:
# - IRQ 지연: 3μs (양호)
# - 스레드 지연: 52 - 3 = 49μs (높음)
# - 원인: eth0 IRQ(18)가 CPU 2에 진입 → 후속 NET_RX softirq가 40μs 소모
# - 해결: echo 3 > /proc/irq/18/smp_affinity (IRQ를 CPU 0,1로 이동)

위 trace에서 핵심 단서를 찾는 순서는 다음과 같습니다.

  1. timerlat_main 행 확인 — 총 지연이 threshold를 초과했는지 봅니다.
  2. timerlat_irq 행 확인 — IRQ 지연이 큰지 작은지 봅니다. 작으면 스레드 지연이 문제입니다.
  3. 두 행 사이의 이벤트 확인 — timerlat_irq와 timerlat_main 사이에 어떤 이벤트가 끼어들었는지 봅니다.
  4. softirq, sched_switch, irq_handler 확인 — CPU를 가져간 주체를 식별합니다.
  5. 시간 차분 계산 — 각 이벤트의 타임스탬프 차이로 정확한 소모 시간을 계산합니다.

trace 출력의 플래그 열을 읽는 방법도 중요합니다. 각 문자의 의미를 정리합니다.

위치문자의미
1번째d인터럽트 비활성(irqs_disabled)
1번째.인터럽트 활성
2번째nneed_resched 플래그 설정됨
2번째.need_resched 없음
3번째h하드 IRQ 컨텍스트
3번째ssoftirq 컨텍스트
3번째.프로세스 컨텍스트
4번째숫자선점(Preemption) 깊이(preempt_count)
5번째숫자마이그레이션 비활성 깊이

예를 들어 d.h1.는 "인터럽트 비활성, resched 없음, 하드 IRQ 컨텍스트, 선점 깊이 1, 마이그레이션 비활성 없음"을 의미합니다. ..s..는 "인터럽트 활성, softirq 컨텍스트, 선점/마이그레이션 비활성 없음"입니다. 이 플래그는 해당 이벤트가 어떤 컨텍스트에서 발생했는지를 빠르게 파악하는 데 핵심적입니다. ftrace / Tracepoints 문서에서 이 플래그의 자세한 의미를 확인할 수 있습니다.

trace 해석 주의점: trace 출력의 타임스탬프는 monotonic clock 기반이므로, 벽시계 시간과 다릅니다. 또한 tracing 자체가 수 μs의 오버헤드를 가지므로, 1μs 이하의 차이는 해석에 주의가 필요합니다. 여러 번 측정하여 재현 가능한 패턴만 원인으로 판단해야 합니다.

히스토그램 해석법

timerlat histosnoise hist는 지연 분포를 히스토그램으로 보여줍니다. 평균값이나 최대값만으로는 보이지 않는 패턴을 찾는 데 필수적입니다. 특히 꼬리 지연(tail latency)바이모달 분포는 시스템 설정 오류나 간헐적 간섭의 강력한 증거입니다.

정상 분포 단일 피크, 좁은 분포 → 양호 바이모달 분포 두 피크 → C-state, 주파수 전환 의심 꼬리 지연 긴 꼬리 → 간헐적 간섭, SMI 의심 히스토그램 패턴별 대응 전략 정상 단봉: 피크 폭이 좁고 꼬리가 짧음. 시스템이 안정적. p99 ≈ p999 수준. 바이모달: 두 개의 피크가 보임. CPUfreq 전환, C-state 복귀, NUMA 원격 접근 등 두 가지 상태 전환. 긴 꼬리: 대부분 양호하지만 드물게 큰 지연 발생. SMI, 긴 IRQ, 드라이버 버그, 펌웨어 간섭 의심. 넓은 분포: 전반적으로 넓게 퍼짐. CPU 격리 미흡, housekeeping 스레드 오염, 전력 정책 미설정. 절벽(cliff): 특정 값에서 급격히 끊김. RT 스로틀링(rt_bandwidth), 하드웨어 타이머 해상도 제한.
# timerlat 히스토그램: 버킷 크기 1μs, 100개 엔트리
sudo rtla timerlat hist -b 1 -e 100 -c 2,3 -d 60s

# 출력 예시:
# Index   IRQ-002   Thr-002   IRQ-003   Thr-003
# 0       0         0         0         0
# 1       854       12        891       8
# 2       102       345       78        298
# 3       38        512       24        547
# 4       5         98        6         112
# 5       1         28        1         31
# 6       0         4         0         3
# 7       0         1         0         1
# over:   0         0         0         0
# count:  1000      1000      1000      1000
# min:    1         2         1         2
# avg:    1.4       3.1       1.3       3.2
# max:    5         7         5         7

# osnoise 히스토그램
sudo rtla osnoise hist -b 1 -e 50 -c 2,3 -d 60s

백분위수(percentile) 계산법: 히스토그램에서 p99는 "전체 샘플의 99%가 이 값 이하"라는 뜻입니다. 예를 들어 1000개 샘플에서 990번째로 작은 값이 p99입니다. p999(99.9번째 백분위)는 1000개 중 999번째입니다. 실시간 시스템에서는 최대값보다 p99/p999가 더 의미 있는 지표인 경우가 많습니다. 단발성 최대값은 통제 불가능한 하드웨어 이벤트일 수 있지만, p999가 높으면 시스템적 문제를 의미합니다.

옵션설명기본값
-b <μs>버킷(bucket) 크기1μs
-e <count>히스토그램 엔트리 수256
-d <time>측정 시간무한
-c <cpus>측정 대상 CPU모든 CPU
-P <prio>측정 스레드 우선순위95
버킷 크기 선택: 버킷이 너무 크면(예: 10μs) 미세한 패턴이 뭉개지고, 너무 작으면(예: 0.1μs) 의미 없는 잡음이 각 버킷에 분산됩니다. 목표 지연이 10μs 이하면 1μs 버킷이 적절하고, 100μs 수준이면 5μs 버킷이 읽기 좋습니다.

히스토그램 비교 분석

히스토그램의 가장 큰 가치는 변경 전/후 비교에 있습니다. 커널 업데이트, 설정 변경, 하드웨어 교체 전후의 히스토그램을 비교하면 어떤 변화가 레이턴시에 영향을 미쳤는지 시각적으로 알 수 있습니다.

# 변경 전 히스토그램 저장
sudo rtla timerlat hist -c 2,3 -P 95 -d 300s \
  -b 1 -e 100 > /tmp/before.txt

# (설정 변경 적용)

# 변경 후 히스토그램 저장
sudo rtla timerlat hist -c 2,3 -P 95 -d 300s \
  -b 1 -e 100 > /tmp/after.txt

# diff로 비교
diff /tmp/before.txt /tmp/after.txt

# 주요 비교 포인트:
# 1. max 값의 변화 — 최악 사례 개선 여부
# 2. avg 값의 변화 — 전반적 지연 변화
# 3. 분포 형태 변화 — 꼬리가 줄었는지, 바이모달이 해소되었는지
# 4. over 카운트 — 히스토그램 범위 초과 발생 여부

osnoise 히스토그램과의 교차 분석: timerlat hist와 osnoise hist를 같은 CPU에서 동시에 비교하면, 간섭량과 지연의 상관관계를 확인할 수 있습니다. osnoise 간섭이 높은 CPU에서 timerlat 지연도 높으면 격리 문제이고, osnoise는 깨끗한데 timerlat 지연이 높으면 스케줄링 설계 문제입니다.

히스토그램 기반 SLA 검증

서비스 수준 합의(SLA)에서 레이턴시 요구사항을 정의할 때, 히스토그램을 직접 증거로 사용할 수 있습니다.

# SLA 검증 예시: "p99 ≤ 10μs, max ≤ 50μs"
sudo rtla timerlat hist -c 2,3 -P 95 -d 3600s \
  -b 1 -e 100 > /tmp/sla-check.txt

# 결과에서 확인:
# max: 12  → max ≤ 50μs 충족
# p99 계산: count의 99%에 해당하는 버킷 확인
# 예: count=3600000, 99%=3564000
# 버킷 1~3에 3564000 이상이 누적되면 p99 ≤ 3μs

# 자동화된 SLA 판정
max_irq=$(grep "max:" /tmp/sla-check.txt | awk 'NR==1{print $2}')
max_thr=$(grep "max:" /tmp/sla-check.txt | awk 'NR==2{print $2}')

if [ "$max_thr" -le 50 ]; then
    echo "SLA PASS: max thread latency = ${max_thr}μs"
else
    echo "SLA FAIL: max thread latency = ${max_thr}μs (limit: 50μs)"
fi
# osnoise 히스토그램 (간섭 분포)
sudo rtla osnoise hist -c 2,3 -d 60s -b 1 -e 50 > /tmp/osnoise-hist.txt

# timerlat 히스토그램 (지연 분포)
sudo rtla timerlat hist -c 2,3 -d 60s -b 1 -e 50 > /tmp/timerlat-hist.txt

# 비교 관점:
# osnoise noise=0, timerlat thread_max=50 → 스케줄링 문제
# osnoise noise=200, timerlat thread_max=50 → 격리 문제 (상관)

PREEMPT_RT 환경 튜닝

PREEMPT_RT 커널에서 rtla의 진가가 발휘됩니다. RT 커널은 대부분의 IRQ를 스레드화하고, spinlock을 rt_mutex로 교체하며, softirq를 전용 스레드로 분리합니다. 이 구조에서는 커널 자체의 선점 가능성이 크게 높아지지만, 동시에 우선순위 설계와 CPU 격리 전략이 훨씬 더 중요해집니다.

CPU 격리 + PREEMPT_RT 부트 파라미터 조합 Housekeeping CPUs (0-1) irqaffinity=0-1 모든 디바이스 IRQ 수용 RCU 콜백, workqueue 처리 타이머 tick, housekeeping 태스크 시스템 데몬, 모니터링, 로깅 Isolated CPUs (2-3) isolcpus=domain,managed_irq,2-3 nohz_full=2-3 (tick 제거) rcu_nocbs=2-3 (RCU 콜백 위임) RT 워크로드 전용 최소한의 커널 개입 부트 파라미터 상세 isolcpus=domain,managed_irq,2-3 domain: 스케줄러 로드밸런싱에서 제외 | managed_irq: 관리 IRQ 자동 배치 차단 nohz_full=2-3 격리 CPU의 스케줄러 tick 제거 (태스크 1개만 실행 중일 때). jiffies 갱신 불필요. rcu_nocbs=2-3 RCU 콜백 처리를 rcuo 스레드로 위임. 격리 CPU에서 RCU softirq 제거. threadirqs 모든 IRQ를 강제 스레드화. PREEMPT_RT에서는 대부분 기본. 우선순위 제어 가능. irqaffinity=0-1 디바이스 IRQ의 기본 affinity를 housekeeping CPU로 설정.

PREEMPT_RT에서 레이턴시 튜닝의 핵심 원칙은 다음과 같습니다.

  1. 격리 CPU에서 불필요한 작업 완전 제거isolcpus, nohz_full, rcu_nocbs를 조합합니다.
  2. IRQ 스레드 우선순위 설계threadirqs 모드에서 IRQ 스레드의 기본 우선순위는 50입니다. RT 워크로드보다 높거나 같으면 간섭 원인이 됩니다.
  3. RT 스로틀링 확인/proc/sys/kernel/sched_rt_runtime_us가 -1이 아니면 RT 태스크가 CPU 시간의 일부만 사용할 수 있습니다.
  4. C-state/CPUfreq 제어 — 깊은 C-state 복귀 지연이 수십 μs에 달할 수 있습니다. processor.max_cstate=1 또는 idle=poll(전력 비용 주의)로 제한합니다.
# RT 스로틀링 확인 및 비활성화
cat /proc/sys/kernel/sched_rt_runtime_us
# 950000 → 1초당 950ms만 RT 사용 가능 (95%)
# -1 → 스로틀링 비활성화

# 격리 CPU에서는 RT 스로틀링 비활성화 권장
echo -1 | sudo tee /proc/sys/kernel/sched_rt_runtime_us

# C-state 제한 (부트 파라미터 또는 런타임)
# 부트: processor.max_cstate=1 intel_idle.max_cstate=0
# 런타임: cpupower idle-set -D 0

# CPUfreq governor 고정
sudo cpupower frequency-set -g performance

# IRQ 스레드 우선순위 확인
ps -eo pid,cls,rtprio,comm | grep irq/
# PID CLS RTPRI COMMAND
# 123 FF     50  irq/18-eth0
# 124 FF     50  irq/19-ahci

# 특정 IRQ 스레드 우선순위 변경
sudo chrt -f -p 40 123

SCHED_DEADLINE과 레이턴시: SCHED_DEADLINE 태스크는 SCHED_FIFO보다 높은 우선순위를 가집니다. 만약 격리 CPU에 SCHED_DEADLINE 태스크가 있으면, SCHED_FIFO RT 태스크의 스레드 지연이 증가할 수 있습니다. timerlat 측정 스레드 자체는 SCHED_FIFO이므로, SCHED_DEADLINE 워크로드가 있는 CPU를 측정할 때는 이 점을 고려해야 합니다.

커널 스레드 우선순위 설계

PREEMPT_RT에서는 대부분의 인터럽트가 스레드화되므로, 커널 스레드 우선순위를 체계적으로 설계해야 합니다. 잘못된 우선순위 배치는 timerlat에서 스레드 지연 증가로 나타납니다.

우선순위 범위용도예시
99migration, watchdogmigration/N, watchdog/N
90-98최고 우선순위 RT 워크로드실시간 제어 루프
80-89고우선순위 RT 워크로드센서 읽기, 통신 스레드
50 (기본)IRQ 스레드irq/18-eth0, irq/19-ahci
1-49저우선순위 RT 스레드데이터 로깅, 모니터링
0 (CFS)일반 프로세스셸, 데몬, 사용자 프로세스
# RT 커널 스레드 우선순위 전체 확인
ps -eo pid,cls,rtprio,psr,comm --sort=-rtprio | head -30

# 특정 패턴의 IRQ 스레드 우선순위 일괄 변경
# 예: 네트워크 IRQ를 우선순위 40으로 낮추기
for pid in $(pgrep -f "irq/.*-eth"); do
    sudo chrt -f -p 40 $pid
    echo "Set irq thread $pid to FIFO:40"
done

# tuna로 시각적으로 확인
sudo tuna --show_threads --irqs

# 변경 후 timerlat으로 검증
sudo rtla timerlat hist -c 2,3 -P 80 -d 60s
RT 스로틀링 함정: sched_rt_runtime_us가 기본값(950000)이면 RT 태스크가 1초 중 950ms만 실행됩니다. 격리 CPU에서 RT 태스크가 단독으로 돌 때는 이 제한에 걸릴 수 있고, 이때 timerlat이 50ms 단위의 지연 스파이크를 보고할 수 있습니다. 이 패턴을 보면 먼저 /proc/sys/kernel/sched_rt_runtime_us를 확인하세요.

PREEMPT_RT와 일반 커널의 timerlat 차이

같은 하드웨어에서 일반 PREEMPT 커널과 PREEMPT_RT 커널의 timerlat 결과가 다르게 나오는 이유를 이해하면 RT 커널의 특성을 정확히 파악할 수 있습니다.

비교 항목PREEMPT (일반)PREEMPT_RT
IRQ 지연 평균낮음 (1-3μs)약간 높을 수 있음 (스레드화 오버헤드)
IRQ 지연 최대높을 수 있음 (긴 critical section)낮음 (spinlock → rt_mutex)
스레드 지연 평균보통보통
스레드 지연 최대높을 수 있음 (선점 불가 구간)낮음 (거의 모든 곳에서 선점 가능)
꼬리 지연길 수 있음짧고 예측 가능
softirq 간섭IRQ 컨텍스트에서 직접 처리전용 스레드로 분리
# 일반 커널 vs RT 커널 비교 테스트

# 1. 현재 커널의 선점 모드 확인
grep PREEMPT /boot/config-$(uname -r)
# CONFIG_PREEMPT_RT=y → RT 커널
# CONFIG_PREEMPT=y    → 일반 선점 커널

# 또는 uname으로 확인
uname -v
# #1 SMP PREEMPT_RT → RT 커널
# #1 SMP PREEMPT    → 일반 선점 커널

# 2. 같은 조건에서 timerlat 실행
sudo rtla timerlat hist -c 2,3 -P 95 -d 300s -b 1 -e 100

# 3. RT 커널에서 기대하는 결과:
# - Thread max가 일반 커널보다 확연히 낮아야 함
# - 히스토그램 꼬리가 짧아야 함
# - IRQ max는 비슷하거나 약간 높을 수 있음 (정상)

# 4. RT 커널인데도 max가 높다면:
# - RT 스로틀링 확인: cat /proc/sys/kernel/sched_rt_runtime_us
# - IRQ affinity 확인: cat /proc/interrupts
# - 격리 설정 확인: cat /proc/cmdline

PREEMPT_RT의 핵심 이점은 최대 지연의 상한을 예측 가능하게 만드는 것입니다. 평균 지연은 일반 커널과 비슷하거나 약간 높을 수 있지만, 최대 지연(꼬리 지연)이 크게 줄어듭니다. timerlat hist에서 이 차이가 가장 뚜렷하게 보입니다. 일반 커널의 히스토그램은 긴 꼬리를 가지는 경향이 있고, RT 커널의 히스토그램은 좁고 깔끔한 분포를 보입니다.

PREEMPT_RT 전환 검증 체크리스트

일반 커널에서 PREEMPT_RT로 전환한 후 다음 사항을 체계적으로 검증합니다.

# === PREEMPT_RT 전환 검증 체크리스트 ===

# 1. RT 커널 확인
uname -v | grep -q PREEMPT_RT && echo "RT kernel" || echo "NOT RT kernel"

# 2. IRQ 스레드화 확인
echo "=== IRQ threads ==="
ps -eo pid,cls,rtprio,comm | grep "irq/" | head -10

# 3. softirq 스레드 확인
echo "=== ksoftirqd ==="
ps -eo pid,cls,rtprio,psr,comm | grep ksoftirqd

# 4. RT 스로틀링 확인
echo "=== RT throttling ==="
cat /proc/sys/kernel/sched_rt_runtime_us
cat /proc/sys/kernel/sched_rt_period_us

# 5. 하드웨어 지연 기준 (hwlat)
echo "=== Hardware latency ==="
sudo rtla hwnoise top -c 2,3 -d 30s 2>&1 | tail -3

# 6. 격리 CPU 청결도 (osnoise)
echo "=== CPU isolation ==="
sudo rtla osnoise top -c 2,3 -d 30s 2>&1 | tail -3

# 7. 지연 분포 (timerlat)
echo "=== Latency distribution ==="
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50 2>&1 | tail -10

# 8. cyclictest 회귀 비교
echo "=== cyclictest ==="
sudo cyclictest -m -S -p 95 -i 1000 -D 60s -q -a 2,3

# 9. 부하 상태 테스트
echo "=== Under load ==="
taskset -c 0,1 stress-ng --cpu 4 --io 2 --vm 1 --vm-bytes 512M --timeout 60s &
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50
wait

이 체크리스트의 모든 항목이 양호하면 PREEMPT_RT 전환이 성공적입니다. 특히 timerlat hist의 max 값이 일반 커널 대비 크게 감소했는지, 히스토그램 꼬리가 짧아졌는지를 확인합니다. 만약 개선이 미미하면 격리 설정(isolcpus, nohz_full, rcu_nocbs)이 제대로 적용되었는지 재점검합니다.

검증 순서: PREEMPT_RT 환경 튜닝 후 다음 순서로 검증합니다.
  1. rtla osnoise top -c 2,3 -d 30s → 격리 CPU 청결도 확인
  2. rtla timerlat hist -c 2,3 -d 60s → 지연 분포 확인
  3. cyclictest -m -S -p 95 -i 1000 -d 60 -c 2-3 -h 100 -q → 회귀 비교 기준점
  4. 필요시 rtla timerlat trace -T 50 -c 2,3 → 원인 추적

trace-cmd 연동

rtla는 tracefs를 직접 제어하지만, 보다 세밀한 필터링이나 후처리가 필요하면 trace-cmd와 함께 사용할 수 있습니다. trace-cmd는 tracefs의 범용 프론트엔드이고, rtla는 레이턴시 분석에 특화된 프론트엔드입니다.

항목rtlatrace-cmd
주요 목적레이턴시/간섭 전용 분석범용 tracing 프론트엔드
tracer 설정자동 (timerlat/osnoise)수동 지정
출력 형식요약표, 히스토그램raw trace, .dat 파일
시각화텍스트 기반KernelShark 연동
후처리제한적 (auto_analysis)스크립트, Python, SQL
커널 요구timerlat/osnoise tracerftrace만 있으면 동작
# trace-cmd로 timerlat tracer 직접 사용
sudo trace-cmd record -p timerlat -O timerlat_us:1000 -C 2,3 sleep 10

# 기록된 trace 확인
trace-cmd report | head -50

# 특정 이벤트만 필터링
trace-cmd report -F 'timerlat_irq: timer_latency > 10'

# KernelShark로 시각화
kernelshark trace.dat

# ftrace event와 함께 기록 (IRQ 추적 강화)
sudo trace-cmd record -p timerlat \
  -e irq_handler_entry -e irq_handler_exit \
  -e softirq_entry -e softirq_exit \
  -e sched_switch -e sched_wakeup \
  -C 2,3 sleep 10

# Python으로 trace.dat 후처리
trace-cmd report --cpu 2 | python3 analyze_latency.py

trace-cmd의 가장 큰 장점은 KernelShark GUI와의 연동입니다. trace.dat 파일을 KernelShark에서 열면 시간축 위에 CPU별 이벤트를 시각적으로 볼 수 있어, 텍스트 trace로는 파악하기 어려운 시간 관계를 한눈에 파악할 수 있습니다.

#!/usr/bin/env python3
# analyze_latency.py — trace-cmd report 출력을 파싱하여 통계 계산
import sys
import re

irq_latencies = []
thread_latencies = []

for line in sys.stdin:
    m = re.search(r'timerlat_irq:.*timer_latency\s+(\d+)\s+us', line)
    if m:
        irq_latencies.append(int(m.group(1)))
        continue
    m = re.search(r'timerlat_main:.*timer_latency\s+(\d+)\s+us', line)
    if m:
        thread_latencies.append(int(m.group(1)))

def percentile(data, p):
    data.sort()
    idx = int(len(data) * p / 100)
    return data[min(idx, len(data)-1)]

for name, lat in [("IRQ", irq_latencies), ("Thread", thread_latencies)]:
    if not lat:
        continue
    print(f"{name}: count={len(lat)} min={min(lat)} avg={sum(lat)//len(lat)} "
          f"p99={percentile(lat,99)} p999={percentile(lat,99.9)} max={max(lat)}")

KernelShark 활용 팁: KernelShark에서 timerlat 관련 이벤트를 분석할 때, 다음 설정이 유용합니다.

# trace-cmd로 timerlat + sched + irq 이벤트를 함께 기록하는 완전한 예제
sudo trace-cmd record \
  -p timerlat \
  -e timerlat:timerlat_sample \
  -e osnoise:irq_noise \
  -e osnoise:softirq_noise \
  -e osnoise:thread_noise \
  -e irq:irq_handler_entry \
  -e irq:irq_handler_exit \
  -e irq:softirq_entry \
  -e irq:softirq_exit \
  -e sched:sched_switch \
  -e sched:sched_wakeup \
  -e sched:sched_wakeup_new \
  -C 2,3 \
  sleep 30

# 결과 분석: 지연이 큰 샘플 찾기
trace-cmd report -F 'timerlat_sample: timer_latency > 20'

# 특정 CPU의 이벤트만 추출
trace-cmd report --cpu 2 > cpu2_trace.txt

# KernelShark로 시각적 분석
kernelshark trace.dat &
rtla vs trace-cmd 선택 기준: 빠르게 "이 CPU의 지연이 어떤 수준인가"를 보려면 rtla가 효율적이고, "지연이 발생한 순간에 정확히 무슨 일이 있었는지"를 복원하려면 trace-cmd로 이벤트를 풍부하게 기록하는 것이 좋습니다. 자세한 ftrace 활용법은 ftrace / Tracepoints 문서를 참고하세요. PMU 기반 하드웨어 카운터 분석은 perf 서브시스템, 하드웨어 수준 계측은 Intel PCM 문서를 참고하세요.

hwlat_detector 비교

레이턴시 문제의 원인이 소프트웨어가 아니라 하드웨어/펌웨어에 있을 수 있습니다. SMI(System Management Interrupt)는 CPU를 완전히 멈추고 SMM(System Management Mode)으로 전환하며, 이 시간 동안 운영체제는 어떤 제어도 할 수 없습니다. hwlat_detector는 이런 하드웨어 수준의 지연을 검출하는 전용 tracer입니다.

레이턴시 원인 어디에? 하드웨어/펌웨어 hwlat_detector 커널 (IRQ/스레드) timerlat CPU 간섭 (잡음) osnoise • SMI / NMI 검출 • BIOS 열 관리 • 메모리 ECC 스크러빙 • PCIe ASPM 전환 • 하드웨어 워치독 • IRQ 지연 분리 • 스레드 스케줄링 지연 • auto_analysis • threshold 기반 추적 • 히스토그램 분포 • 간섭 유형 분류 • 격리 CPU 청결도 • 누적 시간 집계 • NMI/IRQ/softirq/thread • runtime vs noise 비율 권장 순서: hwlat → osnoise → timerlat → ftrace 하드웨어 잡음 확인 → CPU 격리 검증 → 지연 원인 분리 → 함수 수준 추적
# hwlat_detector 사용법
# tracefs를 통해 직접 설정하거나 rtla로 실행

# 방법 1: rtla (6.2+)
sudo rtla hwnoise top

# 방법 2: tracefs 직접 사용
echo hwlat > /sys/kernel/tracing/current_tracer
echo 1000000 > /sys/kernel/tracing/tracing_thresh  # 1ms threshold (ns)
cat /sys/kernel/tracing/trace

# SMI 카운터 확인 (Intel CPU)
sudo rdmsr -a 0x34  # MSR_SMI_COUNT

# BIOS에서 SMI 관련 설정 확인
# - USB Legacy Support: Disabled
# - Serial Port Console Redirection: Disabled
# - Thermal Management SMI: 주의 (비활성화 시 과열 위험)
검출 대상hwlattimerlatosnoise
SMI (System Management Interrupt)직접 검출간접 (IRQ 지연에 포함)간접 (noise에 포함)
NMI검출 가능간접직접 분류
하드 IRQ 지연검출 불가직접 분리직접 분류
softirq 간섭검출 불가간접 (스레드 지연)직접 분류
스레드 스케줄링 지연검출 불가직접 분리간접
BIOS 열 관리(Thermal Management)직접 검출간접간접
메모리 ECC 스크러빙직접 검출간접간접
hwlat 사용 주의: hwlat_detector는 CPU를 완전히 점유하면서 측정하므로, 운영 중인 시스템에서는 격리 CPU에서만 실행해야 합니다. 또한 SMI 비활성화는 시스템 안정성에 영향을 줄 수 있으므로(과열 보호, 하드웨어 워치독 등), BIOS 설정 변경 전에 반드시 하드웨어 벤더와 확인하세요.

SMI 검출과 대응

SMI(System Management Interrupt)는 운영체제가 제어할 수 없는 가장 높은 우선순위의 인터럽트입니다. CPU는 SMI를 받으면 현재 실행을 완전히 멈추고 SMM(System Management Mode)으로 전환합니다. 이 동안 OS는 어떤 것도 할 수 없으며, 복귀 후에도 OS는 이 사실을 직접 알 수 없습니다.

# SMI 카운터 확인 (Intel CPU, msr-tools 필요)
sudo modprobe msr
# CPU 0의 SMI 카운터
sudo rdmsr -p 0 0x34
# 모든 CPU의 SMI 카운터
sudo rdmsr -a 0x34

# 일정 시간 후 다시 읽어서 증가량 확인
echo "Before:" && sudo rdmsr -a 0x34
sleep 60
echo "After:" && sudo rdmsr -a 0x34

# SMI가 증가하고 있다면 BIOS에서 확인:
# - USB Legacy Emulation → Disabled
# - Serial Port Console Redirection → Disabled
# - Memory Patrol Scrubbing → Disabled (또는 주기 연장)
# - Power Management SMI → 주의 필요 (과열 보호와 관련)

# hwlat로 SMI 지속 시간 확인
sudo rtla hwnoise top -c 2,3 -d 60s
# max_single이 수십~수백 μs면 SMI 가능성 높음

SMI의 일반적인 지속 시간은 10μs~수백 μs 범위입니다. 극저지연 환경(HFT, 산업 PLC)에서는 이 시간이 허용 범위를 초과할 수 있으므로, BIOS 레벨에서 불필요한 SMI 소스를 비활성화해야 합니다. 다만 열 관리 SMI는 비활성화하면 과열 위험이 있으므로 매우 신중하게 접근해야 합니다.

SMI 소스일반적 지속 시간비활성화 가능?BIOS 설정 이름
USB Legacy Emulation10~50μs대부분 가능USB Legacy Support
Serial Port Redirection10~100μs가능Console Redirection
메모리 ECC 스크러빙50~500μs주기 연장 가능Patrol Scrub
온도 관리10~100μs주의 (과열 위험)Thermal Management
하드웨어 워치독10~30μs주의 (안정성)HW Watchdog
전력 관리변동일부 가능Power Management
PCIe ASPM과 레이턴시: PCIe 절전 기능(ASPM)도 하드웨어 수준 지연의 원인이 됩니다. L1 상태에서 복귀하는 데 수 μs~수십 μs가 소요됩니다. pcie_aspm=off 부트 파라미터로 비활성화하거나, lspci -vv로 개별 디바이스의 ASPM 상태를 확인할 수 있습니다. 자세한 하드웨어 계측 방법은 Intel PCM 문서를 참고하세요.

CPU 격리 전략 종합

레이턴시 튜닝의 핵심은 실시간 워크로드가 동작하는 CPU를 최대한 조용하게 만드는 것입니다. 리눅스에서 CPU를 격리하는 도구와 방법이 여러 가지 있으므로, 각각의 역할과 겹치는 부분을 정리합니다.

CPU 격리 계층과 도구 커널 부트 파라미터 (가장 강력) isolcpus=domain,managed_irq,2-3 nohz_full=2-3 rcu_nocbs=2-3 irqaffinity=0-1 → 스케줄러 도메인 제외, tick 제거, RCU 위임, IRQ 배치. 재부팅 필요. cgroup cpuset (런타임 유연) systemd CPUAffinity=, cgroup v2 cpuset.cpus, cpuset.cpus.partition=isolated → 프로세스 그룹 단위 CPU 제한. 재부팅 불필요. systemd 서비스 파일에서 설정 가능. tuna IRQ·스레드 affinity GUI/CLI → Red Hat RT 도구. 직관적 UI. cset (cpuset) CPU 셋 기반 프로세스 격리 → 셸드/비셸드 파티션 개념. 격리 검증 체크리스트 1. cat /sys/devices/system/cpu/isolated → 격리 CPU 목록 2. cat /proc/interrupts | awk '{print $1,$NF}' → IRQ 분포 3. ps -eo psr,comm | sort -n → CPU별 프로세스 4. rtla osnoise top -c 2,3 -d 10s → 잡음 수준
도구/방법적용 시점격리 수준유연성주요 장점
isolcpus부트 타임매우 강력재부팅 필요스케줄러 도메인 완전 제외
nohz_full부트 타임tick 제거재부팅 필요타이머 인터럽트 제거
rcu_nocbs부트 타임RCU 위임재부팅 필요RCU softirq 제거
cgroup cpuset런타임프로세스 단위동적 변경 가능systemd 통합, 세밀한 제어
tuna런타임IRQ/스레드동적 변경 가능직관적 GUI, IRQ 재배치(Relocation)
cset런타임프로세스 셋동적 변경 가능shield 개념으로 간단한 격리
taskset런타임단일 프로세스즉시가장 단순, 스크립트 친화적
# === 격리 검증 종합 스크립트 ===

# 1. 격리 CPU 확인
echo "=== Isolated CPUs ==="
cat /sys/devices/system/cpu/isolated

# 2. nohz_full 확인
echo "=== nohz_full CPUs ==="
cat /sys/devices/system/cpu/nohz_full 2>/dev/null || echo "not set"

# 3. rcu_nocbs 확인
echo "=== RCU nocbs ==="
grep rcu_nocbs /proc/cmdline

# 4. 격리 CPU의 IRQ 확인 (있으면 문제)
echo "=== IRQ on isolated CPUs ==="
# CPU 2,3에 IRQ가 배치되어 있는지 확인
cat /proc/interrupts | awk 'NR==1 || ($3+0 > 0) || ($4+0 > 0)'

# 5. 격리 CPU에서 실행 중인 프로세스 (최소한이어야 함)
echo "=== Processes on CPU 2-3 ==="
ps -eo psr,pid,cls,rtprio,comm | awk '$1==2 || $1==3'

# 6. systemd 서비스에서 CPUAffinity 설정 예시
# /etc/systemd/system/my-rt-app.service
# [Service]
# CPUAffinity=2 3
# Nice=-20
# LimitRTPRIO=99
# LimitMEMLOCK=infinity

# 7. cgroup v2 cpuset 설정
echo "2-3" > /sys/fs/cgroup/rt-workload/cpuset.cpus
echo "0" > /sys/fs/cgroup/rt-workload/cpuset.mems
echo $$ > /sys/fs/cgroup/rt-workload/cgroup.procs
실전 권장 조합: 부트 파라미터로 isolcpus+nohz_full+rcu_nocbs+irqaffinity를 설정하여 기본 격리를 확보하고, 런타임에 tuna 또는 cgroup cpuset으로 세부 조정하는 것이 가장 안정적입니다. rtla osnoise로 격리 CPU의 잡음 수준을 측정하면 격리가 실제로 동작하는지 확인할 수 있습니다.

격리 실패 진단 패턴

격리 설정을 했는데도 osnoise 결과가 좋지 않을 때, 흔한 원인과 진단 방법을 정리합니다.

증상진단 방법원인해결
IRQ 간섭이 높음cat /proc/interruptsmanaged IRQ가 격리 CPU에 남아 있음isolcpusmanaged_irq 추가
SOFTIRQ 간섭이 높음mpstat -P 2,3 1네트워크/블록 softirq가 격리 CPU에서 실행IRQ affinity 먼저 이동
THREAD 간섭이 높음ps -eo psr,pid,commkworker, migration 등이 격리 CPU에서 실행WQ_UNBOUND affinity, cgroup
NMI 간섭이 높음perf stat -e nmi:nmi_handlerperfmon NMI, watchdog NMInmi_watchdog=0, perf 중지
RCU 콜백 발생grep rcu /proc/softirqsrcu_nocbs 미설정 또는 부분 설정부트 파라미터 재확인
# 격리 실패 종합 진단 스크립트
echo "=== 1. 부트 파라미터 확인 ==="
cat /proc/cmdline | tr ' ' '\n' | grep -E 'isolcpus|nohz_full|rcu_nocbs|irqaffinity|threadirqs'

echo "=== 2. 격리 CPU에 남은 IRQ ==="
# CPU 2의 IRQ 카운트 확인 (0보다 크면 문제)
awk -F: '{if(NR>1){split($1,a," "); if(a[4]+0 > 0) print $0}}' /proc/interrupts

echo "=== 3. 격리 CPU에서 실행 중인 스레드 ==="
ps -eo psr,pid,cls,rtprio,ni,comm --sort=psr | awk '$1==2 || $1==3'

echo "=== 4. 워크큐 affinity 확인 ==="
cat /sys/devices/virtual/workqueue/cpumask 2>/dev/null

echo "=== 5. NMI watchdog 상태 ==="
cat /proc/sys/kernel/nmi_watchdog

echo "=== 6. RT 스로틀링 상태 ==="
cat /proc/sys/kernel/sched_rt_runtime_us

커널 6.x 변경사항

RTLA 도구와 timerlat/osnoise tracer는 커널 6.x에서 활발하게 개선되고 있습니다. 주요 변경사항을 버전별로 정리합니다.

커널 버전변경 내용영향
6.1RTLA 커널 트리 통합, rtla 유틸리티 공식 포함별도 설치 없이 커널 빌드 시 함께 생성 가능
6.2rtla hwnoise 명령 추가hwlat_detector를 rtla 프론트엔드에서 직접 사용
6.3timerlat auto_analysis 기본 활성화threshold 초과 시 자동으로 원인 분석 요약 출력
6.3timerlat user-space tracing 지원커널 스레드 대신 사용자 공간 스레드에서 측정 가능
6.4osnoise pid filter 추가특정 PID의 간섭만 추적 가능
6.5aa_only 옵션 (auto_analysis only)raw trace 없이 분석 요약만 출력 (빠른 확인용)
6.5timerlat hist 개선: 사용자 공간 지연 열 추가커널/사용자 지연 분리 히스토그램
6.6osnoise softirq 세분류NET_RX, BLOCK, TIMER 등 개별 softirq 식별
6.7hrtimer 정밀도 개선 (TSC 기반)측정 정확도 향상, 특히 짧은 지연에서
6.8+timerlat cgroup 인식, osnoise per-cpu threshold컨테이너(Container) 환경 지원, CPU별 다른 threshold 설정
# auto_analysis only 모드 (6.5+) — 빠른 원인 확인
sudo rtla timerlat trace -T 50 -c 2 --aa-only

# user-space tracing (6.3+)
# 커널이 /dev/timerlat 디바이스를 제공, 사용자 공간 앱이 읽기
sudo rtla timerlat top --user-threads

# osnoise pid filter (6.4+)
sudo rtla osnoise trace --filter-pid 1234

# per-CPU threshold (6.8+)
sudo rtla osnoise trace -c 2 -T 10 -c 3 -T 20

# RTLA 버전 확인
rtla --version
# rtla 6.x.y (커널 버전과 동일)
user-space tracing의 의미: 커널 6.3 이전에는 timerlat이 커널 스레드로만 측정했으므로, 사용자 공간 스케줄링 지연은 별도 도구(cyclictest)로 측정해야 했습니다. user-space tracing이 추가되면서 커널 스레드 지연과 사용자 공간 지연을 같은 도구에서 함께 볼 수 있게 되었습니다. 이 기능은 특히 사용자 공간 RT 애플리케이션의 종단 지연을 분석할 때 유용합니다.
버전별 호환성: 오래된 커널(5.x)에서는 rtla 명령 자체가 없을 수 있습니다. 이 경우 tracefs를 직접 사용하거나(echo timerlat > /sys/kernel/tracing/current_tracer), 별도로 rtla를 빌드해야 합니다. auto_analysis 등 최신 기능은 커널 tracer 자체가 지원해야 하므로, 도구만 업데이트해서는 사용할 수 없습니다.

커널 5.x에서의 대안

커널 5.x에서는 rtla가 없지만 timerlat/osnoise tracer 자체는 5.14부터 사용 가능합니다. tracefs를 직접 제어하거나 trace-cmd로 접근해야 합니다. 5.14 이전에는 hwlat_detector만 사용 가능하며, 레이턴시 분석은 cyclictest + ftrace 조합에 의존합니다.

커널 버전사용 가능한 기능권장 접근법
5.0~5.13hwlat_detector, ftracecyclictest + ftrace + hwlat
5.14~5.19timerlat, osnoise tracertracefs 직접 사용 또는 trace-cmd
6.0timerlat, osnoise, hwlattracefs 또는 rtla 수동 빌드
6.1+rtla 명령 포함rtla 사용 권장
6.3+auto_analysis, user-spacertla 최신 기능 활용
# 커널 5.14~5.19에서 timerlat 사용 (tracefs 직접)
echo timerlat > /sys/kernel/tracing/current_tracer
echo 2-3 > /sys/kernel/tracing/osnoise/cpus
cat /sys/kernel/tracing/trace_pipe | head -100

# 커널 버전 확인
uname -r
# 6.1.141 → rtla 사용 가능

# available_tracers에서 tracer 존재 확인
grep -o 'timerlat\|osnoise\|hwlat' /sys/kernel/tracing/available_tracers

커널 CONFIG 옵션 상세

timerlat/osnoise tracer를 사용하려면 다음 커널 설정이 활성화되어야 합니다.

# 필수 CONFIG 옵션
CONFIG_TRACING=y               # 기본 tracing 프레임워크
CONFIG_OSNOISE_TRACER=y        # osnoise tracer
CONFIG_TIMERLAT_TRACER=y       # timerlat tracer (OSNOISE_TRACER에 의존)
CONFIG_HWLAT_TRACER=y          # hwlat tracer (선택)

# 권장 CONFIG 옵션 (심층 분석용)
CONFIG_FTRACE=y                # function tracer
CONFIG_FUNCTION_TRACER=y       # 함수 추적
CONFIG_FUNCTION_GRAPH_TRACER=y # 함수 그래프 추적
CONFIG_TRACEPOINTS=y           # tracepoint 인프라
CONFIG_HIST_TRIGGERS=y         # 히스토그램 트리거
CONFIG_SCHED_TRACER=y          # 스케줄러 추적
CONFIG_IRQSOFF_TRACER=y        # IRQ 비활성 구간 추적
CONFIG_PREEMPTIRQ_DELAY_TEST=y # 선점/IRQ 지연 테스트 (디버그용)

# RT 커널 추가 옵션
CONFIG_PREEMPT_RT=y            # PREEMPT_RT 커널
CONFIG_HIGH_RES_TIMERS=y       # 고해상도 타이머 (hrtimer)

# 현재 커널의 CONFIG 확인 방법
zcat /proc/config.gz 2>/dev/null | grep -E 'OSNOISE|TIMERLAT|HWLAT|PREEMPT'
# 또는
grep -E 'OSNOISE|TIMERLAT|HWLAT|PREEMPT' /boot/config-$(uname -r)
# 또는
cat /sys/kernel/tracing/available_tracers
배포판별 상황: 대부분의 배포판 커널은 CONFIG_OSNOISE_TRACERCONFIG_TIMERLAT_TRACER를 기본으로 활성화합니다. 그러나 일부 최소 설치 커널이나 임베디드 커널에서는 비활성화되어 있을 수 있습니다. Red Hat/Fedora의 RT 커널은 모든 관련 CONFIG가 활성화되어 있습니다. 커널 심볼 정보(CONFIG_KALLSYMS)가 있으면 trace 출력에 함수 이름이 표시되어 분석이 쉬워집니다. 자세한 내용은 커널 심볼 & 디버그 정보 문서를 참고하세요.

실전 벤치마크 시나리오

레이턴시 요구사항은 도메인마다 크게 다릅니다. 여기서는 대표적인 네 가지 실전 시나리오별로 측정 방법과 기준값을 정리합니다.

도메인별 레이턴시 요구사항과 측정 전략 네트워크 (DPDK/XDP) 목표: <10μs p99 핵심: NIC IRQ 격리 poll 모드 vs IRQ 모드 NAPI busy_poll 설정 측정: timerlat + perf 참고: GSO/GRO 산업 PLC (EtherCAT) 목표: <100μs 절대 상한 핵심: PREEMPT_RT 필수 1ms 주기 제어 루프 RT 스로틀링 비활성화 측정: timerlat + cyclictest 참고: RT Mutex HFT (고빈도 트레이딩) 목표: <5μs p999 핵심: 전 경로 지연 최소화 kernel bypass (DPDK) C-state=0, governor=perf 측정: hwlat + osnoise 참고: Intel PCM 오디오 (JACK) 목표: <1ms 버퍼 핵심: xrun 방지 IRQ 스레드 우선순위 PipeWire RT 정책 측정: timerlat + hist 참고: 스케줄러 시나리오별 측정 명령어 네트워크: rtla timerlat hist -c 2,3 -P 80 -d 300s -b 1 -e 50 산업 PLC: rtla timerlat trace -c 2 -P 95 -T 100 -d 3600s HFT: rtla osnoise top -c 2-7 -d 600s && rtla hwnoise top -c 2-7 -d 60s 오디오: rtla timerlat hist -c 0-3 -p 500 -d 120s -b 1 -e 100 공통 검증: cyclictest -m -S -p 95 -i 1000 -h 100 -q -D 300s 하드웨어: rtla hwnoise top -d 60s (SMI/NMI 검출)
# === 네트워크 레이턴시 벤치마크 (DPDK/XDP 환경) ===
# NIC IRQ를 housekeeping CPU로 이동
echo 3 > /proc/irq/18/smp_affinity  # CPU 0,1에만 배치

# 격리 CPU에서 timerlat 측정
sudo rtla timerlat hist -c 2,3 -P 80 -d 300s -b 1 -e 50

# === 산업 PLC 벤치마크 (EtherCAT 1ms 주기) ===
# RT 스로틀링 비활성화
echo -1 | sudo tee /proc/sys/kernel/sched_rt_runtime_us

# 1시간 연속 측정, 100μs 초과 시 trace 확보
sudo rtla timerlat trace -c 2 -P 95 -T 100 -d 3600s

# === HFT 벤치마크 (극저지연) ===
# 먼저 하드웨어 잡음 확인
sudo rtla hwnoise top -d 60s

# CPU 격리 상태에서 osnoise
sudo rtla osnoise top -c 2-7 -d 600s

# Intel PCM으로 주파수/전력 교차 확인
# (Intel PCM 문서 참고)
sudo pcm -r -- sleep 60

# === 오디오 실시간 벤치마크 (JACK/PipeWire) ===
# 오디오 IRQ 확인
cat /proc/interrupts | grep snd

# 타이머 주기를 오디오 버퍼에 맞춤 (500μs ≈ 48kHz/24 샘플)
sudo rtla timerlat hist -c 0-3 -p 500 -d 120s -b 1 -e 100
시나리오목표 지연주요 측정 도구핵심 튜닝 포인트관련 문서
네트워크 (DPDK/XDP)<10μs p99timerlat, perfNIC IRQ 격리, NAPI, busy_pollGSO/GRO, 네트워킹
산업 PLC (EtherCAT)<100μs 절대timerlat, cyclictestPREEMPT_RT, RT 스로틀링 해제RT Mutex, 스케줄러
HFT (고빈도 트레이딩)<5μs p999hwlat, osnoisekernel bypass, C-state=0Intel PCM, 최적화
오디오 (JACK/PipeWire)<1ms 버퍼timerlat histIRQ 우선순위, RT 정책스케줄러
기준값은 출발점: 위 표의 목표 지연은 일반적인 가이드라인입니다. 실제 요구사항은 워크로드, 하드웨어, SLA에 따라 다릅니다. 중요한 것은 "현재 시스템이 이 기준을 만족하는가"를 rtla로 객관적으로 확인하고, 만족하지 않으면 어떤 계층(하드웨어/커널/스케줄링)에서 병목(Bottleneck)인지를 분리하는 것입니다.

CI/CD 파이프라인 통합

레이턴시 벤치마크를 CI/CD에 통합하면 커널 업데이트, 설정 변경, 드라이버 업데이트 후의 회귀를 자동으로 감지할 수 있습니다.

#!/bin/bash
# ci-latency-check.sh — CI/CD 레이턴시 회귀 검사 스크립트

THRESHOLD_IRQ=10    # IRQ 최대 지연 기준 (μs)
THRESHOLD_THR=30    # Thread 최대 지연 기준 (μs)
DURATION=300         # 측정 시간 (초)
CPUS="2,3"           # 측정 대상 CPU

echo "[CI] Starting latency benchmark..."

# 1. osnoise로 격리 상태 확인
echo "[CI] Checking CPU isolation..."
sudo rtla osnoise top -c $CPUS -d 10s 2>&1 | tee /tmp/osnoise.txt

# 2. timerlat 히스토그램 수집
echo "[CI] Collecting timerlat histogram..."
sudo rtla timerlat hist -c $CPUS -P 95 -d ${DURATION}s \
  -b 1 -e 100 2>&1 | tee /tmp/timerlat-hist.txt

# 3. cyclictest 회귀 비교
echo "[CI] Running cyclictest..."
sudo cyclictest -m -S -p 95 -i 1000 -D ${DURATION}s \
  -h 100 -q -a $CPUS 2>&1 | tee /tmp/cyclictest.txt

# 4. 결과 판정
MAX_LAT=$(grep "Max:" /tmp/cyclictest.txt | awk '{print $NF}' | sort -n | tail -1)
echo "[CI] Maximum latency: ${MAX_LAT}μs"

if [ "$MAX_LAT" -gt "$THRESHOLD_THR" ]; then
    echo "[CI] FAIL: Maximum latency ${MAX_LAT}μs exceeds threshold ${THRESHOLD_THR}μs"
    # 원인 추적 trace 수집
    sudo rtla timerlat trace -T $THRESHOLD_THR -c $CPUS -d 60s \
      -o /tmp/rtla-trace.txt
    exit 1
else
    echo "[CI] PASS: Maximum latency ${MAX_LAT}μs within threshold"
    exit 0
fi
회귀 감지 기준: 단순 최대값 비교만으로는 단발성 이벤트에 민감하게 반응합니다. 보다 안정적인 기준은 p99 또는 p999의 변화율을 모니터링하는 것입니다. 히스토그램에서 p99가 이전 빌드 대비 2배 이상 증가하면 의미 있는 회귀로 판단합니다.

멀티 시나리오 통합 테스트

실전에서는 단일 측정이 아니라 다양한 부하 조건에서 측정해야 합니다. 유휴 상태(Idle State)에서는 양호하지만 I/O 부하, 네트워크 부하, 메모리 압박 상황에서 지연이 급증하는 패턴이 흔합니다.

# === 부하 조건별 레이턴시 테스트 ===

# 1. 유휴 상태 기준 측정
echo "=== Idle baseline ==="
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50 > /tmp/idle.txt

# 2. CPU 부하 상태 (stress-ng를 housekeeping CPU에서 실행)
echo "=== CPU stress ==="
taskset -c 0,1 stress-ng --cpu 4 --timeout 60s &
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50 > /tmp/cpu-stress.txt
wait

# 3. I/O 부하 상태
echo "=== I/O stress ==="
taskset -c 0,1 stress-ng --io 4 --hdd 2 --timeout 60s &
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50 > /tmp/io-stress.txt
wait

# 4. 네트워크 부하 상태
echo "=== Network stress ==="
taskset -c 0,1 iperf3 -c 192.168.1.1 -t 60 &
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50 > /tmp/net-stress.txt
wait

# 5. 메모리 압박 상태
echo "=== Memory pressure ==="
taskset -c 0,1 stress-ng --vm 2 --vm-bytes 1G --timeout 60s &
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50 > /tmp/mem-stress.txt
wait

# 6. 결과 비교
for f in idle cpu-stress io-stress net-stress mem-stress; do
    echo "=== $f ==="
    grep -E "^(min|avg|max)" /tmp/$f.txt
done

부하 조건별 비교에서 주목할 패턴은 다음과 같습니다.

부하 조건격리 양호 시 기대격리 불량 시 증상
CPU stress격리 CPU 영향 없음스레드 지연 증가 → isolcpus 확인
I/O stress격리 CPU 영향 없음IRQ 지연 증가 → 스토리지 IRQ affinity 확인
네트워크 stress격리 CPU 영향 미미IRQ + softirq 증가 → NIC IRQ 이동
메모리 압박격리 CPU 영향 미미스레드 지연 증가 → compaction, reclaim 확인
stress 테스트 주의: stress-ng를 반드시 housekeeping CPU에서만 실행하세요(taskset -c 0,1). 격리 CPU에서 실행하면 의도하지 않은 간섭이 발생하여 측정이 무의미해집니다. 또한 실제 워크로드와 유사한 부하를 사용하는 것이 가장 정확합니다.

도구 선택 의사결정

레이턴시 문제를 만났을 때 어떤 도구부터 써야 하는지를 증상 기반으로 정리합니다. 도구를 잘못 선택하면 원인과 무관한 데이터에 시간을 낭비하게 됩니다.

레이턴시 문제 발생 하드웨어/펌웨어 잡음 의심? hwlat 아니오 CPU 간섭이 전반적으로 높은가? osnoise 아니오 특정 순간 지연이 크게 튀는가? timerlat 아니오 함수 수준 호출 경로가 필요한가? ftrace 아니오 회귀 비교/CI 기준점이 필요한가? cyclictest 일반 분석 perf / top / vmstat 도구별 핵심 역할 hwlat SMI/NMI, 펌웨어 간섭 osnoise CPU 격리 청결도 timerlat IRQ/스레드 지연 분리 ftrace 함수 호출 경로 추적 cyclictest 회귀 비교 기준점 perf PMU, 프로파일링 스케줄링 이벤트 분석
증상1차 도구보조 도구핵심 확인 사항
모든 CPU에서 간헐적으로 수십 μs 지연hwlatrdmsr 0x34 (SMI)SMI 횟수 증가 여부, BIOS 설정
격리 CPU인데도 잡음이 지속osnoisecat /proc/interrupts격리 CPU에 남은 IRQ, kthread
특정 순간 최대 지연이 크게 튐timerlat traceftraceauto_analysis 결과, IRQ vs thread
지연 분포가 이상함 (바이모달)timerlat histcpupowerC-state, CPUfreq governor
커널 업데이트 후 지연 회귀cyclictesttimerlat histbefore/after 비교, 히스토그램 변화
특정 함수에서 오래 걸리는 것 같음ftraceperffunction_graph, 호출 스택
CPU 주파수/전력 관련 의심perf / Intel PCMosnoise주파수 전환 빈도, C-state 잔류 시간
도구 조합 패턴: 실무에서 가장 효과적인 패턴은 다음과 같습니다.
  1. hwlat으로 하드웨어 잡음 배제 (1회, 60초)
  2. osnoise로 CPU 격리 상태 확인 (격리 변경 시마다)
  3. timerlat hist로 지연 분포 확인 (기준 설정)
  4. timerlat trace로 이상 지연 원인 추적 (문제 발생 시)
  5. cyclictest로 변경 전/후 회귀 비교 (정기적)
이 순서를 따르면 원인 범위를 체계적으로 좁힐 수 있습니다. 자세한 tracing 기법은 ftrace / Tracepoints 문서를, PMU 기반 분석은 perf 서브시스템 문서를 참고하세요.

레이턴시 버짓 설계

실시간 시스템에서는 "허용 가능한 최대 지연"을 레이턴시 버짓(latency budget)으로 분해하여 각 계층에 할당합니다. rtla의 측정 결과는 이 버짓의 각 항목을 검증하는 데 직접 사용됩니다.

계층지연 범위측정 도구예시 (산업 PLC)
하드웨어 (SMI/NMI)0~100μshwlat목표: <5μs
IRQ 지연1~50μstimerlat (IRQ 열)목표: <10μs
스레드 스케줄링1~100μstimerlat (Thread 열)목표: <30μs
사용자 공간 진입1~10μscyclictest목표: <5μs
CPU 간섭0~1000μsosnoise목표: noise <10μs/s
총 지연합산종합목표: <100μs

각 계층의 측정값이 할당된 버짓을 초과하면 해당 계층을 튜닝합니다. 모든 계층이 버짓 내에 있는데도 총 지연이 초과하면 버짓 재분배가 필요합니다. 이 접근법은 "어디를 튜닝해야 가장 효과가 큰가"를 체계적으로 판단하게 해줍니다.

레이턴시 버짓 설계 원칙

버짓 설계 시 주의할 핵심 원칙을 정리합니다.

# 레이턴시 버짓 종합 검증 스크립트
echo "=== 1. 하드웨어 지연 (hwlat) ==="
sudo rtla hwnoise top -c 2,3 -d 30s 2>&1 | tail -5

echo "=== 2. CPU 간섭 (osnoise) ==="
sudo rtla osnoise top -c 2,3 -d 30s 2>&1 | tail -5

echo "=== 3. IRQ/스레드 지연 (timerlat) ==="
sudo rtla timerlat top -c 2,3 -P 95 -d 30s 2>&1 | tail -5

echo "=== 4. 종단 지연 (cyclictest) ==="
sudo cyclictest -m -S -p 95 -i 1000 -D 30s -h 100 -q -a 2,3 2>&1 | tail -5
버짓 초과 시 우선순위: 여러 계층이 동시에 버짓을 초과하면, 하위 계층(하드웨어)부터 해결합니다. 하드웨어 잡음이 있으면 소프트웨어 튜닝이 효과가 없고, IRQ 격리가 안 되면 스케줄링 튜닝이 효과가 없습니다. 항상 hwlat → osnoise → timerlat → cyclictest 순서로 검증합니다.

일반적인 문제와 해결

RTLA 사용 시 자주 겪는 문제와 해결 방법을 정리합니다.

문제원인해결
rtla 명령이 없음커널 6.1 이전 또는 미설치make -C tools/tracing/rtla/로 빌드하거나 tracefs 직접 사용
"tracer not available" 오류커널 CONFIG 미활성화CONFIG_OSNOISE_TRACER=y, CONFIG_TIMERLAT_TRACER=y 확인
"Permission denied"root 권한 필요sudo rtla 또는 CAP_SYS_ADMIN capability
"busy" 오류다른 tracer가 이미 실행 중echo nop > /sys/kernel/tracing/current_tracer로 초기화
측정값이 비정상적으로 큼tracing 오버헤드, 다른 trace 활성화function_tracer 비활성화, 이벤트 최소화
특정 CPU에서만 값이 큼IRQ, kthread 간섭osnoise로 확인 후 IRQ affinity 조정
hist 모드에서 "over" 카운트가 많음히스토그램 범위 초과-e (엔트리 수)를 늘리거나 -b (버킷 크기)를 키움
timerlat 스레드가 보이지 않음CPU가 오프라인이거나 격리 과도CPU 온라인 상태 확인, -c로 명시 지정
# 문제 진단 체크리스트

# 1. rtla 명령 존재 확인
which rtla || echo "rtla not found — build from kernel source"

# 2. 필수 커널 CONFIG 확인
zcat /proc/config.gz 2>/dev/null | grep -E 'OSNOISE|TIMERLAT|HWLAT'
# 또는
grep -E 'OSNOISE|TIMERLAT|HWLAT' /boot/config-$(uname -r)

# 3. tracefs 마운트 확인
mount | grep tracefs || echo "tracefs not mounted"

# 4. 현재 활성 tracer 확인 (nop이어야 사용 가능)
cat /sys/kernel/tracing/current_tracer

# 5. available_tracers 확인
cat /sys/kernel/tracing/available_tracers

# 6. tracer 강제 초기화
echo nop > /sys/kernel/tracing/current_tracer
echo 0 > /sys/kernel/tracing/tracing_on
동시 사용 충돌: rtla, trace-cmd, perf record 등이 동시에 tracefs를 사용하면 충돌합니다. 특히 perf record가 function_tracer를 사용 중이면 rtla가 timerlat tracer를 활성화할 수 없습니다. 측정 전에 다른 tracing 도구가 실행 중인지 확인하세요. perf 서브시스템ftrace 문서에서 각 도구의 tracefs 사용 방식을 참고하세요.

자주 묻는 질문

Q: timerlat과 cyclictest의 수치가 다른 이유는?

A: timerlat은 커널 내부에서 측정하고 cyclictest는 사용자 공간에서 측정합니다. cyclictest의 값은 timerlat의 Thread 지연보다 약간 높은 것이 정상입니다. 사용자 공간 복귀와 시스템 콜 오버헤드가 포함되기 때문입니다.

Q: timerlat을 장시간 실행해도 안전한가?

A: timerlat top은 오버헤드가 최소이므로 장시간 실행 가능합니다. 그러나 timerlat trace는 trace 버퍼를 채우므로 장시간 실행 시 메모리 사용에 주의해야 합니다. threshold(-T)를 설정하면 이벤트 양을 제한할 수 있습니다.

Q: 가상화(Virtualization) 환경(VM)에서 rtla를 사용할 수 있는가?

A: 가능하지만 해석이 달라집니다. VM의 vCPU 스케줄링 지연이 측정값에 포함되므로, 호스트의 CPU 지연과 게스트의 스케줄링 지연을 구분할 수 없습니다. 정확한 레이턴시 분석은 베어메탈에서 수행하는 것이 권장됩니다.

Q: 컨테이너(Docker/Kubernetes)에서 rtla를 사용할 수 있는가?

A: tracefs 접근과 CAP_SYS_ADMIN capability가 필요합니다. 특권 컨테이너(privileged container)에서는 사용 가능하지만, 보안 정책에 따라 제한될 수 있습니다. 커널 6.8+의 cgroup 인식 기능을 사용하면 컨테이너별 측정이 가능합니다.

Q: osnoise의 noise가 0인데도 timerlat 지연이 높은 이유는?

A: osnoise는 "커널이 CPU를 가져간 시간"을 측정하고, timerlat은 "특정 스레드가 제때 실행되었는지"를 측정합니다. osnoise가 깨끗해도 해당 스레드보다 높은 우선순위의 RT 스레드가 CPU를 점유하면 timerlat 지연이 발생합니다. 우선순위 설계를 확인하세요.

Q: NUMA 시스템에서 timerlat 결과가 CPU마다 다른 이유는?

A: NUMA 시스템에서는 원격 노드 메모리 접근이 추가 지연을 발생시킬 수 있습니다. 또한 NUMA 노드 간 인터커넥트(QPI/UPI) 트래픽이 캐시 일관성(Cache Coherency) 프로토콜에 영향을 줄 수 있습니다. 실시간 워크로드는 가능한 같은 NUMA 노드의 CPU와 메모리를 사용하도록 설계해야 합니다. numactl --membindtaskset을 함께 사용하세요.

Q: rtla를 원격 시스템에서 실행하면서 결과를 실시간으로 볼 수 있는가?

A: SSH를 통해 rtla timerlat top을 실행하면 실시간 출력을 볼 수 있습니다. 다만 SSH 세션 자체가 네트워크 IRQ를 발생시키므로, 해당 IRQ가 측정 대상 CPU에 배치되어 있으면 간섭 원인이 됩니다. SSH IRQ를 housekeeping CPU로 이동하거나, screen/tmux에서 실행하고 분리(detach)한 뒤 결과 파일을 확인하는 방식을 권장합니다.

Q: 측정 시간은 얼마나 해야 충분한가?

A: 일반적인 검증은 5분~10분이면 충분합니다. SLA 검증이나 회귀 비교는 30분~1시간을 권장합니다. 극저지연 시스템(HFT, PLC)에서는 24시간 이상 연속 측정하여 드문 이벤트까지 포착하는 것이 좋습니다. 측정 시간이 길수록 max 값은 높아지는 경향이 있으므로, 비교 시 반드시 같은 측정 시간을 사용해야 합니다.

Q: multi-socket 시스템에서 주의할 점은?

A: 소켓(Socket) 간 인터커넥트 트래픽이 레이턴시에 영향을 줄 수 있습니다. 실시간 워크로드와 측정을 같은 소켓의 CPU에서 실행하고, 메모리도 같은 NUMA 노드에 바인딩하세요. lstoponumactl -H로 토폴로지(Topology)를 먼저 확인합니다. Intel PCM으로 소켓 간 트래픽을 모니터링할 수 있습니다.

Q: ARM 프로세서에서도 rtla를 사용할 수 있는가?

A: 네, rtla는 아키텍처 독립적입니다. timerlat/osnoise tracer는 hrtimer와 tracefs에 의존하며, 이들은 ARM을 포함한 모든 주요 아키텍처에서 지원됩니다. 다만 hwlat의 일부 기능(SMI 카운터 등)은 x86 전용입니다. ARM에서는 GIC(Generic Interrupt Controller) 관련 지연이 x86과 다를 수 있으므로, 아키텍처별 특성을 감안하여 해석해야 합니다.

RTLA 프레임워크 아키텍처

RTLA는 단순한 단일 도구가 아니라, 커널 tracing 인프라 위에 계층적으로 구축된 레이턴시 분석 프레임워크입니다. 사용자 공간 프론트엔드(rtla 바이너리), 커널 내부 tracer(timerlat, osnoise, hwlat), tracefs 인터페이스, tracepoint 이벤트 시스템이 유기적으로 연결되어 동작합니다. 이 구조를 이해하면 각 도구의 역할 경계와 한계를 정확히 파악할 수 있습니다.

RTLA 프레임워크 아키텍처 사용자 공간 (User Space) rtla CLI timerlat/osnoise/hwnoise trace-cmd 범용 tracing 프론트엔드 cyclictest 사용자 공간 지연 측정 bpftrace/perf eBPF/PMU 계측 KernelShark 시각화 GUI tracefs 인터페이스 (/sys/kernel/tracing/) current_tracer trace_pipe osnoise/cpus tracing_thresh events/osnoise/* per_cpu/ 커널 Tracer 계층 (kernel/trace/trace_osnoise.c) timerlat tracer hrtimer 기반 IRQ/Thread 지연 분리 osnoise tracer 루프 기반 간섭 측정 NMI/IRQ/softirq/thread hwlat tracer 타이트 루프 기반 SMI/NMI 검출 tracepoint 시스템 irq_noise, softirq_noise thread_noise, nmi_noise 하드웨어 / 커널 인프라 hrtimer 고해상도 타이머 ktime/TSC 나노초 시간 소스 IRQ 서브시스템 인터럽트 처리 경로 스케줄러 RT 정책, 런큐 SMI/NMI 펌웨어

RTLA 프레임워크의 핵심 설계 원칙은 측정 계층의 분리입니다. 각 tracer는 독립적인 측정 영역을 담당하며, 사용자 공간 도구는 이들을 조합하여 종합적인 분석을 제공합니다.

계층구성 요소역할소스 위치
사용자 공간 프론트엔드rtla 바이너리tracefs 제어, 출력 포맷팅, auto_analysistools/tracing/rtla/
VFS 인터페이스tracefs커널-사용자 공간 통신 채널kernel/trace/trace.c
커널 tracertimerlat, osnoise, hwlat실제 측정 로직kernel/trace/trace_osnoise.c
이벤트 시스템tracepoint이벤트 기록 및 전달include/trace/events/osnoise.h
하드웨어 인프라hrtimer, TSC, APIC시간 소스, 타이머, 인터럽트아키텍처별

RTLA 소스 코드 구조

RTLA 프레임워크의 소스 코드는 커널 트리의 두 위치에 분산되어 있습니다. 커널 측 tracer와 사용자 공간 도구를 분리하여 이해하면 동작 원리를 정확히 파악할 수 있습니다.

# RTLA 소스 코드 구조

# === 커널 측 (tracer 구현) ===
kernel/trace/trace_osnoise.c     # timerlat + osnoise tracer 핵심
kernel/trace/trace_hwlat.c       # hwlat tracer
include/trace/events/osnoise.h   # tracepoint 이벤트 정의
kernel/trace/trace.c             # tracefs 코어 프레임워크

# === 사용자 공간 (rtla 도구) ===
tools/tracing/rtla/
├── Makefile
├── src/
│   ├── rtla.c           # 메인 진입점, 서브커맨드 디스패치
│   ├── timerlat.c       # timerlat 서브커맨드 (top/hist/trace)
│   ├── timerlat_top.c   # timerlat top 모드
│   ├── timerlat_hist.c  # timerlat hist 모드
│   ├── timerlat_aa.c    # auto_analysis 엔진
│   ├── osnoise.c        # osnoise 서브커맨드
│   ├── osnoise_top.c    # osnoise top 모드
│   ├── osnoise_hist.c   # osnoise hist 모드
│   ├── hwnoise.c        # hwnoise 서브커맨드
│   └── utils.c          # tracefs 유틸리티 함수
└── man/                 # man page 소스

# 소스 빌드
cd /usr/src/linux
make -C tools/tracing/rtla/ clean
make -C tools/tracing/rtla/
sudo make -C tools/tracing/rtla/ install

timerlat과 osnoise의 내부 관계

timerlat은 사실 osnoise tracer의 확장입니다. 커널 소스에서 둘은 같은 파일(trace_osnoise.c)에 구현되어 있으며, osnoise의 간섭 감지 인프라를 공유합니다. timerlat이 활성화되면 osnoise의 간섭 분류 기능이 함께 동작하여, 타이머 지연의 원인을 자동으로 분류할 수 있습니다.

timerlat / osnoise 공유 인프라 trace_osnoise.c — 공유 인프라 osnoise_variables (per-CPU) 간섭 분류 콜백 tracepoint 이벤트 timerlat 전용 timerlat_variables (per-CPU) timerlat_irq() — IRQ 콜백 timerlat_main() — 스레드 루프 hrtimer 주기 관리 osnoise 전용 osnoise_main() — 측정 루프 run_osnoise() — 타이트 루프 sample_period / sample_runtime 누적 간섭량 집계 auto_analysis 엔진 (timerlat_aa.c) threshold 초과 시 자동 원인 분류 osnoise tracepoint → IRQ/softirq/thread 간섭 식별 → 원인 요약 출력
/* trace_osnoise.c — timerlat이 osnoise 인프라를 공유하는 구조 (간략화) */

/* 공유 per-CPU 변수: 간섭 추적 */
struct osnoise_variables {
    u64 irq_count;        /* IRQ 간섭 횟수 */
    u64 softirq_count;    /* softirq 간섭 횟수 */
    u64 thread_count;     /* 스레드 간섭 횟수 */
    u64 nmi_count;        /* NMI 간섭 횟수 */
    u64 noise;            /* 총 간섭 시간 (ns) */
    u64 runtime;          /* 측정 루프 실행 시간 (ns) */
};

/* timerlat 전용 per-CPU 변수 */
struct timerlat_variables {
    struct task_struct *kthread;  /* per-CPU 커널 스레드 */
    struct hrtimer timer;         /* 고해상도 타이머 */
    u64 abs_period;               /* 예정된 만료 시각 */
    u64 timer_period;             /* 타이머 주기 (ns) */
    unsigned int count;          /* 시퀀스 카운터 */
};

/* timerlat 활성화 시 osnoise 간섭 콜백도 함께 등록 */
static int timerlat_tracer_init(struct trace_array *tr)
{
    /* osnoise 간섭 감지 콜백 등록 */
    osnoise_register_callbacks();

    /* per-CPU timerlat 스레드 생성 */
    start_per_cpu_timerlat_threads(tr);

    return 0;
}
아키텍처 설계 의도: RTLA의 계층 분리 설계는 의도적입니다. Daniel Bristot de Oliveira(RTLA 주 개발자)가 강조한 원칙은 "측정 도구가 측정 대상에 미치는 영향을 최소화"하는 것입니다. 커널 tracer는 최소한의 코드만 실행하고, 복잡한 분석과 포맷팅은 사용자 공간의 rtla가 담당합니다. 이 분리 덕분에 측정 오버헤드가 최소화되면서도 분석 기능은 풍부하게 제공할 수 있습니다.

레이턴시 측정 흐름 상세

timerlat의 측정 흐름을 타이머 인터럽트 발생부터 최종 측정 완료까지 나노초 단위로 추적합니다. 각 단계에서 어떤 지연이 발생할 수 있는지, 그리고 그 지연이 timerlat 출력의 어떤 열에 반영되는지를 정확히 이해하면 분석 정확도가 크게 향상됩니다.

timerlat 레이턴시 측정 흐름 (나노초 단위) t T₀ IRQ 비활성 T₁ 다른 IRQ 처리 T₂ timerlat_irq() T₃ 스케줄링 지연 (runqueue 대기) T₄ timerlat_main() T₅ 사용자 공간 (6.3+) T₆ T₀ʼ (다음 주기) 지연 분해 (Latency Breakdown) IRQ 지연 = T₂ - T₀ 구성 요소: 1. 인터럽트 비활성(local_irq_disable) 구간 2. 선행 IRQ 처리 대기 (IRQ 경합) 3. NMI/SMI 처리 시간 (OS 제어 불가) 스레드 지연 = T₄ - T₀ 구성 요소: 1. IRQ 지연 (T₂ - T₀) 포함 2. softirq 후속 처리 시간 3. 런큐 대기 (우선순위 경합) 순수 스케줄링 지연 = Thread - IRQ Thread 열 - IRQ 열 = 순수 스레드 대기 시간 이 값이 크면: softirq, 우선순위 역전, 런큐 혼잡 사용자 공간 지연 = T₆ - T₀ (6.3+) 커널 스레드 → 사용자 공간 전환 비용 포함 cyclictest와 유사한 종단 지연 측정 측정 흐름 핵심 포인트 1. T₀는 hrtimer가 만료된 예정 시각이지, 실제 실행 시각이 아닙니다. 2. IRQ 지연은 "인터럽트가 늦게 처리된 시간", 스레드 지연은 "스레드가 늦게 깨어난 시간"입니다. 3. Thread 열은 IRQ 지연을 포함합니다. 순수 스케줄링 지연 = Thread - IRQ. 4. auto_analysis(6.3+)는 T₂~T₄ 사이에 발생한 간섭을 자동으로 분류하여 원인을 제시합니다.

각 단계에서 발생할 수 있는 지연의 구체적인 원인과 크기를 정리합니다.

구간지연 범위주요 원인진단 방법
T₀ → T₁ (IRQ 전달)0.1~100μslocal_irq_disable 구간, SMI, NMIhwlat, irqsoff tracer
T₁ → T₂ (IRQ 경합)0~50μs선행 IRQ 처리, IRQ 큐 대기trace irq_handler_entry/exit
T₂ → T₃ (IRQ 콜백)<1μstimerlat_irq() 실행 시간 (최소)function_graph tracer
T₃ → T₄ (스케줄링)1~500μssoftirq, 우선순위 역전, 런큐 경합sched_switch trace, osnoise
T₄ → T₅ (스레드 실행)<1μstimerlat_main() 측정 코드function_graph tracer
T₅ → T₆ (사용자 공간)1~10μs커널→사용자 전환, 시스템 콜timerlat --user-threads (6.3+)

IRQ 지연 경로 상세 분석

IRQ 지연(T₀→T₂)이 클 때는 인터럽트 비활성 구간, 선행 IRQ 처리, 또는 하드웨어 수준 간섭(SMI/NMI) 중 하나가 원인입니다. 각 원인을 구별하는 방법을 정리합니다.

# === IRQ 지연 원인 분리 ===

# 1. irqsoff tracer: 인터럽트 비활성 구간 추적
echo irqsoff > /sys/kernel/tracing/current_tracer
echo 50 > /sys/kernel/tracing/tracing_thresh   # 50μs 초과 시 기록
echo 1 > /sys/kernel/tracing/tracing_on
sleep 10
echo 0 > /sys/kernel/tracing/tracing_on
cat /sys/kernel/tracing/trace | head -30
# → irq_disable ~ irq_enable 사이의 가장 긴 구간 출력

# 2. preemptoff tracer: 선점 비활성 구간 추적
echo preemptoff > /sys/kernel/tracing/current_tracer
echo 50 > /sys/kernel/tracing/tracing_thresh
echo 1 > /sys/kernel/tracing/tracing_on
sleep 10
echo 0 > /sys/kernel/tracing/tracing_on
cat /sys/kernel/tracing/trace | head -30

# 3. preemptirqsoff: 두 가지를 동시에 추적
echo preemptirqsoff > /sys/kernel/tracing/current_tracer
echo 50 > /sys/kernel/tracing/tracing_thresh

# 4. timerlat trace의 IRQ 지연과 교차 분석
sudo rtla timerlat trace -T 10 -c 2,3 -d 60s

# trace 출력에서 확인할 패턴:
# - irq_handler_entry/exit 사이 시간이 긴 IRQ → 해당 IRQ 이동
# - local_irq_disable/enable 사이 시간이 긴 구간 → 커널 코드 경로 확인
# - IRQ 없이 지연 발생 → SMI/NMI 의심 → hwlat 확인

스레드 지연 경로 상세 분석

스레드 지연(T₃→T₄)이 클 때의 원인 분리 방법입니다. 이 구간은 wake_up_process()가 호출된 이후 실제로 timerlat 스레드가 CPU를 얻을 때까지의 시간입니다.

# === 스레드 지연 원인 분리 ===

# 1. 현재 CPU에서 실행 중인 다른 작업 확인
# timerlat trace에서 sched_switch 이벤트를 확인
sudo rtla timerlat trace -T 20 -c 2 --aa-only
# auto_analysis 출력 예시:
# ## CPU 2 hit stop tracing, analyzing it ##
# IRQ handler delay:           3 us
# IRQ latency:                 3 us
#     Blocked by:
#       irq/18-eth0             2 us
# Thread latency:             25 us
#     Blocked by:
#       ksoftirqd/2            15 us  [NET_RX]
#       kworker/2:1             5 us

# 2. 우선순위 역전 확인
ps -eo pid,cls,rtprio,psr,comm | awk '$4==2' | sort -k3 -rn
# timerlat 스레드(95)보다 높은 우선순위 스레드가 같은 CPU에 있는지 확인

# 3. softirq 간섭 확인
# osnoise가 softirq_noise를 보고하면 softirq가 원인
sudo rtla osnoise trace -T 10 -c 2 -d 30s

# 4. RT 스로틀링 확인
cat /proc/sys/kernel/sched_rt_runtime_us
# 950000이면 RT 스로틀링 활성 → 50ms 주기 스파이크 가능
auto_analysis 해석 주의: auto_analysis는 threshold 초과 시 해당 시점 전후의 osnoise tracepoint를 분석하여 "누가 CPU를 가져갔는지"를 보고합니다. 그러나 이 분석은 해당 측정 주기 내의 간섭만 보고하므로, 이전 주기에서 누적된 softirq 백로그(Backlog)나 RCU 콜백 등은 직접 보여주지 않습니다. auto_analysis가 "원인 없음"을 보고해도 지연이 반복된다면 osnoise hist를 병행해야 합니다.

osnoise 측정 알고리즘 상세

osnoise의 측정 알고리즘은 직관적이면서도 정교합니다. 핵심 아이디어는 "CPU를 완전히 점유한 채 루프를 돌면서, 루프 반복 사이에 예상보다 오래 걸린 시간을 간섭으로 분류"하는 것입니다. 이 방식은 어떤 종류의 간섭이든 빠짐없이 감지할 수 있다는 장점이 있습니다.

osnoise 측정 알고리즘 osnoise_main() 시작 last = ktime_get_ns() while (runtime < sample_runtime) now = ktime_get_ns(); delta = now - last delta > threshold? 간섭 감지! noise += delta 간섭 유형 분류: in_nmi() → nmi_count++ in_irq() → irq_count++ 아니오 정상 루프 간섭 유형 분류 메커니즘 NMI in_nmi() true IRQ in_irq() true softirq in_softirq() true thread 나머지 (커널 스레드) 우선순위: NMI > IRQ > softirq > thread — 중첩 시 가장 높은 우선순위로 분류
/* trace_osnoise.c — osnoise 측정 루프 핵심 (간략화) */
static int run_osnoise(void)
{
    struct osnoise_variables *osn_var = this_cpu_osn_var();
    u64 start, sample_start, last, now, delta;
    u64 total_noise = 0;

    sample_start = ktime_get_ns();
    last = sample_start;

    /* 타이트 루프: sample_runtime 동안 반복 */
    do {
        now = ktime_get_ns();
        delta = now - last;

        /* 루프 1회 실행에 예상되는 최소 시간보다 크면 간섭 */
        if (delta > osn_var->threshold) {
            total_noise += delta;

            /* 간섭 유형 분류 */
            if (in_nmi()) {
                osn_var->nmi_count++;
                trace_nmi_noise(delta);
            } else if (in_irq()) {
                osn_var->irq_count++;
                trace_irq_noise(delta);
            } else if (in_softirq()) {
                osn_var->softirq_count++;
                trace_softirq_noise(delta);
            } else {
                osn_var->thread_count++;
                trace_thread_noise(delta);
            }
        }

        last = now;
    } while (now - sample_start < osn_var->sample_runtime);

    osn_var->noise = total_noise;
    osn_var->runtime = now - sample_start;

    return 0;
}

osnoise 간섭 소스별 상세 분석

각 간섭 유형의 특성과 발생 시나리오를 상세히 분석합니다.

간섭 유형컨텍스트일반적 지속 시간발생 빈도제어 가능성
NMINon-Maskable Interrupt1~10μs (perf), 10~500μs (SMI)낮음 (perf 샘플링 주기)nmi_watchdog=0, perf 중지
IRQ (하드)인터럽트 컨텍스트1~100μs (디바이스 의존)높음 (NIC, 스토리지)IRQ affinity 이동
softirqsoftirq 컨텍스트1~1000μs (NET_RX 등)IRQ에 따라 변동IRQ 이동 시 함께 이동
thread커널 스레드변동 (1μs~수 ms)설정 의존cgroup, affinity, 우선순위
# === 간섭 유형별 상세 추적 ===

# 1. NMI 간섭 추적
# perf NMI 비활성화 (nmi_watchdog)
echo 0 > /proc/sys/kernel/nmi_watchdog

# perf 실행 여부 확인
ps aux | grep perf
# perf가 실행 중이면 NMI 간섭 발생 가능

# 2. IRQ 간섭 상세
# 격리 CPU에 할당된 IRQ 확인
for irq in $(ls /proc/irq/); do
    if [ -f /proc/irq/$irq/smp_affinity_list ]; then
        affinity=$(cat /proc/irq/$irq/smp_affinity_list 2>/dev/null)
        if echo "$affinity" | grep -qE '(^|,)2(,|$)|(^|,)3(,|$)'; then
            name=$(cat /proc/irq/$irq/actions 2>/dev/null || echo "unknown")
            echo "IRQ $irq → CPUs: $affinity ($name)"
        fi
    fi
done

# 3. softirq 유형별 세분화 (커널 6.6+)
# osnoise trace에서 softirq 유형 확인
sudo rtla osnoise trace -T 5 -c 2,3 -d 30s 2>&1 | grep softirq
# 출력 예시:
# softirq_noise: softirq=NET_RX duration=45000 ns
# softirq_noise: softirq=TIMER duration=2000 ns
# softirq_noise: softirq=RCU duration=8000 ns

# 4. thread 간섭 추적
# 격리 CPU에서 실행되는 커널 스레드 확인
ps -eo psr,pid,cls,rtprio,ni,comm --sort=psr | awk '$1==2 || $1==3'
# kworker, ksoftirqd, rcuop 등이 보이면 간섭 원인
softirq 세분류(6.6+)의 활용: 커널 6.6부터 osnoise는 softirq 간섭을 NET_RX, NET_TX, BLOCK, TIMER, RCU 등으로 세분류합니다. 이전 버전에서는 단순히 "softirq"로만 분류되어 원인 추적에 추가 작업이 필요했습니다. 예를 들어 NET_RX softirq가 주 간섭 원인이면 해당 NIC의 IRQ를 다른 CPU로 이동하면 softirq도 함께 이동합니다.

간섭 소스 추적

osnoise가 "이 CPU에 간섭이 있다"고 보고했을 때, 정확히 어떤 경로에서 간섭이 발생하는지를 추적하는 고급 기법을 다룹니다. tracing_threshold, print_stack, ftrace event filter를 조합하면 간섭의 정확한 커널 함수 경로를 복원할 수 있습니다.

간섭 소스 추적 워크플로 1단계: 간섭 감지 rtla osnoise top noise > 0 확인 2단계: 간섭 유형 분류 rtla osnoise trace -T 10 NMI/IRQ/softirq/thread 3단계: 스택 추적 print_stack + threshold 커널 함수 경로 확인 tracing_threshold + print_stack 활용 설정 방법: echo 1 > osnoise/print_stack echo 10 > tracing_thresh 출력 예시: osnoise/2 [002] 123.456: irq_noise => trace_irq_noise+0x38 => __handle_irq_event+0x90 => handle_irq_event+0x3c => handle_edge_irq+0x98 => do_IRQ+0x50 → 어떤 IRQ 핸들러가 오래 걸렸는지 확인 가능 ftrace event filter 조합 특정 IRQ만 필터링: echo 'irq==18' > events/ irq/irq_handler_entry/filter duration 기반 필터링: echo 'duration > 10000' > events/ osnoise/irq_noise/filter function_graph 연동: echo function_graph > current_tracer echo e1000_intr > set_graph_function → 특정 드라이버 함수의 실행 시간 추적 가능
# === 간섭 소스 추적 실전 ===

# 1단계: 간섭 확인
sudo rtla osnoise top -c 2,3 -d 30s
# noise > 0이면 2단계로 진행

# 2단계: 간섭 유형 분류 + 스택 추적
# print_stack 활성화
echo 1 > /sys/kernel/tracing/osnoise/print_stack
# osnoise tracer 활성화
echo osnoise > /sys/kernel/tracing/current_tracer
echo 2-3 > /sys/kernel/tracing/osnoise/cpus
# threshold 설정 (10μs 초과만 기록)
echo 10 > /sys/kernel/tracing/tracing_thresh
echo 1 > /sys/kernel/tracing/tracing_on
sleep 30
echo 0 > /sys/kernel/tracing/tracing_on
cat /sys/kernel/tracing/trace

# 3단계: 특정 간섭 원인의 함수 경로 추적
# 예: IRQ 간섭이 원인이면 해당 IRQ의 핸들러 함수를 function_graph로 추적
echo nop > /sys/kernel/tracing/current_tracer
echo function_graph > /sys/kernel/tracing/current_tracer
# e1000_intr (네트워크 드라이버 IRQ 핸들러) 추적
echo e1000_intr > /sys/kernel/tracing/set_graph_function
echo 2 > /sys/kernel/tracing/set_ftrace_pid   # 특정 CPU만
echo 1 > /sys/kernel/tracing/tracing_on
sleep 5
echo 0 > /sys/kernel/tracing/tracing_on
cat /sys/kernel/tracing/trace

# 4단계: bpftrace로 실시간 간섭 모니터링
sudo bpftrace -e '
tracepoint:osnoise:irq_noise /args->duration > 10000/ {
    printf("CPU %d: IRQ %s duration %d ns\n",
        cpu, args->desc, args->duration);
}

tracepoint:osnoise:softirq_noise /args->duration > 5000/ {
    printf("CPU %d: softirq duration %d ns\n",
        cpu, args->duration);
}

tracepoint:osnoise:thread_noise /args->duration > 5000/ {
    printf("CPU %d: thread %s duration %d ns\n",
        cpu, args->comm, args->duration);
}
'

간섭 원인별 대응 매트릭스

간섭 유형, 구체적 원인, 진단 명령, 해결 방법을 체계적으로 정리합니다.

간섭 유형구체적 원인진단 명령해결 방법
NMIperf 샘플링 NMIps aux | grep perfperf 종료 또는 샘플링 주기 감소
nmi_watchdogcat /proc/sys/kernel/nmi_watchdogecho 0 > /proc/sys/kernel/nmi_watchdog
SMI (NMI로 분류)rdmsr -a 0x34BIOS 설정 변경
IRQNIC IRQcat /proc/interrupts | grep ethIRQ affinity 이동: echo 3 > /proc/irq/N/smp_affinity
스토리지 IRQcat /proc/interrupts | grep ahciIRQ affinity 이동
타이머 IRQ (LOC)cat /proc/interrupts | grep LOCnohz_full 설정
softirqNET_RX/NET_TXcat /proc/softirqsNIC IRQ를 다른 CPU로 이동 (softirq 동반)
RCU softirqgrep RCU /proc/softirqsrcu_nocbs 부트 파라미터
TIMER softirqgrep TIMER /proc/softirqsnohz_full 설정
threadkworkerps -eo psr,comm | grep kworkerWQ_UNBOUND affinity 제한
ksoftirqdps -eo psr,comm | grep ksoftirqdIRQ 소스 이동
rcuop/rcuosps -eo psr,comm | grep rcurcu_nocbs 부트 파라미터

자동 분석 스크립트

RTLA 측정 결과를 수동으로 해석하는 것은 반복적이고 오류가 발생하기 쉽습니다. 자동 수집, 파싱, 시각화 스크립트를 사용하면 분석 효율을 크게 높일 수 있습니다.

종합 측정 자동화 스크립트

#!/bin/bash
# rtla-benchmark.sh — RTLA 종합 벤치마크 자동화
# 사용법: sudo ./rtla-benchmark.sh [CPU목록] [측정시간(초)] [출력디렉터리]

CPUS=${1:-"2,3"}
DURATION=${2:-300}
OUTDIR=${3:-"/tmp/rtla-benchmark-$(date +%Y%m%d_%H%M%S)"}
PRIO=95
BUCKET=1
ENTRIES=100

mkdir -p "$OUTDIR"
echo "[RTLA] 벤치마크 시작: CPUs=$CPUS Duration=${DURATION}s Output=$OUTDIR"

# 시스템 정보 수집
echo "[RTLA] 시스템 정보 수집..."
{
    echo "=== 커널 ==="
    uname -a
    echo "=== 선점 모드 ==="
    grep PREEMPT /boot/config-$(uname -r) 2>/dev/null || echo "unknown"
    echo "=== 부트 파라미터 ==="
    cat /proc/cmdline
    echo "=== 격리 CPU ==="
    cat /sys/devices/system/cpu/isolated
    echo "=== nohz_full ==="
    cat /sys/devices/system/cpu/nohz_full 2>/dev/null || echo "not set"
    echo "=== RT 스로틀링 ==="
    cat /proc/sys/kernel/sched_rt_runtime_us
    echo "=== CPUfreq governor ==="
    cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor 2>/dev/null | head -1
} > "$OUTDIR/system-info.txt"

# 1. 하드웨어 잡음 (hwlat)
echo "[RTLA] 1/5 하드웨어 잡음 측정 (60초)..."
rtla hwnoise top -c $CPUS -d 60s 2>&1 | tee "$OUTDIR/hwlat.txt"

# 2. CPU 간섭 (osnoise)
echo "[RTLA] 2/5 CPU 간섭 측정 (60초)..."
rtla osnoise top -c $CPUS -d 60s 2>&1 | tee "$OUTDIR/osnoise-top.txt"
rtla osnoise hist -c $CPUS -d 60s -b $BUCKET -e $ENTRIES 2>&1 | \
    tee "$OUTDIR/osnoise-hist.txt"

# 3. timerlat top
echo "[RTLA] 3/5 timerlat top 측정 (${DURATION}초)..."
rtla timerlat top -c $CPUS -P $PRIO -d ${DURATION}s 2>&1 | \
    tee "$OUTDIR/timerlat-top.txt"

# 4. timerlat histogram
echo "[RTLA] 4/5 timerlat histogram 수집 (${DURATION}초)..."
rtla timerlat hist -c $CPUS -P $PRIO -d ${DURATION}s \
    -b $BUCKET -e $ENTRIES 2>&1 | tee "$OUTDIR/timerlat-hist.txt"

# 5. cyclictest 비교
echo "[RTLA] 5/5 cyclictest 비교 (${DURATION}초)..."
cyclictest -m -S -p $PRIO -i 1000 -D ${DURATION}s \
    -h 100 -q -a $CPUS 2>&1 | tee "$OUTDIR/cyclictest.txt"

# 결과 요약
echo "[RTLA] 결과 요약:"
echo "=== 하드웨어 잡음 ==="
tail -3 "$OUTDIR/hwlat.txt"
echo "=== CPU 간섭 ==="
tail -3 "$OUTDIR/osnoise-top.txt"
echo "=== timerlat 최대값 ==="
grep -E "^(min|avg|max)" "$OUTDIR/timerlat-hist.txt"
echo "=== cyclictest ==="
grep "Max:" "$OUTDIR/cyclictest.txt"

echo "[RTLA] 벤치마크 완료. 결과: $OUTDIR"

히스토그램 시각화 (gnuplot)

#!/bin/bash
# rtla-plot.sh — timerlat 히스토그램을 gnuplot으로 시각화
# 사용법: ./rtla-plot.sh timerlat-hist.txt output.png

INPUT=$1
OUTPUT=${2:-"latency-histogram.png"}

# timerlat hist 출력에서 데이터 추출
# Index IRQ-002 Thr-002 IRQ-003 Thr-003 형태

# gnuplot 스크립트 생성
cat > /tmp/rtla-plot.gnuplot <<'GNUPLOT'
set terminal png size 1200,600 enhanced font "D2Coding,12"
set output ARG2

set title "timerlat 레이턴시 히스토그램" font ",16"
set xlabel "레이턴시 (μs)"
set ylabel "샘플 수"
set grid
set style fill solid 0.5
set boxwidth 0.8

set key top right

# 숫자만 있는 행 추출 (Index 열 = 양의 정수)
plot ARG1 using 1:2 with boxes title "IRQ" lc rgb "#FF6B6B", \
     ARG1 using 1:3 with boxes title "Thread" lc rgb "#4ECDC4"
GNUPLOT

# 데이터 파일에서 숫자 행만 추출
awk '/^[0-9]+/ {print $1, $2, $3}' "$INPUT" > /tmp/rtla-data.txt

# gnuplot 실행
gnuplot -c /tmp/rtla-plot.gnuplot /tmp/rtla-data.txt "$OUTPUT"
echo "그래프 생성: $OUTPUT"

matplotlib 시각화 (Python)

#!/usr/bin/env python3
# rtla_visualize.py — timerlat 히스토그램 시각화 및 통계 분석
# 사용법: python3 rtla_visualize.py timerlat-hist.txt [output.png]

import sys
import re
import numpy as np

def parse_timerlat_hist(filename):
    """timerlat hist 출력 파싱"""
    irq_data = {}
    thr_data = {}
    stats = {}

    with open(filename) as f:
        for line in f:
            # 숫자 행: "1  854  12  891  8" 형태
            m = re.match(r'^\s*(\d+)\s+(.*)', line)
            if m:
                idx = int(m.group(1))
                values = [int(x) for x in m.group(2).split()]
                if len(values) >= 2:
                    irq_data[idx] = values[0]
                    thr_data[idx] = values[1]

            # 통계 행: "min: 1  2" 형태
            for stat in ['min', 'avg', 'max', 'count']:
                if line.strip().startswith(stat + ':'):
                    stats[stat] = [int(float(x))
                        for x in re.findall(r'[\d.]+', line)]

    return irq_data, thr_data, stats

def calculate_percentile(hist_data, percentile):
    """히스토그램에서 백분위수 계산"""
    total = sum(hist_data.values())
    target = int(total * percentile / 100)
    cumulative = 0
    for bucket in sorted(hist_data.keys()):
        cumulative += hist_data[bucket]
        if cumulative >= target:
            return bucket
    return max(hist_data.keys())

def print_analysis(irq_data, thr_data, stats):
    """분석 결과 출력"""
    print("=== timerlat 히스토그램 분석 ===")
    for name, data in [("IRQ", irq_data), ("Thread", thr_data)]:
        if not data:
            continue
        total = sum(data.values())
        p50 = calculate_percentile(data, 50)
        p99 = calculate_percentile(data, 99)
        p999 = calculate_percentile(data, 99.9)
        max_val = max(k for k, v in data.items() if v > 0)
        print(f"  {name}: count={total} p50={p50}μs p99={p99}μs "
              f"p999={p999}μs max={max_val}μs")

    # SLA 판정
    thr_p99 = calculate_percentile(thr_data, 99) if thr_data else 0
    thr_max = max(k for k, v in thr_data.items() if v > 0) if thr_data else 0
    print(f"\n  SLA 판정: p99={thr_p99}μs max={thr_max}μs")

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("사용법: python3 rtla_visualize.py <hist-file> [output.png]")
        sys.exit(1)

    irq, thr, stats = parse_timerlat_hist(sys.argv[1])
    print_analysis(irq, thr, stats)

    # matplotlib 시각화 (선택)
    if len(sys.argv) >= 3:
        try:
            import matplotlib.pyplot as plt

            fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))

            # IRQ 히스토그램
            ax1.bar(irq.keys(), irq.values(), color='#FF6B6B', alpha=0.7)
            ax1.set_title('IRQ Latency Distribution')
            ax1.set_xlabel('Latency (μs)')
            ax1.set_ylabel('Count')
            ax1.axvline(calculate_percentile(irq, 99),
                color='red', linestyle='--', label='p99')
            ax1.legend()

            # Thread 히스토그램
            ax2.bar(thr.keys(), thr.values(), color='#4ECDC4', alpha=0.7)
            ax2.set_title('Thread Latency Distribution')
            ax2.set_xlabel('Latency (μs)')
            ax2.set_ylabel('Count')
            ax2.axvline(calculate_percentile(thr, 99),
                color='red', linestyle='--', label='p99')
            ax2.legend()

            plt.tight_layout()
            plt.savefig(sys.argv[2], dpi=150)
            print(f"그래프 저장: {sys.argv[2]}")
        except ImportError:
            print("matplotlib가 설치되지 않았습니다.")

변경 전/후 자동 비교 스크립트

#!/bin/bash
# rtla-compare.sh — 설정 변경 전/후 자동 비교
# 사용법: sudo ./rtla-compare.sh before.txt after.txt

BEFORE=$1
AFTER=$2

echo "=== timerlat 히스토그램 비교 ==="
echo ""

# max 값 비교
MAX_BEFORE=$(grep "^max:" "$BEFORE" | awk '{print $2}')
MAX_AFTER=$(grep "^max:" "$AFTER" | awk '{print $2}')
echo "IRQ max: $MAX_BEFORE → $MAX_AFTER"

MAX_THR_BEFORE=$(grep "^max:" "$BEFORE" | awk '{print $3}')
MAX_THR_AFTER=$(grep "^max:" "$AFTER" | awk '{print $3}')
echo "Thread max: $MAX_THR_BEFORE → $MAX_THR_AFTER"

# avg 값 비교
AVG_BEFORE=$(grep "^avg:" "$BEFORE" | awk '{print $2}')
AVG_AFTER=$(grep "^avg:" "$AFTER" | awk '{print $2}')
echo "IRQ avg: $AVG_BEFORE → $AVG_AFTER"

# 판정
if [ -n "$MAX_THR_BEFORE" ] && [ -n "$MAX_THR_AFTER" ]; then
    if [ "$MAX_THR_AFTER" -lt "$MAX_THR_BEFORE" ]; then
        echo "판정: 개선 (Thread max ${MAX_THR_BEFORE}μs → ${MAX_THR_AFTER}μs)"
    elif [ "$MAX_THR_AFTER" -gt "$MAX_THR_BEFORE" ]; then
        RATIO=$(( MAX_THR_AFTER * 100 / MAX_THR_BEFORE ))
        echo "판정: 회귀 (Thread max ${MAX_THR_BEFORE}μs → ${MAX_THR_AFTER}μs, ${RATIO}%)"
        if [ $RATIO -gt 200 ]; then
            echo "경고: 2배 이상 회귀 — timerlat trace로 원인 추적 필요"
        fi
    else
        echo "판정: 동일"
    fi
fi
자동화 팁: 이 스크립트들을 cron이나 systemd timer에 등록하면 정기 측정 자동화가 가능합니다. 예를 들어 매일 새벽 3시에 5분 벤치마크를 실행하고, 이전 결과와 비교하여 회귀가 감지되면 알림을 보내는 식입니다. rtla timerlat hist의 출력은 텍스트 기반이므로 awk, grep 등 표준 도구로 쉽게 파싱할 수 있습니다.

cyclictest vs timerlat 심층 비교

cyclictesttimerlat은 모두 주기적 깨우기 지연을 측정하지만, 측정 위치, 측정 방식, 측정 결과의 의미가 근본적으로 다릅니다. 이 차이를 정확히 이해하면 두 도구를 적절히 조합하여 사용할 수 있습니다.

cyclictest vs timerlat: 측정 경로 비교 cyclictest 측정 경로 clock_nanosleep() 시스템 콜 진입 hrtimer 등록/대기 타이머 만료 + IRQ 스케줄링 + 복귀 clock_gettime() - 예정 시각 측정 범위 전체 경로 포함: - IRQ 지연 - 스케줄링 지연 - 커널→사용자 전환 - clock_gettime 비용 분리 불가: 어떤 구간에서 지연이 발생했는지 알 수 없음 timerlat 측정 경로 hrtimer 만료 (T₀) timerlat_irq() IRQ 지연 기록 wake_up_process() 스케줄링 대기 timerlat_main() 스레드 지연 기록 사용자 공간 (6.3+) IRQ 구간 T₀→T₂ 정밀 측정 인터럽트 지연만 분리 나노초 정밀도 Thread 구간 T₃→T₄ 정밀 측정 스케줄링 지연 분리 auto_analysis로 원인 자동 식별

두 도구의 측정값 차이를 실제 수치로 보면 다음과 같습니다.

# 같은 시스템에서 동시 측정 비교

# cyclictest (사용자 공간 측정)
sudo cyclictest -m -S -p 95 -i 1000 -D 60s -h 100 -q -a 2,3
# T: 0 (42) P:95 I:1000 C:60000 Min:1 Act:2 Avg:2 Max:11
#                                                   ^^^ 전체 경로 지연

# timerlat (커널 내부 측정)
sudo rtla timerlat top -c 2,3 -P 95 -d 60s
# CPU  IRQ_max  Thread_max
#  2     3        8
#  3     2        7
#                 ^^^ 커널 스레드까지의 지연

# 비교 해석:
# cyclictest max (11μs) > timerlat Thread max (8μs)
# 차이 (3μs) = 커널→사용자 공간 전환 + clock_gettime 오버헤드
# 이 차이가 일정하면 시스템이 정상
# 이 차이가 크게 변동하면 사용자 공간 경로에 문제가 있을 수 있음

# timerlat의 IRQ max (3μs)와 Thread max (8μs) 차이 (5μs)가
# 순수 스케줄링 지연이므로, 스케줄링 최적화 필요 여부를 판단
특성cyclictesttimerlat실전 의미
측정 오버헤드시스템 콜 2회 (nanosleep + gettime)ktime_get_ns() 1회timerlat이 더 정밀
최소 측정 단위~100ns (시스템 콜 비용)~10ns (TSC 읽기)나노초 수준 분석은 timerlat
IRQ/스레드 분리불가능기본 제공원인 분석은 timerlat
원인 자동 분석없음auto_analysis (6.3+)디버깅은 timerlat
히스토그램-h 옵션hist 서브커맨드둘 다 제공
사용자 공간 포함기본 포함6.3+ --user-threads종단 지연은 cyclictest
커널 요구없음timerlat tracer호환성은 cyclictest
CI/CD 기준값광범위하게 축적아직 축적 중회귀 비교는 cyclictest
trace 연동별도 도구 필요내장 (trace 모드)추적은 timerlat
실전 조합 전략: 최적의 조합은 다음과 같습니다. (1) cyclictest로 "현재 시스템의 종단 지연"을 정기적으로 측정하여 기준값을 축적합니다. (2) 기준값이 회귀하면 rtla timerlat trace로 원인을 추적합니다. (3) 원인이 CPU 간섭이면 rtla osnoise로 격리 상태를 점검합니다. (4) 하드웨어 잡음이 의심되면 rtla hwnoise로 확인합니다. 이 4단계를 순환하면 체계적으로 문제를 해결할 수 있습니다.

PREEMPT_RT 상세

PREEMPT_RT 패치는 리눅스 커널을 하드 실시간(hard real-time) 운영체제로 전환하는 가장 성숙한 방법입니다. 커널 6.12에서 메인라인에 완전 통합되기 시작한 이 패치의 핵심 메커니즘과 RTLA 분석에 미치는 영향을 상세히 다룹니다.

PREEMPT_RT 핵심 변환 메커니즘 일반 PREEMPT 커널 spinlock 선점 비활성화 하드 IRQ 인터럽트 컨텍스트 softirq IRQ 복귀 시 실행 • 빠른 throughput • 선점 불가 구간 존재 • 우선순위 제어 불가 • 긴 최대 지연 가능 • 네트워크/블록 후처리 • 예측 불가 실행 시간 PREEMPT_RT 커널 rt_mutex 우선순위 상속 지원 threaded IRQ 스레드 컨텍스트 ksoftirqd 전용 스레드 분리 • 선점 가능 잠금 • 우선순위 역전 방지 • 우선순위 제어 가능 • 짧은 최대 지연 • 예측 가능한 실행 • 우선순위 배치 가능 PREEMPT_RT가 timerlat 측정에 미치는 영향 IRQ 지연 일반: 1-3μs (평균), 50μs+ (최대) RT: 1-5μs (평균), 10μs (최대) 스레드 지연 일반: 2-5μs (평균), 500μs+ (최대) RT: 2-8μs (평균), 20μs (최대) 꼬리 지연 일반: 넓은 분포 (긴 꼬리) RT: 좁은 분포 (짧은 꼬리) 핵심: PREEMPT_RT는 평균 지연이 아닌 최대 지연(worst-case)을 줄이는 데 초점을 맞춥니다. timerlat hist에서 이 차이가 가장 명확하게 드러납니다. 일반 커널의 넓은 분포가 RT에서 좁고 깔끔해집니다. RT 커널에서도 지연이 크다면: isolcpus + nohz_full + rcu_nocbs + irqaffinity 조합을 먼저 확인하세요.

PREEMPT_RT 패치 적용 방법

# === PREEMPT_RT 패치 적용 (커널 빌드) ===

# 1. 커널 소스와 RT 패치 다운로드
KERNEL_VERSION="6.6.32"
RT_VERSION="6.6.32-rt32"

wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/6.6/\
patch-${RT_VERSION}.patch.xz

# 2. 패치 적용
tar xf linux-${KERNEL_VERSION}.tar.xz
cd linux-${KERNEL_VERSION}
xzcat ../patch-${RT_VERSION}.patch.xz | patch -p1

# 3. 커널 설정
make oldconfig
# 또는 menuconfig에서:
# General setup → Preemption Model → Fully Preemptible Kernel (Real-Time)
# → CONFIG_PREEMPT_RT=y 선택

# 필수 설정 확인
grep CONFIG_PREEMPT_RT .config
# CONFIG_PREEMPT_RT=y

grep CONFIG_HIGH_RES_TIMERS .config
# CONFIG_HIGH_RES_TIMERS=y

grep CONFIG_OSNOISE_TRACER .config
# CONFIG_OSNOISE_TRACER=y

# 4. 빌드 및 설치
make -j$(nproc)
sudo make modules_install
sudo make install

# 5. 부트로더 설정 (GRUB 예시)
# /etc/default/grub에 RT 부트 파라미터 추가
# GRUB_CMDLINE_LINUX="... isolcpus=domain,managed_irq,2-3 \
#   nohz_full=2-3 rcu_nocbs=2-3 irqaffinity=0-1 threadirqs \
#   processor.max_cstate=1 idle=poll"
sudo update-grub

# 6. 재부팅 후 확인
uname -v | grep PREEMPT_RT
# #1 SMP PREEMPT_RT ...

우선순위 역전 방지 메커니즘

PREEMPT_RT의 rt_mutex우선순위 상속(Priority Inheritance) 프로토콜을 구현합니다. 낮은 우선순위 스레드가 잠금(Lock)을 들고 있을 때 높은 우선순위 스레드가 대기하면, 잠금을 들고 있는 스레드의 우선순위를 일시적으로 올려 빨리 실행되게 합니다.

/* PREEMPT_RT에서 spinlock의 변환 */

/* 일반 커널: spinlock은 바쁜 대기(busy-wait) + 선점 비활성화 */
spin_lock(&lock);     /* → raw_spin_lock() → local_irq_disable() */
/* critical section */
spin_unlock(&lock);   /* → raw_spin_unlock() → local_irq_enable() */

/* PREEMPT_RT 커널: spinlock이 rt_mutex로 교체 */
spin_lock(&lock);     /* → rt_mutex_lock() → 선점 가능! */
/* critical section — 더 높은 우선순위 태스크에 의해 선점될 수 있음 */
spin_unlock(&lock);   /* → rt_mutex_unlock() */

/* raw_spin_lock()은 PREEMPT_RT에서도 진짜 spinlock으로 유지됨 */
/* → 매우 짧은 critical section에만 사용 (스케줄러 내부 등) */

timerlat으로 우선순위 역전을 감지하는 방법은 다음과 같습니다.

# 우선순위 역전 감지
# timerlat 스레드(95)가 IRQ 스레드(50)보다 높은 우선순위인데
# Thread 지연이 높으면 → 우선순위 역전 가능성

# 1. IRQ 스레드와 timerlat 스레드 우선순위 확인
ps -eo pid,cls,rtprio,psr,comm | grep -E "timerlat|irq/" | sort -k3 -rn

# 2. PI(Priority Inheritance) 활성화 확인
# RT 커널에서는 rt_mutex 기반이므로 자동 활성화
# PI 체인 추적
cat /proc/<pid>/status | grep -i pi

# 3. lockdep으로 잠금 의존성 확인
cat /proc/lockdep_stats
# 순환 의존성이 있으면 우선순위 역전 위험

# 4. RT 스케줄링 정책 확인
chrt -p <pid>
# SCHED_FIFO: 같은 우선순위 내 FIFO 순서
# SCHED_RR: 같은 우선순위 내 라운드로빈
# SCHED_DEADLINE: 최고 우선순위, deadline 기반

RT 스케줄링 정책 비교

정책우선순위특성사용 시나리오
SCHED_DEADLINE최고 (FIFO/RR보다 높음)deadline, runtime, period 기반주기적 제어 루프, 멀티미디어
SCHED_FIFO1~99같은 우선순위 내 FIFO, 무한 실행RT 워크로드, IRQ 스레드
SCHED_RR1~99같은 우선순위 내 라운드로빈동일 우선순위 RT 태스크
SCHED_OTHER (CFS)0nice 값 기반 공정 스케줄링일반 프로세스
# RT 스케줄링 정책 설정 예시

# SCHED_FIFO 우선순위 90으로 실행
sudo chrt -f 90 ./my-rt-app

# SCHED_DEADLINE 설정 (1ms 주기, 500μs 런타임)
# chrt -d --sched-runtime 500000 --sched-deadline 1000000 \
#   --sched-period 1000000 0 ./my-deadline-app

# 현재 프로세스의 스케줄링 정책 확인
chrt -p $$

# RT 밴드위스 확인 (SCHED_DEADLINE에 영향)
cat /proc/sys/kernel/sched_rt_runtime_us
cat /proc/sys/kernel/sched_rt_period_us

# mlockall로 페이지 폴트 방지 (RT 필수)
# 코드에서: mlockall(MCL_CURRENT | MCL_FUTURE)

# systemd 서비스에서 RT 설정
# [Service]
# CPUSchedulingPolicy=fifo
# CPUSchedulingPriority=90
# CPUAffinity=2 3
# LimitRTPRIO=99
# LimitMEMLOCK=infinity
PREEMPT_RT 함정들:
  • printk — RT 커널에서 printk()는 콘솔 출력으로 인해 수 ms의 지연을 유발할 수 있습니다. RT 경로에서 trace_printk()를 사용하세요.
  • 메모리 할당kmalloc(GFP_KERNEL)은 RT 컨텍스트에서 예측 불가능한 지연을 유발합니다. 사전 할당(pre-allocation)을 사용하세요.
  • 페이지 폴트 — 사용자 공간 RT 앱에서 mlockall(MCL_CURRENT|MCL_FUTURE)로 모든 페이지를 잠가야 합니다.
  • 파일 I/O — RT 경로에서 파일 I/O는 절대 금지입니다. 별도 스레드에서 처리하세요.

실시간 튜닝 가이드

레이턴시 튜닝은 단일 설정 변경이 아니라 계층별 체계적 접근이 필요합니다. 하드웨어 → BIOS → 커널 부트 → 런타임 → 애플리케이션 순서로 각 계층을 최적화하고, rtla로 각 단계의 효과를 검증합니다.

실시간 튜닝 5단계 접근법 1. 하드웨어 NUMA 토폴로지 확인 NIC 선택 (RT 지원) SSD (NVMe) ECC 메모리 전용 클럭 소스 검증: hwlat rdmsr 0x34 (SMI) 2. BIOS/UEFI USB Legacy 비활성화 Serial Redirection Off Hyper-Threading 검토 C-state 제한 Turbo Boost 고정 검증: hwlat (변경 후) SMI 카운터 비교 3. 커널 부트 isolcpus=domain,... nohz_full=N-M rcu_nocbs=N-M irqaffinity=0-1 threadirqs 검증: osnoise CPU 격리 청결도 4. 런타임 IRQ affinity 미세 조정 RT 스로틀링 해제 CPUfreq = performance workqueue affinity IRQ 스레드 우선순위 검증: timerlat IRQ/Thread 분리 5. 애플리케이션 mlockall() SCHED_FIFO/DL taskset -c N 사전 할당 lock-free 큐 검증: cyclictest 종단 지연 각 단계 검증 후 다음 단계 진행 — 순서가 중요! 원칙 1: 하위 계층이 정리되지 않으면 상위 계층 튜닝이 효과 없음 원칙 2: 변경 1개씩 적용하고 측정 → 효과가 없으면 되돌림 원칙 3: 동일 측정 조건(시간, 부하, 온도) 유지 → 비교 가능한 데이터 확보 원칙 4: 전력 절약 vs 지연 trade-off 인식 — idle=poll은 전력 소모 큼 원칙 5: Hyper-Threading은 RT 워크로드에서 비활성화 권장 — 캐시/파이프라인 경합 원칙 6: 온도 안정화 후 측정 — 콜드 스타트에서 측정하면 주파수 변동으로 결과 왜곡

단계별 튜닝 체크리스트

#!/bin/bash
# rt-tuning-checklist.sh — 실시간 튜닝 종합 체크리스트

echo "=== 실시간 튜닝 체크리스트 ==="

# 1. 하드웨어/BIOS
echo "--- 1. 하드웨어/BIOS ---"
echo "SMI 카운터:"
sudo rdmsr -a 0x34 2>/dev/null || echo "  msr-tools 미설치"

echo "Hyper-Threading:"
lscpu | grep "Thread(s) per core"

echo "NUMA 노드:"
numactl -H 2>/dev/null | head -5 || echo "  numactl 미설치"

# 2. 커널
echo "--- 2. 커널 ---"
echo "커널 버전:"
uname -r
echo "선점 모드:"
uname -v | grep -o 'PREEMPT[_A-Z]*' || echo "  비선점 커널"

echo "부트 파라미터:"
cat /proc/cmdline | tr ' ' '\n' | grep -E \
  'isolcpus|nohz_full|rcu_nocbs|irqaffinity|threadirqs|idle=|max_cstate'

# 3. CPU 격리
echo "--- 3. CPU 격리 ---"
echo "격리 CPU:"
cat /sys/devices/system/cpu/isolated
echo "nohz_full:"
cat /sys/devices/system/cpu/nohz_full 2>/dev/null || echo "  미설정"

# 4. 전력 관리
echo "--- 4. 전력 관리 ---"
echo "CPUfreq governor:"
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null \
  || echo "  cpufreq 미지원"
echo "C-state 최대:"
cat /sys/module/intel_idle/parameters/max_cstate 2>/dev/null \
  || echo "  intel_idle 미사용"

# 5. RT 설정
echo "--- 5. RT 설정 ---"
echo "RT 스로틀링:"
cat /proc/sys/kernel/sched_rt_runtime_us
echo "NMI watchdog:"
cat /proc/sys/kernel/nmi_watchdog

# 6. IRQ 배치
echo "--- 6. 격리 CPU의 IRQ ---"
# 격리 CPU(2,3)에 할당된 IRQ 확인
echo "격리 CPU에 남은 IRQ:"
cat /proc/interrupts | awk 'NR==1{next} {
  for(i=4;i<=5;i++) if($i+0>0) {print $NF, "→ CPU", i-2-1, "count:", $i; break}
}'

# 7. 워크큐
echo "--- 7. 워크큐 CPU 마스크 ---"
cat /sys/devices/virtual/workqueue/cpumask 2>/dev/null

# 8. 빠른 검증
echo "--- 8. 빠른 검증 (10초) ---"
echo "osnoise:"
sudo rtla osnoise top -c 2,3 -d 10s 2>&1 | tail -3
echo "timerlat:"
sudo rtla timerlat top -c 2,3 -P 95 -d 10s 2>&1 | tail -3
Hyper-Threading과 RT: Hyper-Threading(SMT)은 두 개의 논리 코어가 물리적 실행 자원을 공유합니다. RT 워크로드가 동작하는 코어의 형제 코어(sibling)에서 다른 작업이 실행되면 캐시와 파이프라인 경합으로 레이턴시가 증가합니다. 극저지연 환경에서는 nosmt 부트 파라미터로 HT를 비활성화하거나, isolcpus에 형제 코어도 함께 포함시켜야 합니다. lscpu -elstopo로 형제 코어 관계를 확인하세요.

eBPF 기반 고급 분석

osnoise/timerlat의 tracepoint는 eBPF 프로그램에서 접근 가능합니다. bpftrace나 libbpf를 사용하면 커스텀 필터링, 실시간 통계, 조건부 알림 등 RTLA 내장 기능을 넘어선 분석이 가능합니다.

bpftrace 실전 스크립트

# 1. IRQ 간섭 히스토그램 (μs 단위)
sudo bpftrace -e '
tracepoint:osnoise:irq_noise {
    @irq_us = hist(args->duration / 1000);
    @by_irq[args->desc] = count();
}

END {
    printf("\n=== IRQ 간섭 히스토그램 (μs) ===\n");
    print(@irq_us);
    printf("\n=== IRQ별 간섭 횟수 ===\n");
    print(@by_irq);
}
'

# 2. softirq 간섭을 유형별로 분류
sudo bpftrace -e '
tracepoint:osnoise:softirq_noise {
    @softirq_duration[args->vector] = hist(args->duration / 1000);
}

interval:s:30 {
    printf("\n=== 30초 softirq 간섭 분포 ===\n");
    print(@softirq_duration);
    clear(@softirq_duration);
}
'

# 3. 스레드 간섭 추적 (어떤 스레드가 격리 CPU를 침범하는지)
sudo bpftrace -e '
tracepoint:osnoise:thread_noise /cpu == 2 || cpu == 3/ {
    printf("CPU %d: %s (pid %d) duration %d ns\n",
        cpu, args->comm, args->pid, args->duration);
    @invaders[args->comm] = sum(args->duration);
}

END {
    printf("\n=== 격리 CPU 침입자 (총 간섭 시간 ns) ===\n");
    print(@invaders);
}
'

# 4. timerlat 지연 실시간 모니터링 (threshold 초과 시 경고)
sudo bpftrace -e '
tracepoint:timerlat:timerlat_sample /args->context == 1 && args->timer_latency > 20000/ {
    printf("ALERT: CPU %d Thread latency %d ns (seq %d)\n",
        cpu, args->timer_latency, args->seqnum);
}
'

# 5. osnoise 간섭과 timerlat 지연의 상관관계 분석
sudo bpftrace -e '
tracepoint:osnoise:irq_noise {
    @irq_ns[cpu] += args->duration;
}

tracepoint:osnoise:softirq_noise {
    @softirq_ns[cpu] += args->duration;
}

tracepoint:timerlat:timerlat_sample /args->context == 1/ {
    printf("CPU %d: thread_lat=%d ns, irq_noise=%lld ns, softirq_noise=%lld ns\n",
        cpu, args->timer_latency,
        @irq_ns[cpu], @softirq_ns[cpu]);
    @irq_ns[cpu] = 0;
    @softirq_ns[cpu] = 0;
}
'

지연 경보 시스템

#!/bin/bash
# rtla-alert.sh — 레이턴시 threshold 초과 시 경보 + 자동 trace 수집

CPUS="2,3"
THRESHOLD=50   # μs
ALERT_EMAIL="admin@example.com"
TRACE_DIR="/var/log/rtla-alerts"

mkdir -p "$TRACE_DIR"

# timerlat trace 모드로 실행 (threshold 초과 시 자동 수집)
sudo rtla timerlat trace -c $CPUS -P 95 -T $THRESHOLD \
    -o "$TRACE_DIR/trace-$(date +%Y%m%d_%H%M%S).txt" \
    --stop-tracing

# trace가 생성되었으면 경보
LATEST=$(ls -t "$TRACE_DIR"/trace-*.txt 2>/dev/null | head -1)
if [ -n "$LATEST" ] && [ -s "$LATEST" ]; then
    echo "경보: ${THRESHOLD}μs 초과 지연 감지!"
    echo "Trace 파일: $LATEST"

    # auto_analysis 결과 추출 (6.3+)
    grep -A 20 "hit stop tracing" "$LATEST"

    # 이메일 알림 (선택)
    # mail -s "RTLA Alert: latency ${THRESHOLD}μs exceeded" \
    #   "$ALERT_EMAIL" < "$LATEST"
fi
eBPF vs rtla trace: rtla timerlat trace는 threshold 초과 시 커널 trace 버퍼를 덤프하는 방식이고, eBPF는 tracepoint에 직접 프로그램을 부착하는 방식입니다. 단순 threshold 기반 추적은 rtla trace가 편리하고, 복합 조건 필터링이나 실시간 통계 집계는 eBPF가 강력합니다. 두 방식을 상황에 맞게 선택하면 됩니다. eBPF 기반 분석의 자세한 내용은 ftrace / Tracepoints 문서의 BPF 연동 섹션을 참고하세요.

NUMA 및 멀티코어 환경 분석

대규모 서버에서 RTLA를 사용할 때는 NUMA 토폴로지와 CPU 마이크로아키텍처 특성을 고려해야 합니다. 같은 시스템이라도 CPU 위치에 따라 레이턴시 특성이 크게 다를 수 있습니다.

NUMA 시스템의 레이턴시 영향 NUMA 노드 0 (Socket 0) CPU 0-1 HK CPU 2-3 격리 CPU 4-5 CPU 6-7 로컬 메모리 접근 지연: ~80ns | LLC 공유 NUMA 노드 1 (Socket 1) CPU 8-9 CPU 10-11 CPU 12-13 CPU 14-15 로컬 메모리 접근 지연: ~80ns | LLC 공유 QPI/UPI +40~80ns NUMA 관련 레이턴시 영향 요소 1. 원격 메모리 접근: 로컬 80ns vs 원격 120-160ns → 캐시 미스 시 40-80ns 추가 지연 2. 캐시 일관성 트래픽: 소켓 간 캐시 라인 전송 → 스핀로 대기 시간 증가 3. IPI(Inter-Processor Interrupt): 소켓 간 IPI가 소켓 내 IPI보다 느림 4. 권장: RT 워크로드, 격리 CPU, 메모리를 모두 같은 NUMA 노드에 배치 (numactl --membind + taskset)
# === NUMA 환경에서의 RTLA 분석 ===

# 1. NUMA 토폴로지 확인
numactl -H
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 3 4 5 6 7
# node 1 cpus: 8 9 10 11 12 13 14 15
# node distances:
# node   0   1
#   0:  10  21
#   1:  21  10

# 2. CPU 토폴로지 (형제 코어 확인)
lscpu -e
# CPU NODE SOCKET CORE L1d:L1i:L2:L3
# 0   0    0      0    0:0:0:0
# 1   0    0      1    1:1:1:0  ← 같은 L3 공유

# 3. NUMA 노드별 timerlat 비교
# 노드 0 격리 CPU
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50 > /tmp/node0.txt

# 노드 1 격리 CPU (비교용)
sudo rtla timerlat hist -c 10,11 -P 95 -d 60s -b 1 -e 50 > /tmp/node1.txt

# 4. 메모리 바인딩과 함께 RT 앱 실행
sudo numactl --membind=0 --cpunodebind=0 taskset -c 2,3 ./my-rt-app

# 5. 형제 코어 확인 (HT 환경)
# CPU 2의 형제 코어 번호
cat /sys/devices/system/cpu/cpu2/topology/thread_siblings_list
# 2,10 → CPU 2와 CPU 10이 형제
# RT 워크로드가 CPU 2에서 돌면 CPU 10도 격리해야 함
시나리오timerlat 영향권장 대응
같은 NUMA 노드, 같은 LLC최소 지연 (최적)이 배치를 목표로 설계
같은 NUMA 노드, 다른 LLC약간의 추가 지연 가능lstopo로 LLC 그룹 확인
다른 NUMA 노드메모리 접근 40-80ns 추가numactl --membind로 메모리 바인딩
HT 형제 코어 미격리캐시/파이프라인 경합형제 코어 함께 격리 또는 nosmt
소켓 간 IRQ 전달IPI 지연 증가IRQ와 워크로드를 같은 소켓에 배치
Intel PCM과 교차 분석: Intel PCM으로 소켓 간 QPI/UPI 대역폭과 트래픽을 모니터링하면서 rtla timerlat을 실행하면, NUMA 원격 접근이 레이턴시에 미치는 영향을 정량적으로 파악할 수 있습니다. QPI/UPI 트래픽이 높은 시점과 timerlat 스파이크가 일치하면 NUMA 배치 문제를 확인할 수 있습니다.

LLC(Last Level Cache) 분할과 레이턴시

Intel의 CAT(Cache Allocation Technology)와 AMD의 L3 캐시 분할 기능을 사용하면 RT 워크로드 전용 LLC 파티션을 할당하여 캐시 오염을 방지할 수 있습니다. 이 기능은 resctrl 파일시스템을 통해 제어합니다.

# === LLC 분할 (Intel CAT / AMD L3 QoS) ===

# 1. resctrl 지원 확인
mount | grep resctrl || mount -t resctrl resctrl /sys/fs/resctrl

# 2. LLC 정보 확인
cat /sys/fs/resctrl/info/L3/cbm_mask
# 7ff → 11비트 (11-way LLC)

cat /sys/fs/resctrl/info/L3/num_closids
# 16 → 최대 16개 CLOSID

# 3. RT 워크로드 전용 LLC 그룹 생성
mkdir /sys/fs/resctrl/rt-workload
# LLC의 절반(5비트)을 RT 전용으로 할당
echo "L3:0=7c0;1=7c0" > /sys/fs/resctrl/rt-workload/schemata
# 나머지(6비트)는 기본 그룹에 남김
echo "L3:0=03f;1=03f" > /sys/fs/resctrl/schemata

# 4. RT 프로세스를 해당 그룹에 배치
echo $RT_PID > /sys/fs/resctrl/rt-workload/tasks

# 5. LLC 분할 후 timerlat으로 효과 검증
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 50
# 기대: 캐시 오염 감소로 스레드 지연의 꼬리 분포가 줄어듦
LLC 분할 효과 측정: LLC 분할 전후의 timerlat hist를 비교하면 캐시 오염의 영향을 정량화할 수 있습니다. 특히 p99/p999 값의 변화를 주목하세요. 평균 지연은 크게 변하지 않지만, 꼬리 지연이 크게 줄어드는 경우가 많습니다. perf stat -e LLC-load-misses로 LLC 미스율 변화를 함께 확인하면 더 정확한 분석이 가능합니다.

실시간 애플리케이션 설계 패턴

RTLA로 커널 수준 레이턴시를 최적화한 후에도 애플리케이션 설계가 부적절하면 종단 지연이 높아질 수 있습니다. 커널 레이턴시를 1μs로 줄여도 애플리케이션에서 동적 메모리 할당이나 페이지 폴트가 발생하면 수십~수백 μs의 지연이 추가됩니다.

RT 애플리케이션 초기화 패턴

/* RT 애플리케이션 초기화 — 필수 설정 */

#include <sys/mman.h>
#include <sched.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>

/* 1. 메모리 잠금 — 모든 페이지를 물리 메모리에 고정 */
void rt_init_memory(void)
{
    /* 현재 + 미래 할당 페이지 모두 잠금 */
    if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
        perror("mlockall failed");
        exit(1);
    }

    /* 스택 사전 할당: 페이지 폴트 방지 */
    unsigned char stack_prefault[8192 * 1024];  /* 8MB */
    memset(stack_prefault, 0, sizeof(stack_prefault));

    /* 힙 사전 할당: malloc 풀 워밍업 */
    void *prealloc = malloc(64 * 1024 * 1024);  /* 64MB */
    memset(prealloc, 0, 64 * 1024 * 1024);
    free(prealloc);
}

/* 2. RT 스케줄링 설정 */
void rt_init_scheduling(int cpu, int priority)
{
    /* CPU affinity 설정 */
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(cpu, &cpuset);
    if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
        perror("sched_setaffinity failed");
        exit(1);
    }

    /* SCHED_FIFO 우선순위 설정 */
    struct sched_param param = { .sched_priority = priority };
    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
        perror("sched_setscheduler failed");
        exit(1);
    }
}

/* 3. RT 메인 루프 — 주기적 실행 */
void rt_main_loop(void)
{
    struct timespec next, now;
    const long PERIOD_NS = 1000000;  /* 1ms 주기 */

    clock_gettime(CLOCK_MONOTONIC, &next);

    while (1) {
        /* 다음 실행 시각까지 대기 */
        next.tv_nsec += PERIOD_NS;
        if (next.tv_nsec >= 1000000000) {
            next.tv_nsec -= 1000000000;
            next.tv_sec++;
        }
        clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);

        /* RT 작업 수행 */
        clock_gettime(CLOCK_MONOTONIC, &now);
        long latency_ns = (now.tv_sec - next.tv_sec) * 1000000000
                        + (now.tv_nsec - next.tv_nsec);

        /* latency_ns가 음수이면 정상 (일찍 깨어남) */
        /* latency_ns가 양수이면 지연 발생 */
        do_rt_work();

        /* 주의: RT 경로에서 금지 사항 */
        /* - malloc/free 금지 (사전 할당 사용) */
        /* - printf 금지 (trace_printk 또는 링 버퍼 사용) */
        /* - 파일 I/O 금지 (별도 스레드에서 처리) */
        /* - mutex 최소화 (lock-free 알고리즘 선호) */
    }
}

RT 경로에서 피해야 할 패턴

금지 패턴지연 영향대안timerlat에서 보이는 증상
malloc()/free()10~100μs (brk/mmap)사전 할당, 메모리 풀간헐적 스레드 지연 스파이크
printf()/write()100μs~수 ms링 버퍼, trace_printk()주기적 큰 스레드 지연
페이지 폴트10~1000μsmlockall(), 사전 접근초기 몇 주기만 높은 지연
mutex_lock() 경합변동 (경합 정도)lock-free 큐, RCU불규칙 스레드 지연
시스템 콜 과다1~10μs/회배칭, io_uring지속적으로 높은 기본 지연
동적 연결 해석첫 호출 시 수 ms정적 링크, -Wl,-z,now초기 1회 큰 스파이크

timerlat으로 애플리케이션 지연 검증

# RT 앱 실행 중 timerlat으로 커널 수준 지연 확인

# 1. RT 앱을 CPU 2에서 FIFO:90으로 실행
sudo taskset -c 2 chrt -f 90 ./my-rt-app &
RT_PID=$!

# 2. timerlat을 같은 CPU에서 같은 우선순위로 실행
# → RT 앱과 같은 조건에서의 커널 지연 확인
sudo rtla timerlat hist -c 2 -P 90 -d 60s -b 1 -e 50

# 3. user-space tracing으로 종단 지연 확인 (6.3+)
sudo rtla timerlat top -c 2 -P 90 --user-threads -d 60s

# 4. RT 앱 자체의 지연 로그와 timerlat 결과를 비교
# timerlat Thread max ≈ RT 앱의 wakeup latency max 이면 정상
# RT 앱의 지연 > timerlat Thread max 이면 앱 내부 문제
# (malloc, I/O, 잠금 경합 등)

# 5. 종료
kill $RT_PID
timerlat 측정 스레드와 RT 앱의 경합: timerlat 스레드와 RT 앱이 같은 CPU에서 같은 우선순위로 실행되면 서로 경합하여 양쪽 모두 지연이 증가합니다. 정확한 측정을 위해서는 (1) timerlat을 RT 앱보다 약간 낮은 우선순위로 실행하거나, (2) RT 앱이 없는 상태에서 timerlat을 먼저 실행하고, (3) RT 앱 실행 중에는 다른 격리 CPU에서 timerlat을 실행하는 방식을 선택합니다.

RT 앱 디버깅을 위한 ftrace 연동

# RT 앱에서 지연이 발생하는 구간을 ftrace로 추적

# 1. trace_marker를 사용한 앱 내부 구간 측정
# 앱 코드에서:
# int fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY);
# write(fd, "RT_LOOP_START\n", 14);
# ... RT 작업 ...
# write(fd, "RT_LOOP_END\n", 12);

# 2. timerlat + trace_marker 동시 기록
echo timerlat > /sys/kernel/tracing/current_tracer
echo 1 > /sys/kernel/tracing/events/ftrace/print/enable
echo 1 > /sys/kernel/tracing/tracing_on

# RT 앱 실행
sudo taskset -c 2 chrt -f 90 ./my-rt-app

# trace 확인: timerlat 이벤트와 앱의 마커가 함께 보임
cat /sys/kernel/tracing/trace | grep -E "timerlat|RT_LOOP"
# → 커널 지연과 앱 내부 처리 시간을 한 타임라인에서 비교 가능
종합 분석 흐름: RT 시스템의 종단 지연을 분석할 때는 다음 순서를 따릅니다. (1) hwlat으로 하드웨어 잡음 배제, (2) osnoise로 CPU 격리 확인, (3) timerlat으로 커널 지연 분리, (4) cyclictest로 커널-사용자 전환 포함 지연 확인, (5) 앱 내부 trace_marker로 앱 수준 지연 식별. 이 5단계를 완료하면 "전체 경로에서 어떤 계층이 얼마나 기여하는지"를 정확히 파악할 수 있습니다. 각 단계의 자세한 기법은 ftrace / Tracepoints, perf 서브시스템, 프로세스 스케줄러 문서를 참고하세요.

전력 관리와 주파수 전환의 레이턴시 영향

CPU 전력 관리 메커니즘은 레이턴시의 숨겨진 주범입니다. C-state 전환, CPUfreq governor, Turbo Boost 등은 에너지 효율을 위해 설계되었지만, 실시간 워크로드에서는 예측 불가능한 지연을 유발합니다. timerlat 히스토그램에서 바이모달 분포가 보이면 전력 관리가 원인일 가능성이 높습니다.

전력 관리 기능복귀 지연timerlat 증상비활성화 방법전력 비용
C1 (HALT)1~3μs보통 문제 없음idle=poll (극단적)높음
C1E (Enhanced HALT)10~20μs약간의 꼬리 지연processor.max_cstate=1중간
C3 (Sleep)30~100μs바이모달 분포processor.max_cstate=1중간
C6 (Deep Sleep)100~500μs긴 꼬리, 큰 스파이크intel_idle.max_cstate=0중간
P-state 전환5~50μs주파수 의존 지연 변동cpufreq governor=performance중간
Turbo Boost 해제10~100μs열 스로틀링 후 지연 증가BIOS에서 고정낮음
Package C-state100~1000μs전 CPU 동시 스파이크intel_idle.max_cstate=0중간
# === 전력 관리 상태 진단 및 튜닝 ===

# 1. 현재 C-state 설정 확인
cat /sys/module/intel_idle/parameters/max_cstate 2>/dev/null
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/name
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/latency  # μs 단위

# 2. CPUfreq governor 확인 및 설정
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# powersave → 주파수가 최저로 유지됨 (느림)
# schedutil → 부하에 따라 동적 변경 (전환 지연)
# performance → 최고 주파수 고정 (빠름, 전력 높음)

# 격리 CPU의 governor를 performance로 고정
for cpu in 2 3; do
    echo performance > /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_governor
done

# 3. C-state 비활성화 (런타임)
# 모든 깊은 C-state 비활성화
for cpu in /sys/devices/system/cpu/cpu*/cpuidle/state[1-9]*; do
    echo 1 > $cpu/disable 2>/dev/null
done

# 4. Turbo Boost 상태 확인
cat /sys/devices/system/cpu/intel_pstate/no_turbo
# 0 → Turbo Boost 활성 (주파수 변동 가능)
# 1 → Turbo Boost 비활성 (주파수 고정)

# RT 환경에서 Turbo Boost 비활성화 (주파수 예측성 확보)
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo

# 5. 전력 관리 변경 전후 timerlat 비교
# 변경 전
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 100 > /tmp/before-power.txt

# C-state + governor 변경 후
sudo rtla timerlat hist -c 2,3 -P 95 -d 60s -b 1 -e 100 > /tmp/after-power.txt

# 기대: 바이모달 분포 → 단봉 분포로 변화
# 기대: 꼬리 지연 감소
diff /tmp/before-power.txt /tmp/after-power.txt
idle=poll의 함정: idle=poll 부트 파라미터는 CPU가 유휴 상태에서도 폴링 루프를 돌게 만들어 C-state 복귀 지연을 완전히 제거합니다. 그러나 유휴 CPU도 100% 전력을 소모하므로, 멀티소켓 서버에서는 전력 비용이 크게 증가합니다. 대안으로 processor.max_cstate=1을 사용하면 C1까지만 허용하여 전력과 지연의 균형을 맞출 수 있습니다. 극저지연 환경(HFT)에서만 idle=poll을 고려하세요.
Turbo Boost와 레이턴시: Turbo Boost는 열 조건에 따라 동적으로 주파수를 올리지만, 열 한계에 도달하면 주파수가 급격히 떨어집니다. 이 전환 과정에서 레이턴시 스파이크가 발생할 수 있습니다. RT 환경에서는 Turbo Boost를 비활성화하고 고정 주파수로 운영하는 것이 예측 가능성 면에서 유리합니다. perf stat -e power/energy-pkg/Intel PCM으로 전력과 주파수 변동을 모니터링하면서 timerlat을 실행하면 전력 관리의 레이턴시 영향을 정량화할 수 있습니다.

참고자료

다음 학습: