이 코드는 어떤 문제를 푸나요?
lockdep은 관찰한 잠금 획득 순서를 이용해 모순된 의존 관계를 찾는 검사기입니다. 이름만 보고 lockdep_init 안에서 그래프 전체를 새로 할당한다고 생각하기 쉽지만, 이 버전의 함수는 주로 설정 한도와 메모리 사용량을 보고합니다. 실제 의존성 기록·검사는 별도 경로입니다.
읽을 범위: v6.6 · kernel/locking/lockdep.c · lockdep_init 6478–6515행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
잠금 클래스
같은 역할의 잠금을 묶어 획득 순서를 분석하는 단위입니다.
의존성 사이클
A를 가진 채 B를 기다리는 흐름과 B를 가진 채 A를 기다리는 흐름이 함께 가능하면 교착 위험이 생깁니다.
처음 읽을 때
코드를 읽을 때 설정된 클래스·깊이·체인 한도를 출력합니다. 그다음 어느 상태가 바뀌는지 각 줄에서 확인하세요.
더 깊이 살펴볼 때
이 경로의 호출 문맥, 잠금·인터럽트 상태, 오류 시 남는 자원을 함께 추적해 보세요. 호출된 함수가 수행하는 작업과 현재 함수가 직접 보장하는 범위를 구분하는 것이 중요합니다.
그림으로 보는 변화
1단계 고정
1. 검사기 준비 상태 확인
설정된 클래스·깊이·체인 한도를 출력합니다.
그림은 이 초기화 함수의 보고 순서입니다.
2. 자료구조 크기 집계
정적 배열과 조건부 구조의 sizeof를 합칩니다.
sizeof는 새 메모리 할당이 아닙니다.
3. 태스크별 비용 확인
task_struct 한 개의 held_locks 필드 크기를 sizeof로 출력합니다.
이후 실행 중 잠금 획득을 검사하는 경로는 별도입니다.
lockdep_init를 한 줄씩 읽기
줄 번호는 v6.6 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
void __init lockdep_init(void)
{
printk("Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar\n");
printk("... MAX_LOCKDEP_SUBCLASSES: %lu\n", MAX_LOCKDEP_SUBCLASSES);
printk("... MAX_LOCK_DEPTH: %lu\n", MAX_LOCK_DEPTH);
printk("... MAX_LOCKDEP_KEYS: %lu\n", MAX_LOCKDEP_KEYS);
printk("... CLASSHASH_SIZE: %lu\n", CLASSHASH_SIZE);
printk("... MAX_LOCKDEP_ENTRIES: %lu\n", MAX_LOCKDEP_ENTRIES);
printk("... MAX_LOCKDEP_CHAINS: %lu\n", MAX_LOCKDEP_CHAINS);
printk("... CHAINHASH_SIZE: %lu\n", CHAINHASH_SIZE);
printk(" memory used by lock dependency info: %zu kB\n",
(sizeof(lock_classes) +
sizeof(lock_classes_in_use) +
sizeof(classhash_table) +
sizeof(list_entries) +
sizeof(list_entries_in_use) +
sizeof(chainhash_table) +
sizeof(delayed_free)
#ifdef CONFIG_PROVE_LOCKING
+ sizeof(lock_cq)
+ sizeof(lock_chains)
+ sizeof(lock_chains_in_use)
+ sizeof(chain_hlocks)
#endif
) / 1024
);
#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING)
printk(" memory used for stack traces: %zu kB\n",
(sizeof(stack_trace) + sizeof(stack_trace_hash)) / 1024
);
#endif
printk(" per task-struct memory footprint: %zu bytes\n",
sizeof(((struct task_struct *)NULL)->held_locks));
}void __init lockdep_init(void)이 시점의 lockdep 함수는 잠금 의존성 추적의 한도와 정적 저장 공간 크기를 부팅 로그에 보여 줍니다. 실제 교착 가능성 검사는 이후 잠금 획득·해제 경로에서 수행됩니다.
printk("Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar\n");잠금 의존성 검증기가 포함되었음을 알리는 배너를 출력합니다. 아래의 추적 한도와 메모리 사용량 보고가 어느 기능의 정보인지 구분하는 시작입니다.
printk("... MAX_LOCKDEP_SUBCLASSES: %lu\n", MAX_LOCKDEP_SUBCLASSES);같은 잠금 종류 안에서 중첩 관계를 구별할 subclass의 최대 수입니다. 현재 잠겨 있는 객체 개수가 아니라 구분 가능한 계층 수를 출력합니다.
printk("... MAX_LOCK_DEPTH: %lu\n", MAX_LOCK_DEPTH);태스크 하나에서 동시에 보유한 잠금 상태를 기록할 수 있는 최대 깊이입니다. 잠금 함수의 재귀를 이 횟수까지 허용한다는 뜻은 아닙니다.
printk("... MAX_LOCKDEP_KEYS: %lu\n", MAX_LOCKDEP_KEYS);lockdep이 서로 다른 잠금 클래스를 식별하여 보관할 수 있는 최대 수입니다. 같은 클래스의 여러 잠금 객체와 클래스 종류 수를 구분해야 합니다.
printk("... CLASSHASH_SIZE: %lu\n", CLASSHASH_SIZE);잠금 클래스를 빠르게 찾는 해시 테이블의 버킷 수입니다. 분류된 클래스 전체 개수와 다른 저장 구조의 크기를 보고합니다.
printk("... MAX_LOCKDEP_ENTRIES: %lu\n", MAX_LOCKDEP_ENTRIES);먼저 잡은 잠금과 다음에 잡는 잠금 사이의 의존 관계를 기록할 최대 항목 수입니다. 순서 충돌이나 교착 가능성을 찾는 그래프의 용량과 관련됩니다.
printk("... MAX_LOCKDEP_CHAINS: %lu\n", MAX_LOCKDEP_CHAINS);추적할 수 있는 서로 다른 잠금 획득 체인의 최대 수입니다. 한 태스크가 현재 보유한 잠금 개수와는 다른 전체 추적 한도입니다.
printk("... CHAINHASH_SIZE: %lu\n", CHAINHASH_SIZE);이미 본 잠금 획득 체인을 찾기 위한 해시 테이블의 버킷 수입니다. 중복된 체인을 다시 분석하지 않도록 조회하는 기반입니다.
printk(" memory used by lock dependency info: %zu kB\n",lockdep의 정적 클래스·의존 관계·체인 저장소가 차지하는 총량을 출력합니다. 뒤의 sizeof 합계가 이 값이며 지금 동적으로 할당한 객체 수를 세는 출력은 아닙니다.
(sizeof(lock_classes) +자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: (sizeof(lock_classes) +
sizeof(lock_classes_in_use) +자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: sizeof(lock_classes_in_use) +
sizeof(classhash_table) +자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: sizeof(classhash_table) +
sizeof(list_entries) +자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: sizeof(list_entries) +
sizeof(list_entries_in_use) +자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: sizeof(list_entries_in_use) +
sizeof(chainhash_table) +자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: sizeof(chainhash_table) +
sizeof(delayed_free)자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: sizeof(delayed_free)
#ifdef CONFIG_PROVE_LOCKING잠금 획득 순서와 의존 관계를 추적해 교착 가능성을 진단하는 설정입니다. 여기서는 실제 검사 실행이 아니라 이 기능에만 필요한 잠금 체인 자료의 크기를 메모리 사용량 합계에 포함합니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.
+ sizeof(lock_cq)자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: + sizeof(lock_cq)
+ sizeof(lock_chains)자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: + sizeof(lock_chains)
+ sizeof(lock_chains_in_use)자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: + sizeof(lock_chains_in_use)
+ sizeof(chain_hlocks)자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: + sizeof(chain_hlocks)
#endif잠금 의존성 검증을 사용할 때 필요한 체인 자료의 크기 합산을 마칩니다. 전체 바이트 합계를 KiB로 바꾸는 식은 조건 바깥에서 이어집니다.
) / 1024앞에서 합한 바이트 수를 1024로 나누어 KiB 단위로 표시합니다. 출력 문자열의 kB 표기와 관계없이 실제 나눗셈 기준은 1024입니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.
);앞줄들에서 계산한 저장 공간 크기를 로그 함수의 인자로 넘겨 해당 보고를 마칩니다. 이 지점에서 추가 메모리를 할당하지 않습니다.
#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING)IRQ 허용·차단 지점을 추적하는 TRACE_IRQFLAGS와 잠금 의존성을 검사하는 PROVE_LOCKING을 모두 포함한 경우입니다. 잠금 사용 문맥을 진단할 스택 추적 자료가 존재하므로 그 저장 공간 크기를 별도로 출력합니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.
printk(" memory used for stack traces: %zu kB\n",잠금 사용 경로를 진단하기 위해 저장하는 스택 추적 주소 배열과 조회 테이블의 크기를 별도로 출력합니다. 일반 커널 스택 전체 사용량을 뜻하지 않습니다.
(sizeof(stack_trace) + sizeof(stack_trace_hash)) / 1024자료구조의 크기를 바이트 단위로 구해 lockdep의 정적 저장 공간 사용량을 보고하는 식에 더합니다. sizeof 안의 NULL 포인터 표현은 실제로 그 주소를 읽는 동작이 아닙니다. 대상: (sizeof(stack_trace) + sizeof(stack_trace_hash)) / 1024
);앞줄들에서 계산한 저장 공간 크기를 로그 함수의 인자로 넘겨 해당 보고를 마칩니다. 이 지점에서 추가 메모리를 할당하지 않습니다.
#endifIRQ 상태 추적과 잠금 검증을 함께 쓸 때의 스택 추적 메모리 보고를 마칩니다. 뒤에서는 태스크 하나당 잠금 기록 크기를 출력합니다.
printk(" per task-struct memory footprint: %zu bytes\n",task_struct 한 개에 정의된 held_locks 필드의 고정 크기를 출력합니다. 실행 중인 태스크를 하나씩 순회하거나 현재 잠금 개수를 세지 않습니다.
sizeof(((struct task_struct *)NULL)->held_locks));task_struct 한 개의 held_locks 필드 크기를 sizeof로 계산해 별도의 per task-struct 로그 인자로 전달합니다. NULL 포인터 표현은 타입을 지정할 뿐 실제 주소를 역참조하지 않습니다.
함께 생각해 볼 질문
이 함수가 모든 교착을 찾아내나요?
아닙니다. 실제 경로에서 관찰된 잠금 관계와 검사 설정에 따라 진단합니다.
출력하는 sizeof는 malloc인가요?
타입이나 객체가 차지하는 크기를 계산할 뿐 새 공간을 할당하지 않습니다.
CONFIG_PROVE_LOCKING이 왜 나오나요?
해당 검증 기능이 있을 때만 관련 배열의 크기를 계산에 포함하기 때문입니다.
출처와 읽은 범위
Linux stable v6.6 · kernel/locking/lockdep.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고