RTLA / timerlat / osnoise 레이턴시 분석
RTLA는 커널 트레이싱 인프라 위에서 동작하는 레이턴시 분석 도구 묶음입니다. 이 문서는 timerlat과 osnoise를 중심으로 IRQ·스레드(Thread) 지터, CPU 간섭, PREEMPT_RT 환경의 지연(Latency) 시간 분석 흐름을 정리하고, cyclictest와의 역할 차이, tracefs/ftrace 연계, 격리(Isolation) CPU 운영 팁까지 실전 기준으로 설명합니다.
핵심 요약
- RTLA — tracefs와 ftrace 위에서 동작하는 레이턴시 분석 프론트엔드입니다.
- timerlat — 타이머(Timer) 만료 시점부터 IRQ 핸들러(Handler)와 측정 스레드가 실제로 실행되기까지의 지연을 분리해 보여줍니다.
- osnoise — 특정 CPU에서 커널이 사용자 작업을 얼마나 자주 끊는지 간섭 시간을 집계합니다.
- cyclictest와 차이 — cyclictest는 결과 측정용 기준점이고, RTLA는 원인 추적용 계측 도구에 가깝습니다.
- 운영 핵심 — 격리 CPU, IRQ affinity, threadirqs, nohz_full, rcu_nocbs 같은 주변 설정을 함께 봐야 의미가 생깁니다.
단계별 이해
- 증상 확인
최대 지연 시간인지, 장기적인 잡음인지 먼저 구분합니다. - 측정 대상 CPU 고정
격리 CPU와 일반 CPU를 분리하고 IRQ 배치를 먼저 확인합니다. - RTLA 실행
timerlat또는osnoise로 어떤 종류의 간섭인지 좁힙니다. - ftrace 교차 분석
문제가 잡힌 순간의 IRQ, softirq, 스레드 전환을 trace로 확인합니다. - 정책 수정 후 재측정
IRQ affinity, 커널 스레드(Kernel Thread) 배치, CPU 격리 정책을 바꾼 뒤 같은 조건에서 다시 측정합니다.
개요
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 osnoise | CPU를 방해한 커널 간섭 시간 | 격리 CPU 오염, background noise, 관리 스레드 간섭 | 간섭 원인 식별을 위해 추가 trace가 필요함 |
perf sched | 스케줄링 이벤트 전반 | 런큐(Runqueue) 대기, 문맥 전환(Context Switch) 패턴 | 지연 상한 추적보다 일반 분석에 적합 |
레이턴시를 읽는 기준 모델
레이턴시 숫자만 보면 원인 분리가 안 됩니다. 실제로는 타이머 인터럽트가 늦게 도착했는지, 인터럽트는 빨랐지만 측정 스레드가 런큐에서 밀렸는지, 또는 격리 CPU에 원하지 않는 커널 작업이 끼어들었는지를 따로 읽어야 합니다.
실전에서는 보통 다음 순서로 읽습니다. 먼저 timerlat으로 지연이 IRQ 단계인지 스레드 단계인지 나누고, 그 다음 osnoise로 해당 CPU가 전체적으로 얼마나 자주 간섭받는지 봅니다. 마지막으로 ftrace 이벤트나 trace-cmd로 문제 순간의 호출 경로를 복원합니다.
이 3단계 접근법의 핵심은 측정 계층을 분리하는 것입니다. 같은 "50μs 지연"이라도 IRQ 단계에서 40μs가 소모되었다면 인터럽트 관련 설정을 바꿔야 하고, 스레드 단계에서 45μs가 소모되었다면 스케줄링 정책을 재설계해야 합니다. 이 구분 없이 "지연이 크다"는 사실만 가지고 튜닝하면 잘못된 레이어를 건드리게 되어 시간을 낭비하거나 오히려 상황을 악화시킬 수 있습니다.
/sys/kernel/tracing/(또는 레거시 경로 /sys/kernel/debug/tracing/)의 tracefs 인터페이스를 통해 동작합니다. rtla 명령은 이 인터페이스의 편의 프론트엔드입니다. tracefs가 마운트(Mount)되어 있지 않으면 mount -t tracefs tracefs /sys/kernel/tracing으로 수동 마운트할 수 있습니다.
RTLA 도구 구성과 출력 해석
RTLA는 커널 tracing 기반의 여러 모드를 제공합니다. 이 문서에서는 실무에서 가장 많이 쓰이는 timerlat과 osnoise에 집중합니다.
# 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 영향
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
일반적인 해석 흐름은 다음과 같습니다.
- IRQ 지연이 크다
하드 IRQ가 늦게 실행되었다는 뜻입니다. 인터럽트 비활성 구간, 긴 다른 IRQ 처리, 펌웨어성 잡음, 특정 드라이버의 장시간 IRQ 핸들러를 의심합니다. - 스레드 지연이 크다
IRQ는 빨랐지만 측정 스레드가 제때 스케줄되지 못했다는 뜻입니다. 런큐 혼잡, 우선순위(Priority), RT 스로틀링, softirq 후속 처리, 원치 않는 커널 스레드 간섭을 확인합니다. - 둘 다 크다
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_entry와 exit 사이 시간이 큼 | 해당 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에서 자주 문제로 드러나는 항목은 다음과 같습니다.
- housekeeping 누락 — timer tick, RCU 콜백, workqueue가 격리 CPU로 흘러들어옵니다.
- IRQ affinity 미정리 — NIC, 스토리지, 타이머 IRQ가 실시간 CPU에 남아 있습니다.
- softirq 후속 처리 — 하드 IRQ는 짧아도 네트워크/블록 softirq가 뒤에서 CPU를 잡아먹습니다.
- 주파수·전력 정책 — CPUfreq governor, C-state 진입/복귀 비용 때문에 tail latency가 흔들립니다.
이 도구는 "왜 느렸는지"를 바로 한 줄로 답하지는 않지만, "그 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는 "이 CPU가 전반적으로 얼마나 시끄러운가"를 보여주고, timerlat은 "특정 순간에 어떤 종류의 지연이 발생했는가"를 보여줍니다. 둘을 함께 사용하면 가장 효과적입니다. osnoise가 높으면 해당 CPU의 격리 설정을 먼저 점검하고, timerlat으로 구체적인 지연 분리를 합니다. 자세한 tracing 기법은 ftrace / Tracepoints 문서를 참고하세요.
실전 시나리오
| 증상 | 우선 도구 | 주요 원인 후보 | 다음 확인 |
|---|---|---|---|
| 주기 태스크의 최대 지연이 드물게 크게 튐 | rtla timerlat trace | 긴 IRQ, 드라이버 ISR, 펌웨어(Firmware) 간섭 | ftrace로 IRQ 경로 추적 |
| 격리 CPU가 계속 잔잔하게 흔들림 | rtla osnoise hist | housekeeping 태스크, softirq, kworker | IRQ affinity, workqueue 배치 확인 |
| cyclictest 최댓값은 큰데 CPU 사용률은 낮음 | timerlat + osnoise | 스케줄링 지연, 주파수 전환, C-state | perf, Intel PCM 보조 분석 |
| PREEMPT_RT 적용 후에도 지연 개선이 미미함 | osnoise | IRQ 스레드화 미흡, 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 제외).
| 비교 항목 | cyclictest | rtla 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
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
- IRQ 배치 — 실시간 CPU에 디바이스 IRQ가 남아 있지 않은지 확인합니다.
- housekeeping 분리 — 일반 타이머와 관리성 커널 작업이 별도 CPU에서 돌도록 설계합니다.
- 우선순위 점검 — RT 스레드 우선순위가 드라이버 스레드, IRQ thread, 유저 태스크와 충돌하지 않는지 확인합니다.
- 주파수 정책 — tail latency가 중요하면 governor와 깊은 C-state 정책을 함께 검토합니다.
- 측정 오버헤드 — trace 모드는 원인 추적에 강하지만 장시간 상시 측정보다 짧고 집중적으로 쓰는 편이 안전합니다.
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_us | timerlat 타이머 주기 | 1000 |
osnoise/cpus | 측정 대상 CPU 마스크 | 모든 CPU |
osnoise/sample_period | osnoise 샘플 기간 (μs) | 1000000 |
osnoise/sample_runtime | osnoise 샘플 실행 시간 (μs) | 1000000 |
tracing_thresh | trace 기록 threshold (μs) | 0 (모두 기록) |
osnoise/print_stack | threshold 초과 시 스택 출력 | 0 |
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 내부 아키텍처
timerlat은 hrtimer(고해상도 타이머(hrtimer))를 기반으로 동작하는 커널 내장 tracer입니다. 단순히 사용자 공간에서 타이머를 돌리는 cyclictest와 달리, 커널 안에서 직접 타이머 콜백을 설치하고 IRQ 컨텍스트에서 즉시 시간을 기록하기 때문에 인터럽트 지연과 스레드 지연을 정밀하게 분리할 수 있습니다.
timerlat가 활성화되면 각 측정 대상 CPU마다 per-CPU 커널 스레드(timerlat/N)가 생성됩니다. 이 스레드는 FIFO 정책의 높은 우선순위로 실행되며, hrtimer 콜백이 깨울 때까지 대기합니다. 타이머가 만료되면 다음 두 단계가 순서대로 실행됩니다.
- IRQ 컨텍스트 단계 — hrtimer 콜백 함수
timerlat_irq()가 인터럽트 컨텍스트에서 호출됩니다. 이 함수는 "타이머가 만료된 시각"과 "실제 IRQ 핸들러가 실행된 시각"의 차이를 기록하고, 대기 중인timerlat/N스레드를wake_up_process()로 깨웁니다. - 스레드 컨텍스트 단계 — 깨어난
timerlat/N스레드의timerlat_main()함수가 실행됩니다. 이 함수는 "wake_up이 호출된 시각"과 "스레드가 실제로 CPU를 얻은 시각"의 차이를 기록합니다.
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(보통 16바이트)으로 제한됩니다. 따라서 timerlat/ 접두사(9바이트)를 빼면 CPU 번호가 6자리까지 가능합니다. 실제로는 수백 CPU 시스템에서도 문제가 없지만, 커스텀 트레이싱 스크립트에서 이름 파싱 시 이 제한을 알고 있으면 유용합니다.
timerlat 내부 동작 상세
timerlat tracer가 활성화되면 내부적으로 다음 과정이 일어납니다.
- 스레드 생성 — 각 측정 대상 CPU마다
kthread_create()로 커널 스레드를 생성하고,kthread_bind()로 해당 CPU에 고정합니다. - 우선순위 설정 —
sched_setscheduler()로 SCHED_FIFO 정책과 지정된 우선순위를 설정합니다. - hrtimer 등록 —
hrtimer_init()과hrtimer_start()로 주기적 고해상도 타이머를 시작합니다. - 대기 루프 진입 — 스레드는
set_current_state(TASK_INTERRUPTIBLE)로 대기 상태에 진입합니다. - 콜백 실행 — 타이머 만료 시
timerlat_irq()가 IRQ 컨텍스트에서 호출되어 IRQ 지연을 기록하고 스레드를 깨웁니다. - 스레드 실행 — 깨어난 스레드의
timerlat_main()이 스레드 지연을 기록합니다. - 반복 — 다음 주기의 타이머가 이미 설정되어 있으므로, 스레드는 다시 대기 상태로 돌아갑니다.
/* 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μs | 500μs 미만은 오버헤드 주의 |
-P <prio> | 측정 스레드 우선순위 | 95 | 실제 RT 태스크와 동일하게 맞추면 현실적 |
-c <cpus> | 측정 대상 CPU | 모든 CPU | 격리 CPU만 지정하면 잡음이 줄어듦 |
-d <time> | 측정 시간 제한 | 무한 | CI/CD에서는 반드시 설정 |
-t <μs> | threshold (trace 모드) | 0 | 이 값 초과 시만 trace 기록 |
-T <μs> | stop threshold | 0 | 이 값 초과 시 tracing 중단 후 저장 |
osnoise 간섭 소스 분류
osnoise는 "측정 기간 동안 이 CPU에서 커널이 얼마나 개입했는가"를 집계합니다. 타이머 기반으로 특정 순간을 보는 timerlat과 달리, osnoise는 일정 시간 구간 전체를 스캔하면서 누적 간섭량을 보고합니다.
osnoise의 커널 구현은 루프 기반 측정을 사용합니다. 측정 스레드가 CPU를 점유한 채 타이트 루프를 돌면서, 루프 반복 사이의 시간 차이가 예상보다 크면 "무언가가 이 CPU를 가져갔다"고 판단합니다. 가져간 주체를 NMI, IRQ, softirq, thread의 네 가지로 분류하여 각각의 누적 시간과 횟수를 기록합니다.
# 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 열을 보면 어떤 유형의 간섭이 주된 원인인지 알 수 있습니다. 각 유형에 따라 대응 방법이 다릅니다.
| 간섭 유형 | 주요 원인 | 대응 방법 | 확인 명령 |
|---|---|---|---|
| NMI | watchdog, perf PMU, SMI | nmi_watchdog=0, perf 중지 | cat /proc/sys/kernel/nmi_watchdog |
| IRQ | NIC, 타이머, 스토리지, USB | IRQ affinity 이동 | cat /proc/interrupts |
| SOFTIRQ | NET_RX, BLOCK, TIMER, RCU | IRQ 원천 이동, RPS, rcu_nocbs | cat /proc/softirqs |
| THREAD | kworker, ksoftirqd, migration | cgroup, WQ affinity, isolcpus | ps -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
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입니다.
커널 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
| trace 필드 | 의미 | 높을 때 의심 대상 |
|---|---|---|
timer_latency (irq) | 타이머 만료 → IRQ 핸들러 실행까지 | 인터럽트 비활성 구간, NMI, SMI, 다른 IRQ |
timer_latency (thread) | 타이머 만료 → 스레드 실행까지 (총 지연) | 런큐 혼잡, softirq, kthread, RT 스로틀링 |
IRQ handler delay | auto_analysis: 특정 IRQ 핸들러 실행 시간 | NIC, 스토리지, GPU 드라이버 |
Blocking thread | auto_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에서 핵심 단서를 찾는 순서는 다음과 같습니다.
- timerlat_main 행 확인 — 총 지연이 threshold를 초과했는지 봅니다.
- timerlat_irq 행 확인 — IRQ 지연이 큰지 작은지 봅니다. 작으면 스레드 지연이 문제입니다.
- 두 행 사이의 이벤트 확인 — timerlat_irq와 timerlat_main 사이에 어떤 이벤트가 끼어들었는지 봅니다.
- softirq, sched_switch, irq_handler 확인 — CPU를 가져간 주체를 식별합니다.
- 시간 차분 계산 — 각 이벤트의 타임스탬프 차이로 정확한 소모 시간을 계산합니다.
trace 출력의 플래그 열을 읽는 방법도 중요합니다. 각 문자의 의미를 정리합니다.
| 위치 | 문자 | 의미 |
|---|---|---|
| 1번째 | d | 인터럽트 비활성(irqs_disabled) |
| 1번째 | . | 인터럽트 활성 |
| 2번째 | n | need_resched 플래그 설정됨 |
| 2번째 | . | need_resched 없음 |
| 3번째 | h | 하드 IRQ 컨텍스트 |
| 3번째 | s | softirq 컨텍스트 |
| 3번째 | . | 프로세스 컨텍스트 |
| 4번째 | 숫자 | 선점(Preemption) 깊이(preempt_count) |
| 5번째 | 숫자 | 마이그레이션 비활성 깊이 |
예를 들어 d.h1.는 "인터럽트 비활성, resched 없음, 하드 IRQ 컨텍스트, 선점 깊이 1, 마이그레이션 비활성 없음"을 의미합니다. ..s..는 "인터럽트 활성, softirq 컨텍스트, 선점/마이그레이션 비활성 없음"입니다. 이 플래그는 해당 이벤트가 어떤 컨텍스트에서 발생했는지를 빠르게 파악하는 데 핵심적입니다. ftrace / Tracepoints 문서에서 이 플래그의 자세한 의미를 확인할 수 있습니다.
히스토그램 해석법
timerlat hist와 osnoise hist는 지연 분포를 히스토그램으로 보여줍니다. 평균값이나 최대값만으로는 보이지 않는 패턴을 찾는 데 필수적입니다. 특히 꼬리 지연(tail latency)과 바이모달 분포는 시스템 설정 오류나 간헐적 간섭의 강력한 증거입니다.
# 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 |
히스토그램 비교 분석
히스토그램의 가장 큰 가치는 변경 전/후 비교에 있습니다. 커널 업데이트, 설정 변경, 하드웨어 교체 전후의 히스토그램을 비교하면 어떤 변화가 레이턴시에 영향을 미쳤는지 시각적으로 알 수 있습니다.
# 변경 전 히스토그램 저장
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 격리 전략이 훨씬 더 중요해집니다.
PREEMPT_RT에서 레이턴시 튜닝의 핵심 원칙은 다음과 같습니다.
- 격리 CPU에서 불필요한 작업 완전 제거 —
isolcpus,nohz_full,rcu_nocbs를 조합합니다. - IRQ 스레드 우선순위 설계 —
threadirqs모드에서 IRQ 스레드의 기본 우선순위는 50입니다. RT 워크로드보다 높거나 같으면 간섭 원인이 됩니다. - RT 스로틀링 확인 —
/proc/sys/kernel/sched_rt_runtime_us가 -1이 아니면 RT 태스크가 CPU 시간의 일부만 사용할 수 있습니다. - 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에서 스레드 지연 증가로 나타납니다.
| 우선순위 범위 | 용도 | 예시 |
|---|---|---|
| 99 | migration, watchdog | migration/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
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)이 제대로 적용되었는지 재점검합니다.
rtla osnoise top -c 2,3 -d 30s→ 격리 CPU 청결도 확인rtla timerlat hist -c 2,3 -d 60s→ 지연 분포 확인cyclictest -m -S -p 95 -i 1000 -d 60 -c 2-3 -h 100 -q→ 회귀 비교 기준점- 필요시
rtla timerlat trace -T 50 -c 2,3→ 원인 추적
trace-cmd 연동
rtla는 tracefs를 직접 제어하지만, 보다 세밀한 필터링이나 후처리가 필요하면 trace-cmd와 함께 사용할 수 있습니다. trace-cmd는 tracefs의 범용 프론트엔드이고, rtla는 레이턴시 분석에 특화된 프론트엔드입니다.
| 항목 | rtla | trace-cmd |
|---|---|---|
| 주요 목적 | 레이턴시/간섭 전용 분석 | 범용 tracing 프론트엔드 |
| tracer 설정 | 자동 (timerlat/osnoise) | 수동 지정 |
| 출력 형식 | 요약표, 히스토그램 | raw trace, .dat 파일 |
| 시각화 | 텍스트 기반 | KernelShark 연동 |
| 후처리 | 제한적 (auto_analysis) | 스크립트, Python, SQL |
| 커널 요구 | timerlat/osnoise tracer | ftrace만 있으면 동작 |
# 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 관련 이벤트를 분석할 때, 다음 설정이 유용합니다.
- 필터 설정 — "timerlat" 또는 "osnoise" 이벤트만 표시하면 잡음이 줄어들어 패턴이 보입니다.
- CPU 선택 — 격리 CPU만 선택하면 관심 대상에 집중할 수 있습니다.
- 마크 기능 — 지연이 큰 지점에 마크를 찍고, 직전 이벤트를 역추적(Backtrace)합니다.
- 줌 — 지연 발생 시점 전후 100μs~1ms 구간으로 줌인하면 원인 이벤트를 찾기 쉽습니다.
# 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가 효율적이고, "지연이 발생한 순간에 정확히 무슨 일이 있었는지"를 복원하려면 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 사용법
# 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: 주의 (비활성화 시 과열 위험)
| 검출 대상 | hwlat | timerlat | osnoise |
|---|---|---|---|
| SMI (System Management Interrupt) | 직접 검출 | 간접 (IRQ 지연에 포함) | 간접 (noise에 포함) |
| NMI | 검출 가능 | 간접 | 직접 분류 |
| 하드 IRQ 지연 | 검출 불가 | 직접 분리 | 직접 분류 |
| softirq 간섭 | 검출 불가 | 간접 (스레드 지연) | 직접 분류 |
| 스레드 스케줄링 지연 | 검출 불가 | 직접 분리 | 간접 |
| BIOS 열 관리(Thermal Management) | 직접 검출 | 간접 | 간접 |
| 메모리 ECC 스크러빙 | 직접 검출 | 간접 | 간접 |
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 Emulation | 10~50μs | 대부분 가능 | USB Legacy Support |
| Serial Port Redirection | 10~100μs | 가능 | Console Redirection |
| 메모리 ECC 스크러빙 | 50~500μs | 주기 연장 가능 | Patrol Scrub |
| 온도 관리 | 10~100μs | 주의 (과열 위험) | Thermal Management |
| 하드웨어 워치독 | 10~30μs | 주의 (안정성) | HW Watchdog |
| 전력 관리 | 변동 | 일부 가능 | Power Management |
pcie_aspm=off 부트 파라미터로 비활성화하거나, lspci -vv로 개별 디바이스의 ASPM 상태를 확인할 수 있습니다. 자세한 하드웨어 계측 방법은 Intel PCM 문서를 참고하세요.
CPU 격리 전략 종합
레이턴시 튜닝의 핵심은 실시간 워크로드가 동작하는 CPU를 최대한 조용하게 만드는 것입니다. 리눅스에서 CPU를 격리하는 도구와 방법이 여러 가지 있으므로, 각각의 역할과 겹치는 부분을 정리합니다.
| 도구/방법 | 적용 시점 | 격리 수준 | 유연성 | 주요 장점 |
|---|---|---|---|---|
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/interrupts | managed IRQ가 격리 CPU에 남아 있음 | isolcpus에 managed_irq 추가 |
| SOFTIRQ 간섭이 높음 | mpstat -P 2,3 1 | 네트워크/블록 softirq가 격리 CPU에서 실행 | IRQ affinity 먼저 이동 |
| THREAD 간섭이 높음 | ps -eo psr,pid,comm | kworker, migration 등이 격리 CPU에서 실행 | WQ_UNBOUND affinity, cgroup |
| NMI 간섭이 높음 | perf stat -e nmi:nmi_handler | perfmon NMI, watchdog NMI | nmi_watchdog=0, perf 중지 |
| RCU 콜백 발생 | grep rcu /proc/softirqs | rcu_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.1 | RTLA 커널 트리 통합, rtla 유틸리티 공식 포함 | 별도 설치 없이 커널 빌드 시 함께 생성 가능 |
| 6.2 | rtla hwnoise 명령 추가 | hwlat_detector를 rtla 프론트엔드에서 직접 사용 |
| 6.3 | timerlat auto_analysis 기본 활성화 | threshold 초과 시 자동으로 원인 분석 요약 출력 |
| 6.3 | timerlat user-space tracing 지원 | 커널 스레드 대신 사용자 공간 스레드에서 측정 가능 |
| 6.4 | osnoise pid filter 추가 | 특정 PID의 간섭만 추적 가능 |
| 6.5 | aa_only 옵션 (auto_analysis only) | raw trace 없이 분석 요약만 출력 (빠른 확인용) |
| 6.5 | timerlat hist 개선: 사용자 공간 지연 열 추가 | 커널/사용자 지연 분리 히스토그램 |
| 6.6 | osnoise softirq 세분류 | NET_RX, BLOCK, TIMER 등 개별 softirq 식별 |
| 6.7 | hrtimer 정밀도 개선 (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 (커널 버전과 동일)
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.13 | hwlat_detector, ftrace | cyclictest + ftrace + hwlat |
| 5.14~5.19 | timerlat, osnoise tracer | tracefs 직접 사용 또는 trace-cmd |
| 6.0 | timerlat, osnoise, hwlat | tracefs 또는 rtla 수동 빌드 |
| 6.1+ | rtla 명령 포함 | rtla 사용 권장 |
| 6.3+ | auto_analysis, user-space | rtla 최신 기능 활용 |
# 커널 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_TRACER와 CONFIG_TIMERLAT_TRACER를 기본으로 활성화합니다. 그러나 일부 최소 설치 커널이나 임베디드 커널에서는 비활성화되어 있을 수 있습니다. Red Hat/Fedora의 RT 커널은 모든 관련 CONFIG가 활성화되어 있습니다. 커널 심볼 정보(CONFIG_KALLSYMS)가 있으면 trace 출력에 함수 이름이 표시되어 분석이 쉬워집니다. 자세한 내용은 커널 심볼 & 디버그 정보 문서를 참고하세요.
실전 벤치마크 시나리오
레이턴시 요구사항은 도메인마다 크게 다릅니다. 여기서는 대표적인 네 가지 실전 시나리오별로 측정 방법과 기준값을 정리합니다.
# === 네트워크 레이턴시 벤치마크 (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 p99 | timerlat, perf | NIC IRQ 격리, NAPI, busy_poll | GSO/GRO, 네트워킹 |
| 산업 PLC (EtherCAT) | <100μs 절대 | timerlat, cyclictest | PREEMPT_RT, RT 스로틀링 해제 | RT Mutex, 스케줄러 |
| HFT (고빈도 트레이딩) | <5μs p999 | hwlat, osnoise | kernel bypass, C-state=0 | Intel PCM, 최적화 |
| 오디오 (JACK/PipeWire) | <1ms 버퍼 | timerlat hist | IRQ 우선순위, RT 정책 | 스케줄러 |
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
멀티 시나리오 통합 테스트
실전에서는 단일 측정이 아니라 다양한 부하 조건에서 측정해야 합니다. 유휴 상태(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-ng를 반드시 housekeeping CPU에서만 실행하세요(taskset -c 0,1). 격리 CPU에서 실행하면 의도하지 않은 간섭이 발생하여 측정이 무의미해집니다. 또한 실제 워크로드와 유사한 부하를 사용하는 것이 가장 정확합니다.
도구 선택 의사결정
레이턴시 문제를 만났을 때 어떤 도구부터 써야 하는지를 증상 기반으로 정리합니다. 도구를 잘못 선택하면 원인과 무관한 데이터에 시간을 낭비하게 됩니다.
| 증상 | 1차 도구 | 보조 도구 | 핵심 확인 사항 |
|---|---|---|---|
| 모든 CPU에서 간헐적으로 수십 μs 지연 | hwlat | rdmsr 0x34 (SMI) | SMI 횟수 증가 여부, BIOS 설정 |
| 격리 CPU인데도 잡음이 지속 | osnoise | cat /proc/interrupts | 격리 CPU에 남은 IRQ, kthread |
| 특정 순간 최대 지연이 크게 튐 | timerlat trace | ftrace | auto_analysis 결과, IRQ vs thread |
| 지연 분포가 이상함 (바이모달) | timerlat hist | cpupower | C-state, CPUfreq governor |
| 커널 업데이트 후 지연 회귀 | cyclictest | timerlat hist | before/after 비교, 히스토그램 변화 |
| 특정 함수에서 오래 걸리는 것 같음 | ftrace | perf | function_graph, 호출 스택 |
| CPU 주파수/전력 관련 의심 | perf / Intel PCM | osnoise | 주파수 전환 빈도, C-state 잔류 시간 |
hwlat으로 하드웨어 잡음 배제 (1회, 60초)osnoise로 CPU 격리 상태 확인 (격리 변경 시마다)timerlat hist로 지연 분포 확인 (기준 설정)timerlat trace로 이상 지연 원인 추적 (문제 발생 시)cyclictest로 변경 전/후 회귀 비교 (정기적)
레이턴시 버짓 설계
실시간 시스템에서는 "허용 가능한 최대 지연"을 레이턴시 버짓(latency budget)으로 분해하여 각 계층에 할당합니다. rtla의 측정 결과는 이 버짓의 각 항목을 검증하는 데 직접 사용됩니다.
| 계층 | 지연 범위 | 측정 도구 | 예시 (산업 PLC) |
|---|---|---|---|
| 하드웨어 (SMI/NMI) | 0~100μs | hwlat | 목표: <5μs |
| IRQ 지연 | 1~50μs | timerlat (IRQ 열) | 목표: <10μs |
| 스레드 스케줄링 | 1~100μs | timerlat (Thread 열) | 목표: <30μs |
| 사용자 공간 진입 | 1~10μs | cyclictest | 목표: <5μs |
| CPU 간섭 | 0~1000μs | osnoise | 목표: noise <10μs/s |
| 총 지연 | 합산 | 종합 | 목표: <100μs |
각 계층의 측정값이 할당된 버짓을 초과하면 해당 계층을 튜닝합니다. 모든 계층이 버짓 내에 있는데도 총 지연이 초과하면 버짓 재분배가 필요합니다. 이 접근법은 "어디를 튜닝해야 가장 효과가 큰가"를 체계적으로 판단하게 해줍니다.
레이턴시 버짓 설계 원칙
버짓 설계 시 주의할 핵심 원칙을 정리합니다.
- 최악 사례 기반 — 평균이 아닌 p99/p999/max를 기준으로 버짓을 설정합니다. 실시간 시스템에서는 "보통은 괜찮은데 가끔 느리다"가 치명적입니다.
- 마진 확보 — 총 버짓의 70-80%까지만 할당하고 나머지는 예측 불가능한 지연(SMI, 온도 스로틀링 등)의 마진으로 남겨둡니다.
- 독립 검증 — 각 계층을 독립적으로 측정합니다. 모든 계층을 동시에 측정하면 측정 오버헤드가 겹쳐 정확도가 떨어집니다.
- 부하 조건 포함 — 유휴 상태뿐 아니라 최대 부하 상태에서도 버짓을 만족해야 합니다.
- 온도 고려 — 장시간 운영 시 CPU 온도 상승으로 주파수 스로틀링이 발생하면 지연이 증가합니다. 발열 안정화 후 측정해야 합니다.
# 레이턴시 버짓 종합 검증 스크립트
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
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 --membind와 taskset을 함께 사용하세요.
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 노드에 바인딩하세요. lstopo나 numactl -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 프레임워크의 핵심 설계 원칙은 측정 계층의 분리입니다. 각 tracer는 독립적인 측정 영역을 담당하며, 사용자 공간 도구는 이들을 조합하여 종합적인 분석을 제공합니다.
| 계층 | 구성 요소 | 역할 | 소스 위치 |
|---|---|---|---|
| 사용자 공간 프론트엔드 | rtla 바이너리 | tracefs 제어, 출력 포맷팅, auto_analysis | tools/tracing/rtla/ |
| VFS 인터페이스 | tracefs | 커널-사용자 공간 통신 채널 | kernel/trace/trace.c |
| 커널 tracer | timerlat, 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의 간섭 분류 기능이 함께 동작하여, 타이머 지연의 원인을 자동으로 분류할 수 있습니다.
/* 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가 담당합니다. 이 분리 덕분에 측정 오버헤드가 최소화되면서도 분석 기능은 풍부하게 제공할 수 있습니다.
레이턴시 측정 흐름 상세
timerlat의 측정 흐름을 타이머 인터럽트 발생부터 최종 측정 완료까지 나노초 단위로 추적합니다. 각 단계에서 어떤 지연이 발생할 수 있는지, 그리고 그 지연이 timerlat 출력의 어떤 열에 반영되는지를 정확히 이해하면 분석 정확도가 크게 향상됩니다.
각 단계에서 발생할 수 있는 지연의 구체적인 원인과 크기를 정리합니다.
| 구간 | 지연 범위 | 주요 원인 | 진단 방법 |
|---|---|---|---|
| T₀ → T₁ (IRQ 전달) | 0.1~100μs | local_irq_disable 구간, SMI, NMI | hwlat, irqsoff tracer |
| T₁ → T₂ (IRQ 경합) | 0~50μs | 선행 IRQ 처리, IRQ 큐 대기 | trace irq_handler_entry/exit |
| T₂ → T₃ (IRQ 콜백) | <1μs | timerlat_irq() 실행 시간 (최소) | function_graph tracer |
| T₃ → T₄ (스케줄링) | 1~500μs | softirq, 우선순위 역전, 런큐 경합 | sched_switch trace, osnoise |
| T₄ → T₅ (스레드 실행) | <1μs | timerlat_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 주기 스파이크 가능
osnoise 측정 알고리즘 상세
osnoise의 측정 알고리즘은 직관적이면서도 정교합니다. 핵심 아이디어는 "CPU를 완전히 점유한 채 루프를 돌면서, 루프 반복 사이에 예상보다 오래 걸린 시간을 간섭으로 분류"하는 것입니다. 이 방식은 어떤 종류의 간섭이든 빠짐없이 감지할 수 있다는 장점이 있습니다.
/* 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 간섭 소스별 상세 분석
각 간섭 유형의 특성과 발생 시나리오를 상세히 분석합니다.
| 간섭 유형 | 컨텍스트 | 일반적 지속 시간 | 발생 빈도 | 제어 가능성 |
|---|---|---|---|---|
| NMI | Non-Maskable Interrupt | 1~10μs (perf), 10~500μs (SMI) | 낮음 (perf 샘플링 주기) | nmi_watchdog=0, perf 중지 |
| IRQ (하드) | 인터럽트 컨텍스트 | 1~100μs (디바이스 의존) | 높음 (NIC, 스토리지) | IRQ affinity 이동 |
| softirq | softirq 컨텍스트 | 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 등이 보이면 간섭 원인
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단계: 간섭 확인
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);
}
'
간섭 원인별 대응 매트릭스
간섭 유형, 구체적 원인, 진단 명령, 해결 방법을 체계적으로 정리합니다.
| 간섭 유형 | 구체적 원인 | 진단 명령 | 해결 방법 |
|---|---|---|---|
| NMI | perf 샘플링 NMI | ps aux | grep perf | perf 종료 또는 샘플링 주기 감소 |
| nmi_watchdog | cat /proc/sys/kernel/nmi_watchdog | echo 0 > /proc/sys/kernel/nmi_watchdog | |
| SMI (NMI로 분류) | rdmsr -a 0x34 | BIOS 설정 변경 | |
| IRQ | NIC IRQ | cat /proc/interrupts | grep eth | IRQ affinity 이동: echo 3 > /proc/irq/N/smp_affinity |
| 스토리지 IRQ | cat /proc/interrupts | grep ahci | IRQ affinity 이동 | |
| 타이머 IRQ (LOC) | cat /proc/interrupts | grep LOC | nohz_full 설정 | |
| softirq | NET_RX/NET_TX | cat /proc/softirqs | NIC IRQ를 다른 CPU로 이동 (softirq 동반) |
| RCU softirq | grep RCU /proc/softirqs | rcu_nocbs 부트 파라미터 | |
| TIMER softirq | grep TIMER /proc/softirqs | nohz_full 설정 | |
| thread | kworker | ps -eo psr,comm | grep kworker | WQ_UNBOUND affinity 제한 |
| ksoftirqd | ps -eo psr,comm | grep ksoftirqd | IRQ 소스 이동 | |
| rcuop/rcuos | ps -eo psr,comm | grep rcu | rcu_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
rtla timerlat hist의 출력은 텍스트 기반이므로 awk, grep 등 표준 도구로 쉽게 파싱할 수 있습니다.
cyclictest vs timerlat 심층 비교
cyclictest와 timerlat은 모두 주기적 깨우기 지연을 측정하지만, 측정 위치, 측정 방식, 측정 결과의 의미가 근본적으로 다릅니다. 이 차이를 정확히 이해하면 두 도구를 적절히 조합하여 사용할 수 있습니다.
두 도구의 측정값 차이를 실제 수치로 보면 다음과 같습니다.
# 같은 시스템에서 동시 측정 비교
# 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)가
# 순수 스케줄링 지연이므로, 스케줄링 최적화 필요 여부를 판단
| 특성 | cyclictest | timerlat | 실전 의미 |
|---|---|---|---|
| 측정 오버헤드 | 시스템 콜 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 |
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_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_FIFO | 1~99 | 같은 우선순위 내 FIFO, 무한 실행 | RT 워크로드, IRQ 스레드 |
SCHED_RR | 1~99 | 같은 우선순위 내 라운드로빈 | 동일 우선순위 RT 태스크 |
SCHED_OTHER (CFS) | 0 | nice 값 기반 공정 스케줄링 | 일반 프로세스 |
# 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
- 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로 각 단계의 효과를 검증합니다.
단계별 튜닝 체크리스트
#!/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
nosmt 부트 파라미터로 HT를 비활성화하거나, isolcpus에 형제 코어도 함께 포함시켜야 합니다. lscpu -e나 lstopo로 형제 코어 관계를 확인하세요.
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
rtla timerlat trace는 threshold 초과 시 커널 trace 버퍼를 덤프하는 방식이고, eBPF는 tracepoint에 직접 프로그램을 부착하는 방식입니다. 단순 threshold 기반 추적은 rtla trace가 편리하고, 복합 조건 필터링이나 실시간 통계 집계는 eBPF가 강력합니다. 두 방식을 상황에 맞게 선택하면 됩니다. eBPF 기반 분석의 자세한 내용은 ftrace / Tracepoints 문서의 BPF 연동 섹션을 참고하세요.
NUMA 및 멀티코어 환경 분석
대규모 서버에서 RTLA를 사용할 때는 NUMA 토폴로지와 CPU 마이크로아키텍처 특성을 고려해야 합니다. 같은 시스템이라도 CPU 위치에 따라 레이턴시 특성이 크게 다를 수 있습니다.
# === 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와 워크로드를 같은 소켓에 배치 |
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
# 기대: 캐시 오염 감소로 스레드 지연의 꼬리 분포가 줄어듦
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, ¶m) != 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μs | mlockall(), 사전 접근 | 초기 몇 주기만 높은 지연 |
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
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"
# → 커널 지연과 앱 내부 처리 시간을 한 타임라인에서 비교 가능
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-state | 100~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 부트 파라미터는 CPU가 유휴 상태에서도 폴링 루프를 돌게 만들어 C-state 복귀 지연을 완전히 제거합니다. 그러나 유휴 CPU도 100% 전력을 소모하므로, 멀티소켓 서버에서는 전력 비용이 크게 증가합니다. 대안으로 processor.max_cstate=1을 사용하면 C1까지만 허용하여 전력과 지연의 균형을 맞출 수 있습니다. 극저지연 환경(HFT)에서만 idle=poll을 고려하세요.
perf stat -e power/energy-pkg/나 Intel PCM으로 전력과 주파수 변동을 모니터링하면서 timerlat을 실행하면 전력 관리의 레이턴시 영향을 정량화할 수 있습니다.
참고자료
- ftrace / Tracepoints — tracefs, event trace, function_graph, trace-cmd. timerlat/osnoise trace 결과의 심층 분석에 필수
- perf 서브시스템 — PMU 기반 프로파일링, sched 이벤트 분석, CPU 주파수/전력 교차 분석
- Intel PCM — 주파수, 메모리 대역폭(Bandwidth), 전력, 언코어 관점의 하드웨어 계측. HFT/극저지연 환경의 보조 분석
- 커널 심볼 & 디버그 정보 — /proc/kallsyms, vmlinux, DWARF. trace 출력의 함수 이름 해석에 필요
- 성능 최적화 — 계측 기반 튜닝 절차와 회귀 방지 워크플로
- 프로세스 스케줄러 — 런큐 지연, 우선순위, CPU 배치, SCHED_DEADLINE
- EEVDF 스케줄러 — CFS 후속 스케줄러, 가상 데드라인과 공정성(Fairness)
- 인터럽트 — IRQ 처리 경로와 인터럽트 컨텍스트 이해
- Threaded IRQ — request_threaded_irq(), IRQF 플래그, PREEMPT_RT와 스레드화
- RT Mutex — PREEMPT_RT와 우선순위 상속(Priority Inheritance)
- Bottom-Half 메커니즘 — softirq, tasklet, workqueue, 간섭 이해
- 디버깅 & 트러블슈팅 — 증상 기반 진입 허브
Documentation/tools/rtla/— 커널 소스 트리의 RTLA 문서tools/tracing/rtla/— RTLA 사용자 공간 도구 소스kernel/trace/trace_osnoise.c— timerlat/osnoise tracer 커널 구현- 커널 공식 문서 — RTLA — rtla-timerlat, rtla-osnoise 명령어 레퍼런스와 사용법을 제공합니다
- 커널 공식 문서 — timerlat tracer — timerlat tracer의 설계, IRQ/스레드 레이턴시 측정 원리를 설명합니다
- 커널 공식 문서 — osnoise tracer — osnoise tracer의 동작 원리, 간섭 소스 분류 방법을 설명합니다
- LWN.net — Measuring OS noise with osnoise — osnoise tracer의 설계 배경과 구현 세부사항을 다룬 기사입니다
- LWN.net — The timerlat tracer — timerlat tracer가 기존 cyclictest 대비 어떤 장점을 제공하는지 분석합니다
- LWN.net — PREEMPT_RT 메인라인 통합 — PREEMPT_RT 패치셋의 메인라인 병합 과정과 실시간 커널의 의미를 다룹니다
- LWN.net — The real-time Linux kernel — 실시간 리눅스의 역사, 아키텍처, PREEMPT_RT 핵심 개념을 정리합니다
- Daniel Bristot de Oliveira 블로그 — RTLA 개발자의 공식 블로그로, timerlat/osnoise 관련 기술 게시물과 실험 결과를 공유합니다
- Demystifying the Real-Time Linux Latency — Daniel Bristot de Oliveira의 실시간 리눅스 레이턴시 분석 방법론 발표 자료입니다
- Real-Time Linux Wiki — Linux Foundation의 실시간 리눅스 공식 위키로, PREEMPT_RT 빌드·설정·테스트 가이드를 제공합니다
- cyclictest 문서 — cyclictest의 사용법, 파라미터 해설, 히스토그램 분석 방법을 설명합니다
- rt-tests Git 저장소 — cyclictest, hackbench, pip_stress 등 실시간 테스트 도구 모음의 소스 코드입니다
- rt-tests 문서 — rt-tests 패키지에 포함된 각 도구의 역할과 사용법을 정리합니다
- 디버깅 & 트러블슈팅 — 증상 기반 진입 허브
- 크래시 분석 — lockup, panic, stall 후분석
- Intel PCM — 주파수, 메모리, 전력, 언코어 관점 보조 계측
- ftrace / Tracepoints — 심층 tracing, function_graph, 이벤트 필터
- 커널 심볼 & 디버그 정보 — trace 심볼 해석, vmlinux, debuginfo
- perf 서브시스템 — PMU 카운터, 프로파일링, sched 분석