Linux v6.18.37 · 개념과 코드 읽기

NOHZ: 쉬는 CPU에서 불필요한 주기 tick 줄이기

이 코드는 어떤 문제를 푸나요?

할 일이 없는 CPU를 일정 간격으로 계속 깨우면 전력을 쓰고 깊은 유휴 상태에 머물기 어렵습니다. NOHZ는 필요한 시각에 깨우는 방식으로 불필요한 tick을 줄입니다. tick_nohz_idle_enter는 그중 idle 진입 상태를 기록하는 준비 함수이며 이 짧은 함수에 모든 tick 중지 판단이 들어 있지는 않습니다.

읽을 범위: v6.18.37 · kernel/time/tick-sched.c · tick_nohz_idle_enter 1250–1266행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

주기 tick과 다음 이벤트

매번 일정 간격으로 깨우는 방식과 가장 가까운 실제 기한에 맞춰 깨우는 방식을 구분합니다. tick을 줄여도 타이머나 장치 인터럽트가 모두 사라지는 것은 아닙니다.

CPU별 tick_sched

각 CPU의 idle·tick 상태를 담은 구조체입니다. 한 CPU가 idle에 들어갔다고 다른 CPU의 시간 관리도 똑같이 멈추지 않습니다.

로컬 인터럽트 차단

현재 CPU의 인터럽트를 막아 상태 전환 중 같은 CPU의 인터럽트 처리와 엇갈리지 않도록 합니다. 전 시스템의 인터럽트를 모두 끄는 명령은 아닙니다.

처음 읽을 때

알람이 100 ms 뒤에 하나만 있는데 매 1 ms마다 확인하러 깨는 상황을 떠올려 보세요. NOHZ는 필요한 기한과 다른 시스템 요구를 고려해 중간 깨움을 줄입니다. 숫자는 원리를 보여 주는 예시입니다.

더 깊이 살펴볼 때

idle_enter, 실제 tick 중지 판단과 idle_exit을 분리해서 읽으세요. RCU, timer, balancing 등 다음 이벤트의 요구가 tick 중지 가능 여부에 영향을 줍니다.

그림으로 보는 변화

NOHZ: 쉬는 CPU에서 불필요한 주기 tick 줄이기의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. idle 진입 조건 확인

진입 시 로컬 IRQ가 허용되어야 함

화살표는 이 함수의 호출 순서입니다. IRQ를 처음부터 꺼 놓고 호출하는 사용법과 다릅니다.

2. 상태 변경 보호

IRQ 차단 후 this_cpu tick_sched 선택

현재 CPU의 tick 상태를 일관되게 바꿉니다.

3. idle 상태 기록

INIDLE 설정, 유휴 계상 시작, IRQ 재허용

이 화살표는 상태 변경을 뜻합니다. 실제 다음 tick을 무조건 취소한다는 뜻은 아닙니다.

tick_nohz_idle_enter를 한 줄씩 읽기

줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.

void tick_nohz_idle_enter(void)
{
	struct tick_sched *ts;

	lockdep_assert_irqs_enabled();

	local_irq_disable();

	ts = this_cpu_ptr(&tick_cpu_sched);

	WARN_ON_ONCE(ts->timer_expires_base);

	tick_sched_flag_set(ts, TS_FLAG_INIDLE);
	tick_nohz_start_idle(ts);

	local_irq_enable();
}
void tick_nohz_idle_enter(void)

현재 CPU가 idle 경로에 들어갈 때 호출하는 NOHZ 준비 함수입니다.

	struct tick_sched *ts;

현재 CPU의 tick_sched를 가리킬 포인터를 준비합니다.

	lockdep_assert_irqs_enabled();

진입 시 로컬 인터럽트가 허용되어야 한다는 조건을 검증합니다. 뒤의 무조건 재허용과 연결된 사용 조건입니다.

	local_irq_disable();

현재 CPU의 인터럽트를 잠시 차단해 tick 상태를 일관되게 변경합니다.

	ts = this_cpu_ptr(&tick_cpu_sched);

per-CPU 변수에서 지금 CPU에 해당하는 tick_sched 주소를 얻습니다. 모든 CPU가 하나의 ts를 공유하지 않습니다.

	WARN_ON_ONCE(ts->timer_expires_base);

timer_expires_base가 아직 설정된 비정상적인 idle 진입 상태인지 한 번 경고합니다. 정상적인 전환 순서 확인입니다.

	tick_sched_flag_set(ts, TS_FLAG_INIDLE);

현재 CPU가 idle 경로 안에 있음을 flag로 표시합니다.

	tick_nohz_start_idle(ts);

idle 시작 시점과 관련 계상을 준비합니다. 이 호출을 모든 타이머를 끄는 명령으로 읽지 않습니다.

	local_irq_enable();

함수 진입 조건에 맞춰 로컬 인터럽트를 다시 허용합니다. 다른 CPU의 IRQ 상태를 변경하지 않습니다.

함께 생각해 볼 질문

NOHZ면 CPU가 어떤 인터럽트도 받지 않나요?

아닙니다. 불필요한 주기 tick을 줄이는 것이며 필요한 타이머 만료나 장치 이벤트는 여전히 처리합니다.

이 함수만 호출하면 tick이 반드시 중지되나요?

아닙니다. idle 진입 기록과 계상 준비를 담당하며 실제 중지 가능 여부는 관련 경로의 추가 판단에 달려 있습니다.

왜 irqsave 대신 disable/enable을 쓰나요?

함수 진입 시 IRQ가 허용되어야 한다는 전제를 먼저 검사합니다. 그 전제 안에서 차단과 재허용을 짝지어 사용합니다.

출처와 읽은 범위

Linux stable v6.18.37 · kernel/time/tick-sched.c

해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고

맨 위로 ↑