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

timekeeping: 갱신 중인 시계를 일관되게 읽기

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

커널의 현재 시각은 기준 시각과 그 뒤에 흐른 시간으로 계산할 수 있습니다. 그런데 두 값을 읽는 사이 다른 CPU가 기준을 갱신하면 서로 다른 세대의 값을 섞을 수 있습니다. ktime_get은 sequence counter를 확인해 충돌했을 때 다시 읽습니다.

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

먼저 알아둘 개념

monotonic time

벽시계의 날짜·시각을 그대로 반환하는 개념이 아닙니다. ktime_get은 CLOCK_MONOTONIC 계열의 시간을 읽으며 일반적인 시스템 suspend 구간을 포함하는 boottime과도 구분됩니다.

기준 시각과 경과 시간

마지막으로 정리해 둔 base에 clocksource에서 계산한 추가 나노초를 더합니다. 하드웨어 카운터 숫자를 그대로 날짜로 해석하지 않습니다.

sequence counter

읽기 전후에 갱신 여부를 확인하는 방법입니다. 독자가 writer를 막는 대신 충돌한 읽기를 버리고 다시 시도할 수 있습니다.

처음 읽을 때

기준 시각을 옛 값으로 읽고 경과 시간은 새 기준에 맞춘 값으로 읽었다면 합이 잘못될 수 있습니다. 두 값을 같은 갱신 구간에서 얻는 이유를 숫자 예로 설명해 보세요.

더 깊이 살펴볼 때

seqcount 재시도 조건과 timekeeping_get_ns의 clocksource 변환을 연결해 보세요. 독자의 잠금 대기 감소와 재시도 가능성이라는 교환 관계가 있습니다.

그림으로 보는 변화

timekeeping: 갱신 중인 시계를 일관되게 읽기의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 읽기 시작 번호 기록

read_seqcount_begin

화살표는 읽기 순서입니다. 번호는 시간이 아니라 상태 갱신을 확인하는 값입니다.

2. 기준과 경과 시간 읽기

base + clocksource에서 얻은 nsecs

이 두 값은 같은 갱신 상태에 속해야 합니다.

3. 갱신 충돌 확인 후 합산

retry면 다시 읽고 아니면 base+nsecs 반환

되돌아가는 화살표는 데이터 복사가 아니라 재읽기입니다. 전체 시스템 시계를 되돌리지 않습니다.

ktime_get를 한 줄씩 읽기

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

ktime_t ktime_get(void)
{
	struct timekeeper *tk = &tk_core.timekeeper;
	unsigned int seq;
	ktime_t base;
	u64 nsecs;

	WARN_ON(timekeeping_suspended);

	do {
		seq = read_seqcount_begin(&tk_core.seq);
		base = tk->tkr_mono.base;
		nsecs = timekeeping_get_ns(&tk->tkr_mono);

	} while (read_seqcount_retry(&tk_core.seq, seq));

	return ktime_add_ns(base, nsecs);
}
ktime_t ktime_get(void)

monotonic 시각을 ktime_t 형태로 반환합니다. wall clock 날짜를 문자열로 만드는 함수가 아닙니다.

	struct timekeeper *tk = &tk_core.timekeeper;

공통 timekeeper의 주소를 구합니다. 기준 시각과 clocksource 읽기 정보가 연결된 관리 구조체입니다.

	unsigned int seq;

읽기 시작 시점의 sequence 번호를 저장할 변수입니다.

	ktime_t base;

기준 monotonic 시각을 보관합니다.

	u64 nsecs;

기준 이후 추가된 나노초 수를 보관합니다.

	WARN_ON(timekeeping_suspended);

timekeeping이 suspend된 상태에서 일반 읽기를 시도하는지 경고합니다. 정상 시계 갱신 상태가 아닐 수 있다는 검사입니다.

	do {

읽기 결과가 갱신과 충돌했으면 전체를 반복할 블록을 시작합니다.

		seq = read_seqcount_begin(&tk_core.seq);

쓰기 진행 여부를 고려하여 시작 sequence 값을 읽습니다. 이후 검사에서 이 값과 현재 상태를 비교합니다.

		base = tk->tkr_mono.base;

현재 monotonic 기준 시각을 복사합니다.

		nsecs = timekeeping_get_ns(&tk->tkr_mono);

동일한 읽기 기준의 clocksource 정보로 추가 경과 나노초를 계산합니다.

	} while (read_seqcount_retry(&tk_core.seq, seq));

읽는 동안 writer가 상태를 갱신했다면 방금 읽은 조합을 버리고 do 블록을 다시 실행합니다.

	return ktime_add_ns(base, nsecs);

일관된 base와 nsecs를 더해 현재 monotonic 시각을 반환합니다.

함께 생각해 볼 질문

ktime_get은 현재 날짜를 반환하나요?

CLOCK_MONOTONIC 계열의 시간입니다. 현실의 날짜와 시각이 필요한 realtime 조회와 구분하셔야 합니다.

read_seqcount_begin이 writer를 막나요?

일반적인 독자 잠금처럼 writer를 막는 방식이 아닙니다. 갱신과 겹친 읽기를 검출하여 다시 읽습니다.

변수 하나가 아닌 여러 값을 검사하는 이유는 무엇인가요?

각 값이 개별적으로 읽혀도 서로 다른 갱신 시점의 조합이면 계산 결과가 틀릴 수 있기 때문입니다.

출처와 읽은 범위

Linux stable v6.6 · kernel/time/timekeeping.c

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

맨 위로 ↑