커널 필수 함수·매크로·심볼 레퍼런스

커널 코드 리뷰에서 반복해서 등장하는 함수, 매크로, 심볼을 컨텍스트 중심으로 대규모 정리했습니다. 각 항목은 "언제 쓰는가", "자주 나는 실수", "대체 API", "소스 경로"를 함께 담아 실전 참고서로 바로 사용할 수 있게 구성했습니다.

전제 조건: C 언어 & 커널 C 관용어 문서를 먼저 읽으세요. 특히 ERR_PTR, READ_ONCE, container_of의 의미를 이해하고 오면 이 문서를 훨씬 빠르게 사용할 수 있습니다.
일상 비유: 이 개념은 정비 현장의 공구함과 비슷합니다. 드라이버를 수리할 때 매번 새 공구를 만드는 대신, 검증된 공구를 상황에 맞게 꺼내 쓰듯이 커널도 검증된 API 조합을 재사용합니다.

핵심 | 핵심 요약

이 요약은 표 전체를 읽기 전에 반드시 고정해야 할 판단 축을 압축한 구간입니다. 아래 항목을 먼저 이해하면 이후 섹션에서 API 이름이 달라도 판단 기준을 일관되게 유지할 수 있습니다. 특히 반환 규약, 락 규약, 수명주기 순서는 코드 리뷰에서 반복적으로 지적되는 핵심 실패 지점입니다.

실무 체크포인트: 구현 전에 이 5개 항목을 PR 설명의 "사전 점검" 항목으로 그대로 복사해 사용하세요.

  • 반환 규약 — 포인터 반환 API는 NULL인지 ERR_PTR인지 먼저 확인합니다.
  • 락 규약 — 컨텍스트에 맞는 락을 선택하고 sleep 가능 여부를 반드시 구분합니다.
  • 수명주기 — 할당, 등록, 해제의 순서를 쌍으로 기억합니다.
  • 가시성 — 심볼 export는 ABI 계약이므로 최소한만 공개합니다.
  • 관측성 — 문제 재현 전 로그 레벨, tracepoint, 통계를 먼저 설계합니다.

핵심 | 단계별 이해

단계별 이해는 "문맥 확인 → API 선택 → 실패 경로 설계"를 강제로 순서화해 실수를 줄이기 위한 절차입니다. 항목을 건너뛰면 대부분 해제 누락이나 문맥 오용이 발생하므로, 코드 작성 전에 순서를 먼저 확정하는 것이 안전합니다.

실무 체크포인트: 단계 1에서 컨텍스트를 확정하지 못하면 구현을 진행하지 말고 호출 경로부터 다시 추적하세요.

  1. 문맥 파악
    프로세스 문맥인지 인터럽트 문맥인지 먼저 결정합니다.
  2. 기본 API 선택
    메모리, 락, 에러 처리의 기본 세트를 정합니다.
  3. 실패 경로 설계
    중간 실패 시 되돌릴 순서를 코드보다 먼저 정합니다.
  4. 심볼 노출 점검
    정말 외부 모듈이 필요한 심볼만 export 합니다.
  5. 관측 포인트 추가
    pr_debug, tracepoint, 통계 카운터를 최소 단위로 배치합니다.
문서 범위: 항목 수 제한 없이 공통 커널 개발 기준으로 계속 확장되는 레퍼런스입니다. 드라이버, 메모리 관리, 네트워킹, 파일시스템, 보안, 관측성까지 동일한 규칙으로 누적 관리합니다.

핵심 | 이 문서를 빠르게 읽는 법

이 페이지는 "API 목록"이 아니라 "의사결정 지원서"입니다. 각 표는 같은 구조(언제 사용/실수 패턴/대체안)로 작성되어 있으므로, 문제가 발생했을 때 아래 순서로 접근하면 가장 빠릅니다.

  1. 문맥 확인
    현재 코드가 프로세스 문맥인지, IRQ 문맥인지, sleep 가능한지 먼저 확정합니다.
  2. 반환 규약 확인
    호출 API가 NULL 반환형인지 ERR_PTR 반환형인지 먼저 고정합니다.
  3. 짝 API 확인
    할당/해제, lock/unlock, get/put 쌍이 모두 존재하는지 체크합니다.
  4. 실수 패턴 역검증
    표의 "실수 패턴" 열을 기준으로 현재 코드가 이미 같은 실수를 반복하는지 확인합니다.
  5. 대체안 검토
    현재 경로가 문맥과 맞지 않으면 즉시 "대체/보완" API로 치환 전략을 세웁니다.
실전 팁: 신규 코드 작성 전에 이 문서에서 먼저 실패 경로를 설계하면, 리뷰 지적의 절반 이상(해제 누락/문맥 오용/반환값 누락)을 선제적으로 줄일 수 있습니다.

핵심 | API 선택 의사결정 플로우

질문Yes일 때No일 때핵심 참고 섹션
현재 문맥에서 sleep 가능한가?mutex, GFP_KERNEL 계열 우선spinlock, GFP_ATOMIC 계열 우선컨텍스트별 API 선택 매트릭스
반환값이 포인터인가?IS_ERR/PTR_ERR 또는 NULL 규약 먼저 확인정수 errno 규약으로 처리반환값 계약 카탈로그
핫패스(고빈도) 경로인가?로그/락/복사 비용 최소화, 관측 필터 적용가독성과 안정성 우선관측성 카탈로그
모듈 외부에 기능을 노출해야 하는가?EXPORT_SYMBOL* + 네임스페이스 정책 적용static 내부화 유지심볼 Export/네임스페이스
오류 재현이 간헐적인가?tracepoint + rate-limit + 재현 스크립트 고정단계별 로그로 충분문제 유형별 대응 인덱스
시작: 코드 문맥 확인 (sleep 가능 여부) 반환 규약 판별 (NULL vs ERR_PTR) 짝 API 점검 (alloc/free, get/put) 실수 패턴 역검증 (문맥 위반/해제 누락) 대체안 적용 (표의 대체 API로 치환) 최종: 실패 경로 포함 코드 확정
문맥 확인 → 반환 규약 → 짝 API 점검 → 실패 경로 확정 순서로 의사결정을 고정합니다.

실무 체크포인트: 코드 리뷰 전에 문맥, 반환규약, 해제쌍 3가지를 PR 설명에 먼저 명시하세요.

핵심 | 빠른 이동 인덱스

검색형 레퍼런스로 사용할 때는 아래 인덱스에서 도메인을 먼저 고르고, 해당 카탈로그로 바로 이동하세요.

영역즉시 이동메모
핵심 규약컨텍스트 매트릭스, 반환값 계약, 락 선택리뷰 지적의 대부분을 선제 차단
네트워크네트워킹 카탈로그, 프로토콜 심화skb/NAPI/Netfilter 중심
파일시스템파일시스템 카탈로그, 파일시스템 심화VFS와 ext4/XFS/Btrfs 분리 확인
메모리메모리 관리 카탈로그, GFP 카탈로그GFP/folio/reclaim 우선 점검
디바이스/미디어버스 카탈로그, GPU/V4L2/ALSAprobe/remove + DMA 경계
보안/운영보안/LSM, 취약점 대응, 패닉 대응운영 절차와 코드 수정을 함께 관리

핵심 | 개요

아래 다이어그램은 함수, 매크로, 심볼이 어떻게 연결되는지 보여줍니다. 함수는 동작 단위, 매크로는 규약 단축, 심볼은 모듈 간 계약입니다.

함수 kmalloc, mutex_lock 매크로 container_of, IS_ERR 심볼 EXPORT_SYMBOL* 실전 규약 반환값 검사, 락 순서, 수명주기, ABI 안정성
함수·매크로·심볼은 분리된 지식이 아니라 하나의 실전 규약으로 연결되어 동작합니다.

실무 체크포인트: 신규 심볼 export 전에는 반드시 기존 내부 함수로 대체 가능한지 먼저 확인하세요.

/* 실패 경로를 먼저 설계한 전형적인 초기화 패턴 */
static int __init sample_init(void)
{
    int ret;

    ret = register_chrdev(0, "sample", &fops);
    if (ret < 0)
        return ret;

    if (unlikely(!ready)) {
        unregister_chrdev(ret, "sample");
        return -EINVAL;
    }

    pr_info("sample initialized\n");
    return 0;
}

핵심 | 로깅과 출력 함수

로깅 API는 단순 출력 함수가 아니라 장애 대응 속도를 결정하는 운영 인터페이스입니다. 이 표는 로그 레벨 선택, 디바이스 컨텍스트 포함 여부, 폭주 억제 전략을 함께 판단하기 위한 기준으로 읽어야 합니다. 특히 핫패스에서는 정보량보다 샘플링 정책과 레이트 리밋이 우선입니다.

실무 체크포인트: 실패 로그에는 반드시 "실패 동작 + 식별자 + errno"를 한 줄에 남기고, 정상 경로 로그는 기본적으로 억제하세요.

항목언제 사용흔한 실수대체/짝 API
printk기본 커널 로그 출력레벨 미지정pr_*
pr_emerg시스템 즉시 중단급 장애일반 오류에 남용panic
pr_alert즉시 조치가 필요한 오류중복 출력pr_err
pr_crit핵심 기능 손상복구 가능 오류에 사용WARN_ON
pr_err실패 경로 보고에러 코드 누락dev_err
pr_warn비정상 상태 경고정상 경로에서 남용dev_warn
pr_notice운영자가 알아야 할 상태 변화정보 로그와 혼용pr_info
pr_info일반 상태 정보고빈도 경로에 과다 사용pr_debug
pr_debug디버그 빌드 또는 동적 디버그필수 로그를 debug로만 남김dynamic_debug
dev_err디바이스 컨텍스트 포함 오류 로그struct device 없이 사용dev_warn, dev_info

핵심 | 메모리 할당/해제

메모리 API 선택은 크기보다 실행 문맥과 회수 전략을 먼저 결정해야 안전합니다. 같은 할당이라도 sleep 가능 여부와 reclaim 허용 범위가 다르면 실패 확률과 지연 특성이 크게 달라집니다. 표를 볼 때는 "할당 성공"보다 "실패 시 되돌리기"가 닫히는지부터 확인해야 합니다.

메모리 할당이 필요한가? 작은 크기 (< PAGE_SIZE) kmalloc / kzalloc 큰 크기 (연속 불필요) vmalloc / vzalloc 크기 불확실 (fallback) kvzalloc / kvmalloc 반복 객체 (슬랩 캐시) kmem_cache_alloc 디바이스 관리형 devm_kzalloc 페이지 단위 alloc_pages / __get_free_pages 해제 규칙: kmalloc→kfree, vmalloc→vfree, kvmalloc→kvfree
할당 크기와 용도에 따라 슬랩(kmalloc), 가상(vmalloc), 자동 fallback(kvmalloc), 관리형(devm), 페이지 단위로 분류합니다.

실무 체크포인트: 모든 할당 지점에 대응하는 해제 API를 함수 단위가 아니라 실패 경로 단위로 짝지어 점검하세요.

항목언제 사용흔한 실수대체/짝 API
kmalloc작은 연속 메모리문맥에 맞지 않는 GFPkzalloc
kzalloc0 초기화가 필요한 구조체불필요한 중복 memsetkmalloc
kcalloc배열 할당곱셈 오버플로 직접 계산kvcalloc
krealloc버퍼 크기 변경실패 시 원본 포인터 덮어쓰기kvrealloc
kfreekmalloc 계열 해제중복 해제kvfree
vmalloc큰 비연속 가상 메모리DMA 가능한 메모리로 오해vzalloc
vzalloc0 초기화된 vmalloc 영역빈번한 소량 할당에 사용vmalloc
kvzalloc큰 메모리 할당 fallback해제를 kfree로만 처리kvfree
kmem_cache_create반복 객체 할당 최적화ctor 과도 사용KMEM_CACHE
kmem_cache_allocslab 객체 할당캐시 파괴 전 해제 누락kmem_cache_free

핵심 | 사용자 공간 접근

사용자 공간 접근 API는 보안 경계와 오류 전파 규약이 동시에 걸린 고위험 구간입니다. 표의 핵심은 복사 함수 선택 자체보다 반환값 해석과 부분 복사 처리 규칙을 고정하는 데 있습니다. 주소 검증과 실제 복사는 별개의 단계라는 점을 항상 분리해서 보아야 합니다.

실무 체크포인트: `copy_*_user` 반환값을 무시하는 코드는 기능 동작과 무관하게 즉시 수정 대상으로 분류하세요.

항목언제 사용흔한 실수대체/짝 API
copy_from_user유저 버퍼 입력 수신반환값 미검사get_user
copy_to_user유저 버퍼 출력부분 복사 처리 누락put_user
get_user스칼라 값 1개 읽기포인터 유효성 가정copy_from_user
put_user스칼라 값 1개 쓰기에러 코드 무시copy_to_user
strncpy_from_user문자열 길이 제한 복사NUL 보장 오해strscpy_from_user
strnlen_user유저 문자열 길이 확인반환값 단위 해석 오류strlen 사용 금지
access_ok유저 주소 범위 1차 확인복사 안전성 보장으로 오해copy_*_user와 병행
import_iovec벡터형 I/O 인자 검증복잡한 iov 직접 파싱iov_iter
pin_user_pages장기 DMA 고정unpin 누락get_user_pages
unpin_user_pagespin 해제참조 카운트 누락put_page

핵심 | 에러 처리 매크로

에러 처리 매크로는 코드 스타일이 아니라 함수 계약을 표현하는 수단입니다. 포인터 계열과 정수 계열 계약을 혼동하면 정상 경로에서도 잘못된 분기가 만들어져 장애를 유발합니다. 이 섹션은 "검사 매크로 선택"보다 "반환 계약 명시"를 먼저 수행하는 기준으로 사용해야 합니다.

실무 체크포인트: 포인터 반환 함수 선언부 주석에 `NULL` 계열인지 `ERR_PTR` 계열인지를 명시하고 그 규약만 허용하세요.

항목언제 사용흔한 실수대체/짝 API
ERR_PTR포인터 반환 함수 에러 인코딩양수 errno 전달PTR_ERR
IS_ERR에러 포인터 판별NULL 검사로 대체IS_ERR_OR_NULL
PTR_ERR에러 코드 추출정상 포인터에 적용PTR_ERR_OR_ZERO
IS_ERR_OR_NULLNULL 포함 에러 판별설계 문제 은폐IS_ERR
PTR_ERR_OR_ZEROint 반환 함수 연결반환값 의미 혼동PTR_ERR
WARN_ON비정상 조건 경고복구 가능한 조건에서 과다 사용WARN_ON_ONCE
WARN_ON_ONCE반복 경고 억제원인 추적 전 너무 일찍 사용pr_warn_ratelimited
BUG_ON치명적 불변식 위반운영 경로에서 남용WARN_ON
pr_err_ratelimited폭주 로그 억제상태 정보 손실net_ratelimit
might_sleepsleep 가능 문맥 검증atomic 경로에서 호출lockdep_assert_held

핵심 | 자료구조 매크로

자료구조 매크로는 성능 최적화 수단이 아니라 불변식 유지 수단입니다. 순회, 삽입, 삭제에서 한 단계만 어긋나도 use-after-free나 순회 손상이 즉시 발생하므로, 매크로의 전제 조건을 먼저 확인해야 합니다. 특히 RCU, 리스트 safe 순회, ID 할당 회수는 수명주기 규약과 함께 검토해야 합니다.

실무 체크포인트: 순회 중 삭제 가능성이 있으면 기본 순회 매크로 대신 safe 변형을 우선 검토하세요.

항목언제 사용흔한 실수대체/짝 API
container_of멤버 포인터에서 상위 구조체 복원잘못된 member 지정list_entry
ARRAY_SIZE정적 배열 길이 계산포인터에 사용sizeof
list_add이중 연결 리스트 삽입초기화 없는 노드 삽입list_add_tail
list_del리스트 노드 제거다시 순회 시 재사용list_del_init
list_for_each_entry타입 안전 리스트 순회순회 중 삭제list_for_each_entry_safe
hlist_add_head해시 버킷 헤드 삽입pprev 의미 오해hlist_del
xa_storeXArray 저장락 컨텍스트 누락xa_load
xa_loadXArray 조회RCU 규약 누락xa_for_each
idr_allocIDR 기반 ID 할당해제 누락ida_alloc
idr_removeIDR ID 회수중복 제거ida_free

핵심 | 락/동기화

락 선택은 동시성 제어와 지연 특성을 함께 결정하는 아키텍처 선택입니다. 이 표는 락의 이름을 외우기보다 호출 문맥, 임계영역 길이, sleep 가능 여부를 먼저 고정하기 위한 기준입니다. 락 자체보다 락 순서와 해제 경로 대칭성을 함께 확인해야 교착과 경합 회귀를 줄일 수 있습니다.

락 선택 의사결정 트리 임계영역에서 sleep이 필요한가? No → spinlock 계열 짧은 atomic 보호 Yes → mutex 계열 sleep 가능 상호배제 읽기 우세 → RCU 락리스 읽기 경로 IRQ 경쟁 → spin_lock_irqsave R/W 분리 → rw_semaphore 짧은 스냅샷 → seqlock 필수: lockdep 활성화 + 락 획득 순서 문서화 + 임계영역 최소화
sleep 가능 여부가 spinlock/mutex 선택을 결정하고, 읽기 비율이 높으면 RCU/seqlock을 검토합니다.

실무 체크포인트: 새 락을 도입할 때 기존 락과의 획득 순서를 문서화하고 lockdep 경고 가능성을 사전에 점검하세요.

항목언제 사용흔한 실수대체/짝 API
mutex_locksleep 가능한 상호배제IRQ 문맥에서 호출mutex_unlock
spin_lock짧은 atomic 보호락 상태에서 sleepspin_unlock
spin_lock_irqsave인터럽트 경쟁 동시 보호flags 복원 누락spin_unlock_irqrestore
rwlock_t읽기 많고 쓰기 적은 경로기아 상태 무시rw_semaphore
down_readrw_semaphore 읽기 락락 순서 역전up_read
down_writerw_semaphore 쓰기 락긴 임계영역up_write
rcu_read_lock락리스 읽기 보호sleep 호출rcu_read_unlock
synchronize_rcuRCU grace period 대기핫패스에서 사용call_rcu
seqlock_t짧은 쓰기, 긴 읽기읽기 재시도 누락seqcount_t
completion일회성 이벤트 동기화재초기화 누락wait_event

핵심 | 원자 연산/배리어

원자 연산과 배리어는 "동작한다"가 아니라 "가시성 계약이 맞다"를 검증해야 하는 영역입니다. `READ_ONCE`/`WRITE_ONCE`는 컴파일러 최적화 제어이고, 순서 보장은 acquire/release 또는 메모리 배리어가 담당한다는 역할 분리가 핵심입니다. 락리스 코드에서는 재시도 루프와 수명주기 조건을 반드시 함께 설계해야 합니다.

실무 체크포인트: 락리스 변경에는 쌍이 되는 acquire/release 지점을 코드 리뷰 항목으로 고정하세요.

항목언제 사용흔한 실수대체/짝 API
atomic_readatomic_t 값 조회동기화 의미 과대평가READ_ONCE
atomic_setatomic_t 값 설정초기화 이후 경쟁 고려 누락WRITE_ONCE
atomic_inc카운터 증가오버플로 무시refcount_inc
atomic_dec_and_test0 도달 판단수명주기와 분리 사용refcount_dec_and_test
refcount_inc_not_zeroUAF 방지 참조 획득반환값 무시kref_get_unless_zero
READ_ONCE컴파일러 재정렬 억제배리어 대체로 오해smp_load_acquire
WRITE_ONCE단일 저장 보장게시 순서 보장으로 오해smp_store_release
smp_mb양방향 메모리 장벽불필요한 과사용smp_rmb, smp_wmb
smp_load_acquire획득 의미 읽기쌍 API 누락smp_store_release
cmpxchg락리스 CAS 갱신루프 재시도 누락try_cmpxchg

핵심 | 스케줄링/대기

대기와 스케줄링 API는 CPU 사용률보다 깨어남 조건의 정확성이 먼저입니다. 대기 큐, 타임아웃, 인터럽트 가능 여부를 혼동하면 간헐적 행과 응답 지연이 발생합니다. 이 섹션은 "어떻게 기다릴지"보다 "누가 어떤 조건에서 깨우는지"를 먼저 고정하는 용도로 읽어야 합니다.

실무 체크포인트: wait 계열 호출에는 대응되는 wake 경로와 종료 조건을 같은 패치 안에서 함께 제시하세요.

항목언제 사용흔한 실수대체/짝 API
schedule명시적 스케줄 포인트락 보유 상태 호출cond_resched
cond_resched긴 루프에서 자발 양보atomic 문맥에서 사용might_resched
msleep밀리초 단위 sleep정밀 타이밍 기대usleep_range
usleep_range마이크로초 단위 sleep범위 과도하게 좁힘fsleep
wait_event조건 대기조건식에 락 규약 누락wake_up
wait_event_interruptible시그널 중단 가능한 대기반환값 무시wait_event_killable
wake_up대기 큐 깨우기상태 변경 전에 호출wake_up_interruptible
kthread_run커널 스레드 생성+실행정지 경로 누락kthread_create
kthread_should_stop스레드 종료 조건 확인루프에 미반영kthread_stop
kthread_stop스레드 종료 요청+join자기 자신에서 호출complete_and_exit

핵심 | 모듈/디바이스 수명주기

모듈/디바이스 수명주기 API는 등록 성공보다 정리 순서의 일관성이 중요합니다. probe/remove, init/exit, get/put 경계가 어긋나면 언로드 실패와 참조 누수가 장기적으로 누적됩니다. 표를 볼 때는 "생성 경로"와 "실패 경로"를 항상 쌍으로 확인해야 합니다.

devm 관리형 리소스 수명주기 probe 진입 devm_* 리소스 할당 정상 동작 devres 스택에 누적 remove/에러 devres 역순 해제 자동 정리 수동 free 불필요 할당 순서: devm_kzalloc → devm_ioremap_resource → devm_request_irq → devm_clk_get_enabled 해제 순서(자동 역순): clk_disable → free_irq → iounmap → kfree 금지: devm_kzalloc으로 할당 후 수동 kfree 호출 → 이중 해제 위험 원칙: 한 함수 내에서 devm과 수동 관리를 혼용하지 않는다
devm 리소스는 probe에서 할당한 역순으로 자동 해제되므로 수동 free를 혼용하면 이중 해제가 발생합니다.

실무 체크포인트: `devm_*` 사용 범위를 먼저 결정하고 수동 해제 API와 혼용하지 않도록 경계를 명확히 하세요.

항목언제 사용흔한 실수대체/짝 API
module_init모듈 진입점 등록오류 unwind 누락module_exit
module_exit모듈 정리 경로 등록등록 해제 순서 역전__exit
MODULE_LICENSE라이선스 선언누락으로 taint 발생MODULE_AUTHOR
module_param모듈 파라미터 노출권한 설정 부정확module_param_named
devm_kzalloc디바이스 관리형 메모리수동 해제 혼용kzalloc
devm_request_irq관리형 IRQ 등록핸들러 공유 플래그 누락request_irq
platform_get_resource리소스 조회존재 가정devm_platform_ioremap_resource
devm_ioremap_resourceMMIO 매핑+검증ERR_PTR 검사 누락ioremap
request_irqIRQ 핸들러 등록free_irq 누락devm_request_irq
free_irqIRQ 해제잘못된 dev_id 전달synchronize_irq

핵심 | 타이머/워크큐

타이머와 워크큐는 비동기 실행의 편의성만큼 종료 시점 관리가 어렵습니다. 스케줄링 시점보다 취소, 동기 삭제, flush 순서를 먼저 설계해야 use-after-free를 예방할 수 있습니다. 특히 자기 워커 문맥에서 동기 취소 호출 여부를 반드시 검토해야 합니다.

타이머 / 워크큐 수명주기 초기화 timer_setup 스케줄 mod_timer 콜백 실행 (softirq 문맥) 동기 삭제 del_timer_sync 재스케줄 (mod_timer in callback) 초기화 INIT_WORK 큐잉 queue_work 핸들러 실행 (프로세스 문맥) 동기 취소 cancel_work_sync 핵심: 객체 해제 전 반드시 del_timer_sync / cancel_work_sync 호출
타이머(softirq)와 워크큐(프로세스 문맥)는 실행 문맥이 다르지만, 객체 해제 전 동기 삭제가 필수라는 점은 동일합니다.

실무 체크포인트: 객체 해제 전 `del_timer_sync`/`cancel_*_sync`/`flush_*` 순서를 문서로 고정하세요.

항목언제 사용흔한 실수대체/짝 API
timer_setup타이머 초기화컨텍스트 제약 무시hrtimer_init
mod_timer타이머 재스케줄jiffies 변환 누락add_timer
del_timer_sync안전한 타이머 제거락 순서 역전timer_shutdown_sync
INIT_WORKwork_struct 초기화스택 객체 사용INIT_DELAYED_WORK
schedule_work기본 워크큐 비동기 실행중복 스케줄 오해queue_work
queue_work특정 워크큐 실행큐 수명주기 미관리flush_workqueue
queue_delayed_work지연 실행취소 경로 누락mod_delayed_work
cancel_work_sync워크 취소+완료 대기자기 워커에서 호출flush_work
flush_workqueue큐 내 작업 drain호출 빈도 과다drain_workqueue
alloc_workqueue전용 워크큐 생성플래그 선택 부정확system_wq

핵심 | 디버깅/트레이싱

디버깅 API는 문제를 빨리 찾기 위한 도구이지만 잘못 쓰면 시스템 자체를 불안정하게 만듭니다. 임시 추적, 상시 관측, 운영 경보를 목적별로 분리하고 오버헤드 상한을 먼저 정해야 합니다. 이 표는 계측 정확도와 운영 안전성의 균형을 맞추는 기준으로 사용해야 합니다.

실무 체크포인트: `trace_printk` 같은 임시 도구는 리뷰 단계에서 반드시 제거 여부를 체크리스트로 확인하세요.

항목언제 사용흔한 실수대체/짝 API
dump_stack즉시 호출 경로 확인핫패스 상시 호출WARN_ON
trace_printkftrace 버퍼 임시 추적운영 코드 잔존trace_event
tracepoint구조화된 이벤트 관측필드 ABI 무시ftrace
DEFINE_DYNAMIC_DEBUG_METADATA동적 디버그 제어정적 로그와 혼용pr_debug
lockdep_assert_held락 보유 검증락 클래스 오인might_lock
CONFIG_KASAN 계열메모리 오류 탐지 계측운영 커널에 무분별 적용KFENCE
ftrace_set_filter함수 추적 범위 축소전체 추적으로 오버헤드 증가set_ftrace_notrace
register_trace_* tracepoint 핸들러 등록unregister 누락tracepoint_probe_register
dynamic_pr_debug런타임 디버그 토글포맷 문자열 불일치pr_debug
panic복구 불가 오류 처리복구 가능 에러에서 사용BUG, WARN

핵심 | 심볼 Export/네임스페이스

심볼 export는 코드 재사용 편의가 아니라 모듈 간 ABI 계약을 외부에 공개하는 행위입니다. 한 번 노출된 인터페이스는 유지 비용이 커지므로, 최소 공개 원칙과 네임스페이스 정책을 동시에 적용해야 합니다. 이 표는 "export 가능 여부"보다 "내부화 가능성"을 먼저 검토하는 흐름으로 읽어야 합니다.

실무 체크포인트: 새 export에는 사용 모듈, 제거 불가능성, 네임스페이스 선택 근거를 커밋 메시지에 함께 남기세요.

더 깊게 보기: 이 섹션은 빠른 API 레퍼런스입니다. 커널 심볼 문서는 심볼 종류, build/runtime 해석, kallsyms, CRC, GPL/namespace 정책, 장애 대응 순서를 포함한 독립 가이드입니다.
항목언제 사용흔한 실수대체/짝 API
EXPORT_SYMBOL모듈 공용 API 공개내부 구현까지 공개EXPORT_SYMBOL_GPL
EXPORT_SYMBOL_GPLGPL 전용 API 공개라이선스 영향 미고려EXPORT_SYMBOL
EXPORT_SYMBOL_NS네임스페이스 포함 exportimport 누락MODULE_IMPORT_NS
EXPORT_SYMBOL_NS_GPLGPL+네임스페이스 export네임 충돌 방치EXPORT_SYMBOL_NS
MODULE_IMPORT_NS외부 네임스페이스 사용 선언문자열 오타depends 관리
symbol_get선택적 심볼 참조put 누락symbol_put
symbol_putsymbol_get 참조 반환중복 호출try_module_get
THIS_MODULE모듈 참조 카운트 연계NULL 가정module_put
try_module_get모듈 언로드 방지 참조 획득실패 경로 무시module_put
module_put모듈 참조 반납언밸런스 카운트try_module_get

핵심 | 버전/호환성 보조 매크로

호환성 매크로는 분기문 추가 도구가 아니라 유지보수 비용 제어 장치입니다. 버전 비교, Kconfig 조건, 컴파일 타임 검증을 혼용하면 백포트와 장기 유지에서 오류가 누적됩니다. 이 표는 런타임 분기보다 빌드 타임 계약을 우선 고정하는 기준으로 사용해야 합니다.

실무 체크포인트: 버전 조건을 추가할 때는 제거 시점과 대상 안정 커널 범위를 주석에 함께 기록하세요.

항목언제 사용흔한 실수대체/짝 API
KERNEL_VERSION버전 비교 상수 생성런타임 검사와 혼용LINUX_VERSION_CODE
LINUX_VERSION_CODE빌드 대상 커널 버전 조건백포트 정책 무시IS_ENABLED
IS_ENABLEDKconfig y/m 조건 분기#ifdef 과잉 사용IS_BUILTIN
IS_BUILTINbuilt-in 여부 분기모듈 경로 무시IS_MODULE
IS_MODULE모듈 빌드 조건 분기런타임 로직에 사용MODULE
BUILD_BUG_ON컴파일 타임 불변식 검사런타임 값 전달static_assert
static_assert표준 정적 검증메시지 누락BUILD_BUG_ON
FIELD_PREP레지스터 비트필드 설정마스크 불일치FIELD_GET
FIELD_GET비트필드 추출정수 폭 미스매치GENMASK
GENMASK비트 마스크 생성상하 비트 순서 실수BIT

핵심 | 핵심 묶음 해설

앞의 핵심 묶음 표는 "자주 쓰는 API 리스트"가 아니라 "실수 예방 인덱스"입니다. 실무에서는 다음 세 가지 축을 동시에 맞춰야 안정적인 코드가 됩니다.

즉, "컴파일이 된다"는 기준만으로는 충분하지 않습니다. "실패 경로가 닫혔는가", "문맥 위반이 없는가", "운영 중 원인 추적이 가능한가"까지 포함해 API를 선택해야 합니다.

핵심 | 도메인별 심화 예제

핵심 항목을 실제 코드 흐름으로 묶는 예제입니다. 아래 3개 패턴은 드라이버 리뷰에서 자주 보는 전형적인 형태입니다.

예제 1: probe 경로 (devm + ERR_PTR + 로그)

static int demo_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct demo_dev *d;
    int irq, ret;

    d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
    if (!d)
        return -ENOMEM;

    irq = platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;

    ret = devm_request_irq(dev, irq, demo_irq, 0, "demo", d);
    if (ret)
        return ret;

    dev_info(dev, "demo probe complete\\n");
    return 0;
}

예제 2: 락리스 플래그 전달 (READ_ONCE/WRITE_ONCE)

static bool stop_flag;

static int worker_thread(void *arg)
{
    while (!READ_ONCE(stop_flag)) {
        do_one_job();
        cond_resched();
    }
    return 0;
}

static void stop_worker(void)
{
    WRITE_ONCE(stop_flag, true);
}

예제 3: 심볼 export + 네임스페이스

int demo_calc_crc(const void *buf, size_t len)
{
    if (!buf || !len)
        return -EINVAL;
    return crc32_le(~0U, buf, len);
}
EXPORT_SYMBOL_NS_GPL(demo_calc_crc, DEMO_CORE);

/* 소비 모듈 */
MODULE_IMPORT_NS(DEMO_CORE);

확장 | 컨텍스트별 API 선택 매트릭스

같은 함수라도 실행 문맥이 다르면 안전성이 달라집니다. 아래 표는 코드 리뷰에서 가장 먼저 확인해야 하는 "문맥-API 적합성" 규칙입니다.

실행 컨텍스트 계층과 API 제약 하드IRQ (sleep 금지, 최소 작업) spin_lock_irqsave / this_cpu_inc / GFP_ATOMIC / queue_work 소프트IRQ / BH (sleep 금지) spin_lock_bh / GFP_ATOMIC / napi_schedule / netif_receive_skb 프로세스 문맥 (sleep 가능) mutex_lock / GFP_KERNEL / copy_from_user / wait_event / msleep 사용자 문맥 (시스콜 진입) copy_to_user / access_ok / signal_pending / preemptible ← 제약 강함 제약 약함 →
안쪽 문맥은 바깥 문맥의 모든 API를 사용할 수 있지만, 바깥 문맥(하드IRQ)은 안쪽 API(sleep 등)를 사용할 수 없습니다.
문맥사용 가능주의 필요금지
프로세스 문맥 (sleep 가능)mutex_lock, kmalloc(GFP_KERNEL), copy_from_usermight_sleep로 경계 점검해당 없음
소프트IRQspin_lock_bh, kmalloc(GFP_ATOMIC)NAPI 경합 구간 최소화mutex_lock, msleep
하드IRQspin_lock_irqsave, this_cpu_inc핸들러는 짧게 유지copy_to_user, schedule
타이머 콜백queue_work, mod_timer공유 데이터 락 보호mutex_lock, usleep_range
RCU read-sidercu_dereference, READ_ONCE포인터 수명 보장블로킹 API 전반
워크큐 핸들러mutex_lock, flush_work, kvzalloc장시간 작업 분할IRQ 전용 가정 코드
kthread 루프wait_event_interruptible, kthread_should_stop종료 경로 보장무한 busy loop
atomic sectionspin_lock, atomic_inc임계영역 최소화GFP_KERNEL, 모든 sleep API

확장 | GFP 플래그 실전 카탈로그

메모리 할당 실패의 절반은 플래그 선택 오류에서 시작됩니다. 아래는 실무에서 반복되는 결정표입니다.

sleep 가능한 문맥인가? Yes → GFP_KERNEL 기본 No → GFP_ATOMIC 기본 FS 재진입 방지? → GFP_NOFS IO 재진입 방지? → GFP_NOIO 실패 허용 가능? → GFP_NOWAIT 공통: 실패 경로 설계 필수 + __GFP_NOWARN은 예상된 실패에만 No Yes
GFP 플래그 선택은 sleep 가능성 → 재진입 방지 필요성 → 실패 허용 여부 순서로 결정합니다.
플래그의미대표 사용처자주 나는 실수
GFP_KERNELsleep 허용, 기본 reclaimprobe/remove, 파일시스템 일반 경로IRQ 문맥에서 사용
GFP_ATOMICsleep 금지, 비상 풀 사용IRQ/softirq 핸들러대형 버퍼 반복 할당
GFP_NOWAIT즉시 실패, reclaim 최소화지연 민감 경로실패 경로 설계 누락
GFP_NOIOIO 재진입 방지블록 계층 reclaim 경로일반 경로에서 남용
GFP_NOFS파일시스템 재진입 방지FS 락 보유 경로필요 없는 전역 적용
__GFP_ZERO0 초기화민감 구조체 초기값 보장성능 경로에서 불필요 사용
__GFP_HIGH우선순위 높은 할당핵심 네트워크 버퍼일반 경로 확장 남용
__GFP_NOWARN경고 로그 억제예상 가능한 실패 경로원인 추적 포인트 삭제
__GFP_NORETRY강한 reclaim 회피실패 허용 가능한 캐시필수 경로에서 사용
__GFP_RETRY_MAYFAIL재시도 후 실패 허용큰 버퍼, 희소 경로무조건 성공으로 가정
__GFP_COMP복합 페이지 표시hugepage 관련 버퍼해제 API 혼동
__GFP_DMA32DMA32 존 요구32비트 DMA 장치dma_mask 설정 없이 사용
/* 컨텍스트별 API 사용 예시 비교 */

/* 1. 프로세스 문맥 — sleep 가능 */
mutex_lock(&dev->lock);
buf = kmalloc(size, GFP_KERNEL);
if (!buf) { mutex_unlock(&dev->lock); return -ENOMEM; }
mutex_unlock(&dev->lock);

/* 2. 하드IRQ — sleep 금지, 최소 작업 */
static irqreturn_t demo_isr(int irq, void *data)
{
    struct demo_dev *d = data;
    u32 status = readl(d->regs + REG_STATUS);

    if (!(status & IRQ_PENDING))
        return IRQ_NONE;

    writel(status, d->regs + REG_ACK);
    this_cpu_inc(d->stats->irqs);
    queue_work(d->wq, &d->rx_work);   /* 무거운 처리는 워크큐로 */
    return IRQ_HANDLED;
}

/* 3. 소프트IRQ (NAPI) — sleep 금지, atomic 할당만 */
spin_lock(&ring->lock);
skb = netdev_alloc_skb(ndev, len);  /* GFP_ATOMIC 내부 */
spin_unlock(&ring->lock);

확장 | 락 선택 매트릭스

락 선택 매트릭스는 성능 최적화 표가 아니라 실패 모드 비교표입니다. 각 락의 장점보다 우선해서, 어떤 워크로드에서 기아·경합·지연이 발생하는지를 먼저 확인해야 합니다. 선택 이후에는 락 교체 기준과 관측 지표를 함께 정의해야 회귀를 빠르게 감지할 수 있습니다.

실무 체크포인트: 락 변경 패치에는 최소 하나의 경합 지표와 회귀 기준을 함께 첨부하세요.

락/기법강점약점적합한 워크로드대체안
mutex단순, 디버깅 용이sleep 불가 문맥 미사용드라이버 제어 경로ww_mutex
spinlockatomic 경로 보호오래 잡으면 지연 증가IRQ 공유 자료raw_spinlock
rw_semaphore읽기 병렬성쓰기 기아 가능성읽기 우세 메타데이터seqlock
seqlock/seqcount읽기 경로 빠름재시도 루프 필요짧은 스냅샷 데이터RCU
RCU락리스 읽기수명주기 설계 복잡읽기 압도적 워크로드srcu
SRCUsleep 가능한 read-side오버헤드 큼긴 콜백 기반 경로mutex
percpu_rwsem읽기 스케일링 우수쓰기 비용 큼CPU 로컬 fast pathrw_semaphore
completion이벤트 동기화 간결다회성 설계에 약함초기화 완료 신호wait_event
waitqueue조건 대기 표현력조건/락 연동 실수상태 기반 대기completion
atomic/refcount락 없이 카운팅복합 상태 보호 불가참조 카운트kref

확장 | 반환값 계약 카탈로그

패턴성공 값실패 값검사 방식대표 API
정수 errno0 또는 양수음수 errnoif (ret)request_irq, clk_prepare_enable
포인터+ERR_PTR유효 포인터ERR_PTR(-Exxx)IS_ERR/PTR_ERRdevm_ioremap_resource
포인터+NULL유효 포인터NULLif (!ptr)kmalloc 일부 래퍼
길이 반환처리 바이트 수음수 errnoret < 0read/write 계열
bool 반환true/false별도 없음의미 문서 확인kthread_should_stop
0/1 의미1 성공0 실패API별 주석 확인try_module_get
반환 타입 확인 포인터 반환 IS_ERR / NULL 규약 확인 정수 반환 0 또는 -errno 처리 길이 반환 ret < 0 에러 분기 실패 경로 및 로그 형식 통일
반환 타입별 분기 규칙을 고정하면 오류 처리 코드의 일관성과 리뷰 속도가 크게 향상됩니다.

실무 체크포인트: 포인터 반환 API는 함수 주석에 NULL 또는 ERR_PTR 규약을 명시하고 시작하세요.

반환값 규약 경고: 포인터 API를 일괄적으로 if (!ptr)로 처리하면 ERR_PTR를 놓칩니다. 호출 전 해당 API가 NULL 계열인지 ERR_PTR 계열인지 먼저 문서화하세요.

확장 | 비차단 경로 전용 API

비차단 경로는 평균 성능보다 최악 지연을 제한하는 것이 우선 목표입니다. 따라서 "성공률을 높이는 API"보다 "실패를 빠르게 표면화하는 API"를 선택해야 합니다. 표의 각 항목은 대체 경로 설계를 포함해야 의미가 있으므로 fallback 없는 사용은 금지에 가깝게 다뤄야 합니다.

실무 체크포인트: non-blocking 경로에는 실패 시 defer할 큐 또는 재시도 정책을 코드로 명시하세요.

분류권장 API피해야 할 API비고
메모리kmalloc(GFP_ATOMIC)kvzalloc(GFP_KERNEL)실패 대비 fallback 필수
spin_trylockmutex_lock실패 시 지연 큐로 이관
로그tracepoint, pr_debug_ratelimited대량 pr_info폭주 로그 차단
대기사용 금지 원칙wait_event, schedule_timeout워크큐로 defer
복사사전 pin + 사후 처리copy_from_user 직접 호출문맥별 예외 점검

확장 | 핵심 API 소스 경로 맵

소스 경로 맵은 API 사용법보다 구현 근거를 빠르게 찾기 위한 역추적 인덱스입니다. 리뷰에서 의견이 갈리면 표의 헤더와 구현 경로를 동시에 열어 계약과 실제 동작을 교차 확인해야 합니다. 특히 인라인 헤더 매크로는 호출부만 보면 의미가 왜곡되기 쉬워 원본 정의 확인이 필수입니다.

실무 체크포인트: 동작 논쟁이 발생하면 헤더 선언과 구현 경로를 함께 링크해 근거 기반으로 결론을 내리세요.

주제주요 헤더핵심 구현 경로리뷰 시 확인 포인트
에러 포인터include/linux/err.hinclude/linux/err.hIS_ERR 누락 여부
메모리 할당include/linux/slab.hmm/slub.c, mm/page_alloc.cGFP 플래그 적합성
리스트include/linux/list.h헤더 인라인 중심순회 중 삭제 안전성
include/linux/spinlock.hkernel/locking/*락 순서/문맥
RCUinclude/linux/rcupdate.hkernel/rcu/*grace period 비용
워크큐include/linux/workqueue.hkernel/workqueue.c취소/flush 경로
타이머include/linux/timer.hkernel/time/timer.c동기 삭제 필요성
스케줄링include/linux/sched.hkernel/sched/*sleep 가능성
모듈include/linux/module.hkernel/module/*export 범위 최소화
트레이싱include/linux/tracepoint.hkernel/trace/*이벤트 ABI 안정성

확장 | 고밀도 실전 스니펫

고밀도 스니펫은 복붙 예제가 아니라 실패 경로 템플릿을 압축한 참고 코드입니다. 각 스니펫은 정상 경로보다 unwind, 참조 반납, 동기화 종료 조건을 보여주는 데 목적이 있습니다. 적용 시에는 변수명보다 제어 흐름과 정리 순서를 우선 이식해야 합니다.

실무 체크포인트: 스니펫 적용 후에는 의도적으로 중간 실패를 주입해 unwind 경로가 닫히는지 먼저 검증하세요.

스니펫 1: 단계별 unwind 템플릿

static int demo_open(struct inode *inode, struct file *filp)
{
    struct demo_ctx *ctx;
    int ret;

    ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
    if (!ctx)
        return -ENOMEM;

    mutex_init(&ctx->lock);

    ret = alloc_ring(ctx);
    if (ret)
        goto err_destroy_lock;

    ret = register_fastpath(ctx);
    if (ret)
        goto err_free_ring;

    filp->private_data = ctx;
    return 0;

err_free_ring:
    free_ring(ctx);
err_destroy_lock:
    mutex_destroy(&ctx->lock);
err_free_ctx:
    kfree(ctx);
    return ret;
}

스니펫 2: RCU 리스트 갱신 패턴

void demo_insert(struct demo_node *n)
{
    spin_lock(&demo_lock);
    list_add_rcu(&n->link, &demo_head);
    spin_unlock(&demo_lock);
}

struct demo_node *demo_find(int id)
{
    struct demo_node *pos, *ret = NULL;

    rcu_read_lock();
    list_for_each_entry_rcu(pos, &demo_head, link) {
        if (READ_ONCE(pos->id) == id) {
            /* 예제 가정: demo_node는 refcount_t refcnt를 가진다 */
            if (refcount_inc_not_zero(&pos->refcnt))
                ret = pos;
            break;
        }
    }
    rcu_read_unlock();
    return ret;
}

확장 | 코드 리뷰 규칙

아래 규칙은 스타일 가이드가 아니라 장애 예방을 위한 최소 수용 기준입니다. 규칙 간 우선순위는 문맥 안전성, 수명주기 완결성, 관측 가능성 순서로 해석해야 합니다. 규칙 위반이 불가피한 경우에는 예외 근거와 복구 계획을 패치 설명에 반드시 포함해야 합니다.

실무 체크포인트: 리뷰 단계에서 규칙 위반 항목은 "후속 수정"으로 미루지 말고 같은 변경 집합에서 닫으세요.

확장 | 문제 유형별 즉시 대응 인덱스

문제 대응 인덱스는 원인 추정을 빠르게 좁히기 위한 초기 분류표입니다. 증상과 의심 지점을 1:1로 고정하지 말고, 표를 시작점으로 삼아 로그·트레이스·락 상태를 교차 확인해야 합니다. 특히 간헐 장애는 단일 도구 결과로 결론 내리지 않는 것이 중요합니다.

실무 체크포인트: 1차 대응에서 최소 두 가지 독립 증거를 확보한 뒤 수정 방향을 결정하세요.

증상1차 의심 지점확인 명령/도구우선 확인 API
Unknown symbol모듈 의존성/라이선스modinfo, cat /proc/kallsymsEXPORT_SYMBOL_GPL, MODULE_LICENSE
슬립 경고 (sleeping in atomic)문맥 오용dmesg, lockdepmutex_lock, might_sleep
간헐적 UAF참조 카운트/RCU 수명KASAN, KFENCErefcount_inc_not_zero, kfree_rcu
IRQ 폭주ack/마스킹 누락/proc/interruptsdisable_irq_nosync, napi_schedule
메모리 누수실패 경로 해제 누락kmemleakkfree, kvfree, devm_*
데드락락 순서 역전lockdep graphspin_lock_irqsave, mutex_lock
성능 회귀핫패스 로그/락 경합perf, ftracepr_debug, rcu_dereference

확장 | 네트워킹 카탈로그

네트워킹 경로는 패킷당 수백 ns 단위 최적화가 필요하므로, API 선택 기준을 명확히 고정해야 합니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
skb 수명alloc_skb패킷 버퍼 생성헤드룸 부족netdev_alloc_skb
skb 수명consume_skb참조 카운트 기반 해제kfree_skb와 혼용dev_kfree_skb_any
skb 수명skb_clone헤더 공유 복제쓰기 전 skb_cow 누락pskb_copy
헤더 조작skb_pull헤더 제거길이 검사 누락pskb_may_pull
헤더 조작skb_put테일 확장tailroom 오버런skb_tailroom
NAPInapi_schedulepoll 트리거인터럽트 마스킹 누락__napi_schedule_irqoff
NAPInapi_complete_donepoll 종료work_done 불일치napi_gro_receive
RX 경로netif_receive_skb일반 수신 경로 진입softirq 문맥 혼동netif_rx
TX 경로dev_queue_xmitL2 송신락 보유 상태로 호출sch_direct_xmit
큐 제어netif_stop_queueTX 큐 중지wake 누락netif_wake_queue
큐 제어netif_tx_stop_queue멀티큐 stop큐 인덱스 오류netif_tx_wake_queue
해시skb_get_hashflow 분산seed 가정 오류skb_set_hash
체크섬skb_checksum_helpSW checksum 보조오프로드 플래그 불일치csum_partial
오프로드skb_is_gsoGSO 여부 판별세그먼트 크기 무시skb_gso_segment
XDPbpf_prog_run_xdpXDP 프로그램 실행리턴 코드 처리 누락xdp_do_redirect
XDPxdp_return_frameXDP 프레임 반환page_pool 연계 누락page_pool_put_page
소켓sock_alloc_send_pskb소켓 송신 버퍼 생성GFP 문맥 부적합sk_stream_alloc_skb
타이머sk_reset_timer소켓 타이머 갱신락 순서 역전inet_csk_reset_xmit_timer
경로탐색ip_route_output_key_hashIPv4 라우팅namespace 누락ip6_dst_lookup_flow
conntracknf_conntrack_find_getCT 조회참조 반납 누락nf_ct_put
/* NAPI poll 최소 패턴 */
static int demo_poll(struct napi_struct *napi, int budget)
{
    int work_done = 0;

    while (work_done < budget && rx_has_packet()) {
        struct sk_buff *skb = build_skb(rx_pop(), 0);
        if (!skb)
            break;
        napi_gro_receive(napi, skb);
        work_done++;
    }

    if (work_done < budget) {
        napi_complete_done(napi, work_done);
        enable_irq(demo_irq);
    }
    return work_done;
}

확장 | 파일시스템 카탈로그

파일시스템 코드는 락 계층, 페이지 캐시, writeback 경계가 복잡하므로 VFS 진입점과 address_space 연계를 함께 확인해야 합니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
inodenew_inode새 inode 할당초기 필드 누락alloc_inode_sb
inodeiget_lockedinode 캐시 조회/생성unlock_new_inode 누락ilookup
inodeiputinode 참조 반납이중 iputihold
dentryd_make_root루트 dentry 생성에러 처리 누락d_obtain_root
dentryd_addlookup 결과 연결음수 dentry 처리 미흡d_splice_alias
슈퍼블록mount_bdev블록 기반 마운트kill_sb 누락mount_nodev
파일 연산generic_file_read_iter기본 read_iter 구현i_size 경계 처리 누락filemap_read
파일 연산generic_file_write_iter기본 write_iter 구현락 순서 역전iomap_file_buffered_write
페이지 캐시filemap_get_foliofolio 조회/생성folio 잠금 규약 위반filemap_grab_folio
페이지 캐시folio_mark_dirtydirty 설정writeback 태깅 누락set_page_dirty
writebackfilemap_fdatawritedirty 페이지 flush에러 전파 누락sync_inode_metadata
writebackfilemap_fdatawaitI/O 완료 대기timeout 설계 누락sync_filesystem
저널링jbd2_journal_start트랜잭션 시작핸들 종료 누락jbd2_journal_stop
디렉터리iterate_sharedreaddir 구현ctx pos 처리 오류dir_emit
권한inode_permission권한 점검idmap 무시generic_permission
링크vfs_link하드링크 생성nlink 갱신 누락simple_link
삭제vfs_unlinkunlink 공통 경로락 경계 위반simple_unlink
renamevfs_renamerename 공통 경로교차 디렉터리 규칙 누락lock_rename
fsnotifyfsnotify_modify수정 이벤트 알림이벤트 누락fsnotify_access
syncvfs_fsync_range부분 fsync데이터/메타 경계 오해vfs_fsync
/* 최소 VFS 연산자 골격 */
static const struct file_operations demo_fops = {
    .owner = THIS_MODULE,
    .read_iter = generic_file_read_iter,
    .write_iter = generic_file_write_iter,
    .mmap = generic_file_mmap,
    .llseek = generic_file_llseek,
    .fsync = generic_file_fsync,
};

확장 | 메모리 관리 카탈로그

메모리 관리 경로는 할당 정책, reclaim 압력, TLB/페이지 테이블 동기화까지 동시에 고려해야 합니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
페이지 할당alloc_pages고차 페이지 필요order 과대 설정alloc_page
페이지 할당__get_free_pages연속 가상 주소 필요해제 API 불일치free_pages
페이지 해제__free_pagesorder 기반 해제order mismatchput_page
매핑kmap_local_page고메모리 단기 매핑언맵 누락kunmap_local
매핑vmap페이지 배열 가상 연속 매핑vmalloc과 혼동vunmap
foliofolio_get참조 카운트 증가put 누락folio_put
foliofolio_lockfolio 변경 보호락 순서 위반folio_unlock
MMUflush_tlb_mm_range매핑 변경 후 TLB 동기화범위 과대 flushflush_tlb_page
VMAvma_lookup주소 기반 VMA 탐색mmap_lock 누락find_vma
VMAvma_modify속성 변경충돌 영역 처리 누락mprotect_fixup
faulthandle_mm_fault페이지 폴트 처리리턴 플래그 오해do_page_fault 경로 추적
reclaimshrink_node노드 reclaim 수행스캔 비율 튜닝 누락balance_pgdat
reclaimtry_to_free_pages직접 reclaimlatency 급증wakeup_kswapd
슬랩kmem_cache_alloc캐시 객체 할당ctor 부작용kmem_cache_zalloc
슬랩kmem_cache_free객체 해제다른 cache에 반환none
mmapvm_mmap커널 내부 mmap 요청권한 플래그 오류do_mmap
pin_user_pages_fast빠른 GUP pinlong-term 플래그 누락unpin_user_pages
DMAdma_alloc_coherent일관성 메모리dma_mask 미설정dma_map_single
보안 해제kvfree_sensitive민감 데이터 해제일반 kfree 사용memzero_explicit
NUMAalloc_pages_node노드 지정 할당fallback 정책 무시kmalloc_node
/* folio + writeback 최소 패턴 */
static int demo_write_folio(struct folio *folio)
{
    folio_lock(folio);
    folio_mark_dirty(folio);
    /* 실제 디바이스 I/O 제출 생략 */
    folio_unlock(folio);
    return 0;
}

확장 | 스토리지 I/O 카탈로그

스토리지 경로는 bio/request/queue 경계와 완료 처리 규약이 핵심입니다. 특히 실패 경로와 타임아웃 회수 정책을 반드시 포함해야 합니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
bio 생성bio_alloc_biosetblock I/O 요청 생성bioset 수명주기 누락bio_init
bio 관리bio_add_page페이지 연결길이/오프셋 경계 오류bio_iov_iter_get_pages
bio 제출submit_bio블록 계층 제출opf 플래그 오설정submit_bio_noacct
bio 완료bio_endio완료 콜백 종료반복 호출blk_status_to_errno
requestblk_mq_alloc_request직접 request 확보timeout 정책 누락blk_get_request
requestblk_mq_start_request디바이스 전송 시작시작/완료 순서 어긋남blk_mq_end_request
requestblk_mq_end_request요청 완료 처리잔여 바이트 처리 누락blk_update_request
queue 제어blk_mq_stop_hw_queues하드웨어 큐 일시 중지재개 누락blk_mq_start_stopped_hw_queues
timeoutblk_mq_rq_timed_out요청 타임아웃 핸들링중복 abortblk_abort_request
태그셋blk_mq_alloc_tag_set큐 태그 자원 준비hctx 수 과대 설정blk_mq_free_tag_set
큐 초기화blk_mq_init_sq_queue단일 큐 초기화queue limits 누락blk_mq_init_queue
큐 제한blk_queue_max_hw_sectors최대 전송 크기 제한장치 한계 초과blk_queue_chunk_sectors
flushblkdev_issue_flush캐시 flush 요청오류 무시REQ_PREFLUSH
discardblkdev_issue_discardtrim/unmap정렬 단위 무시REQ_OP_DISCARD
readaheadpage_cache_ra_unbounded적응형 readahead랜덤 I/O 경로 오남용ondemand_readahead
직접 I/Oiomap_dio_rwDIO 경로 공통 처리정렬 검증 누락blockdev_direct_IO
멀티패스blk_mq_map_queuesCPU-hctx 매핑NUMA 불균형set->map 튜닝
통계part_stat_add디바이스 통계 갱신완료 경로 누락blk_account_io_done
폴링blk_pollbusy polling 완료 확인CPU 소모 과다io_uring IOPOLL
오류 변환blk_status_to_errnoblk_status_t를 errno 변환직접 숫자 매핑errno_to_blk_status
/* bio 제출 최소 패턴 */
static void demo_submit_read(struct block_device *bdev,
                              sector_t sector, struct page *page)
{
    struct bio *bio = bio_alloc(bdev, 1, REQ_OP_READ, GFP_KERNEL);

    bio->bi_iter.bi_sector = sector;
    __bio_add_page(bio, page, PAGE_SIZE, 0);
    bio->bi_end_io = demo_bio_done;
    submit_bio(bio);
}

static void demo_bio_done(struct bio *bio)
{
    if (bio->bi_status)
        pr_err("bio error: %d\n",
               blk_status_to_errno(bio->bi_status));
    bio_put(bio);
}

확장 | 보안/LSM 카탈로그

보안 경로는 훅 호출 순서, cred 수명주기, 정책 캐시 일관성을 동시에 고려해야 합니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
cred 조회current_cred현재 태스크 cred 참조수정 가능한 포인터로 오해get_current_cred
cred 획득prepare_creds권한 변경 사본 생성abort 누락commit_creds
cred 반영commit_creds새 cred 적용검증 없이 적용override_creds
임시 권한override_creds권한 위임 구간revert 누락revert_creds
capabilitycapable전역 CAP 검사ns 컨텍스트 누락ns_capable
LSM 훅security_file_openfile open 정책 검사반환값 무시security_inode_permission
LSM 훅security_bprm_checkexec 검증중복 검사security_bprm_creds_for_exec
inode 정책inode_permission접근 권한 검사MAY_* 플래그 누락security_inode_permission
seccompseccomp_modeseccomp 상태 확인필터 우회 가정secure_computing
auditaudit_log_start감사 로그 시작메모리 실패 경로 누락audit_log_end
IMAima_file_check파일 무결성 검사정책 비활성 가정evm_verifyxattr
키 관리request_key커널 keyring 조회권한 도메인 오해key_lookup
하드닝CONFIG_FORTIFY_SOURCE버퍼 경계 검증 강화경고 무시FORTIFY 경고 수정
메모리 보호set_memory_ro페이지 읽기전용 전환TLB 동기화 누락set_memory_rw
사용자 복사copy_struct_from_user확장 가능한 ABI 복사size 호환성 누락copy_from_user
랜덤get_random_bytes커널 난수 획득초기화 전 사용get_random_u32
스택 검증check_copy_size복사 크기 검증직접 memcpy 사용copy_to_user
로그pr_warn_ratelimited보안 이벤트 경고로그 폭주audit_log*
정책 질의security_locked_downlockdown 제약 검사에러 전파 누락kernel_is_locked_down
레이블security_secid_to_secctxsecid->문자열 변환free 누락security_release_secctx
/* cred 변경 안전 패턴 */
static int demo_elevate(void)
{
    struct cred *new;
    int ret;

    new = prepare_creds();
    if (!new)
        return -ENOMEM;

    /* capability 추가 검증 */
    if (!ns_capable(new->user_ns, CAP_SYS_ADMIN)) {
        abort_creds(new);
        return -EPERM;
    }

    commit_creds(new);
    return 0;
}

확장 | 가상화/KVM 카탈로그

KVM 경로는 VM-Exit 처리 비용, vCPU 동기화, 메모리 슬롯 일관성이 성능과 안정성을 동시에 좌우합니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
VM 생성kvm_create_vmVM 인스턴스 생성아키텍처 init 누락kvm_arch_init_vm
vCPU 생성kvm_vm_ioctl_create_vcpuvCPU 생성 ioctlcpuid 설정 누락kvm_arch_vcpu_create
실행 루프kvm_arch_vcpu_ioctl_run게스트 실행/VM-Exitexit reason 미분기vcpu_enter_guest
메모리 슬롯kvm_set_memory_regionguest phys map 등록겹침 슬롯 검증 누락kvm_arch_prepare_memory_region
페이지 테이블kvm_mmu_mapgva/gpa 매핑TLB flush 경계 오류kvm_flush_remote_tlbs
인터럽트kvm_set_irq가상 IRQ 주입irqchip 모드 오해kvm_irq_delivery_to_apic
타이머kvm_lapic_expired_hv_timerAPIC 타이머 만료 처리주기 계산 오류hrtimer 연계
MSRkvm_get_msr_commonMSR read 에뮬레이션권한/가시성 누락kvm_set_msr_common
CPUIDkvm_update_cpuid_runtime런타임 cpuid 동기화feature mismatchkvm_set_cpu_caps
PIO/MMIOkvm_emulate_ioI/O exit 에뮬레이션반복 exit 폭주coalesced MMIO
dirty loggingkvm_get_dirty_log라이브 마이그레이션 추적비트맵 스캔 비용 과소평가dirty ring
async PFkvm_arch_async_page_readyAPF 완료 처리guest wakeup 누락kvm_make_request
PV clockkvm_write_wall_clock게스트 시간 동기화TSC 안정성 가정kvm_guest_time_update
halt pollkvm_vcpu_blockvCPU sleep/폴링poll tuning 미흡halt_poll_ns 조정
IOMMU/VFIOvfio_pin_pages디바이스 패스스루 pinunpin 누락vfio_unpin_pages
migrationkvm_arch_save_pending_timer상태 저장장치 상태 누락KVM_GET/SET_* ioctls
nestednested_vmx_run중첩 가상화 실행state sync 누락vmcs12 검증
tracetrace_kvm_exitexit reason 프로파일링샘플링 과소perf kvm stat
kvm->slots_lockmemslot 보호락 순서 역전srcu_read_lock
요청 플래그kvm_make_requestvcpu 간 동기 이벤트 전파kick 누락kvm_vcpu_kick
/* KVM run 루프 개념 스니펫 */
for (;;) {
    int r = kvm_arch_vcpu_ioctl_run(vcpu);
    if (r < 0)
        break;

    switch (vcpu->run->exit_reason) {
    case KVM_EXIT_IO:
        handle_pio(vcpu);
        break;
    case KVM_EXIT_MMIO:
        handle_mmio(vcpu);
        break;
    case KVM_EXIT_HLT:
        return;
    default:
        trace_kvm_exit(vcpu->run->exit_reason);
        break;
    }
}

확장 | 인터럽트/타이머 카탈로그

인터럽트 경로는 지연과 안정성이 직결되므로 핸들러 최소화, 하단 처리 분리, 타이머 취소 순서가 핵심입니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
IRQ 등록request_irq기본 IRQ 핸들러 등록free_irq 누락devm_request_irq
IRQ 공유IRQF_SHARED공유 인터럽트 라인dev_id 고유성 누락request_threaded_irq
스레드 IRQrequest_threaded_irqsleep 가능한 후반 처리top half에서 과도 작업IRQ_WAKE_THREAD
마스킹disable_irq_nosync즉시 인터럽트 차단재활성화 누락enable_irq
동기화synchronize_irq진행 중 핸들러 종료 대기락 보유 상태 호출synchronize_hardirq
softirqraise_softirqsoftirq 스케줄컨텍스트 혼동__raise_softirq_irqoff
tasklettasklet_schedule하단 처리 경량 분리긴 작업 처리workqueue
timertimer_setup타이머 초기화콜백 문맥 오해hrtimer_init
timermod_timer타이머 갱신jiffies 계산 오류mod_timer_pending
timerdel_timer_sync동기 제거교착 가능 락 순서timer_shutdown_sync
hrtimerhrtimer_start_range_ns고정밀 타이머슬랙 과소 설정hrtimer_forward_now
clockktime_get_ns단조 시간 측정realtime 혼용ktime_get_boottime_ns
지연usleep_range절전 친화 지연IRQ 문맥 사용fsleep
busy waitudelay매우 짧은 대기긴 대기 오남용usleep_range
workqueuequeue_delayed_work지연 하단 처리취소 경로 누락mod_delayed_work
IPIsmp_call_function_single원격 CPU 콜백dead cpu 대상 호출on_each_cpu
irqdomainirq_domain_alloc_irqs논리 IRQ 할당해제 누락irq_domain_free_irqs
affinityirq_set_affinity_hintIRQ CPU 힌트NUMA 무시irq_set_affinity
통계kstat_incr_irq_this_cpuIRQ 카운터 갱신정확도 오해/proc/interrupts
경고WARN_ON_ONCE핸들러 이상 감지반복 경고 폭주pr_warn_ratelimited
/* 스레드 IRQ + hrtimer 조합 패턴 */
static irqreturn_t demo_hard_isr(int irq, void *data)
{
    struct demo_dev *d = data;
    u32 status = readl(d->regs + IRQ_STATUS);

    if (!(status & DEMO_IRQ_MASK))
        return IRQ_NONE;

    writel(status, d->regs + IRQ_ACK);
    return IRQ_WAKE_THREAD;  /* 후반 처리 위임 */
}

static irqreturn_t demo_thread_isr(int irq, void *data)
{
    struct demo_dev *d = data;

    mutex_lock(&d->lock);       /* sleep 가능 */
    demo_process_data(d);
    mutex_unlock(&d->lock);
    return IRQ_HANDLED;
}

/* 등록 */
devm_request_threaded_irq(dev, irq,
    demo_hard_isr, demo_thread_isr,
    IRQF_SHARED, "demo", d);

확장 | 드라이버 버스(PCI/I2C/SPI) 카탈로그

버스 드라이버는 열거, 자원 매핑, 전원관리, 오류 복구를 일관된 순서로 처리해야 안정적입니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
PCI 등록pci_register_driverPCI 드라이버 등록remove 경로 누락module_pci_driver
PCI enablepcim_enable_device관리형 장치 활성화BAR 요청 전 사용pci_enable_device_mem
PCI BARpcim_iomap_regionsBAR 매핑region 마스크 오류pci_iomap
PCI DMAdma_set_mask_and_coherentDMA 주소폭 설정실패 무시dma_set_mask
PCI IRQpci_alloc_irq_vectorsMSI/MSI-X 벡터 확보fallback 미구현pci_irq_vector
PCI 에러pci_enable_pcie_error_reportingAER 활성화복구 콜백 미구현pci_error_handlers
I2C 등록i2c_add_driverI2C 드라이버 등록id table 누락module_i2c_driver
I2C 전송i2c_transfer복수 메시지 트랜잭션retries 누락i2c_smbus_read_byte_data
I2C 검증i2c_check_functionality어댑터 기능 확인기능 없는 op 호출adapter->algo 점검
SPI 등록spi_register_driverSPI 드라이버 등록of_match 누락module_spi_driver
SPI 준비spi_setupmode/speed 반영cs 변화 무시spi_sync
SPI 동기spi_sync_transfer동기 전송버퍼 수명주기 오류spi_async
regmapdevm_regmap_init_i2cI2C regmap 초기화endian 설정 오류devm_regmap_init_spi
클럭devm_clk_get_enabled클럭 획득+enablerate 설정 누락clk_set_rate
리셋devm_reset_control_get_optional_exclusive리셋 라인 제어deassert 누락reset_control_deassert
레귤레이터devm_regulator_get_enable전원 레일 제어전압 범위 검증 누락regulator_set_voltage
GPIOdevm_gpiod_getGPIO 리소스 획득active-low 처리 누락gpiod_set_value_cansleep
PM runtimepm_runtime_resume_and_get런타임 PM 활성화put 누락pm_runtime_put_autosuspend
펌웨어device_property_read_u32DT/ACPI 속성 공통 읽기기본값 누락fwnode_property_read_u32
매칭of_device_get_match_dataSoC별 데이터 선택null match 처리 누락device_get_match_data
/* PCI 드라이버 probe 최소 골격 */
static int demo_pci_probe(struct pci_dev *pdev,
                           const struct pci_device_id *id)
{
    struct demo_dev *d;
    int ret;

    ret = pcim_enable_device(pdev);
    if (ret)
        return ret;

    ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
    if (ret)
        return ret;

    ret = pcim_iomap_regions(pdev, BIT(0), "demo");
    if (ret)
        return ret;

    d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
    if (!d)
        return -ENOMEM;

    d->regs = pcim_iomap_table(pdev)[0];
    pci_set_master(pdev);
    pci_set_drvdata(pdev, d);

    ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY);
    if (ret < 0)
        return ret;

    dev_info(&pdev->dev, "probe complete\n");
    return 0;
}

/* I2C regmap 패턴 */
static const struct regmap_config demo_regmap_cfg = {
    .reg_bits = 8,
    .val_bits = 8,
    .max_register = 0xFF,
};

static int demo_i2c_probe(struct i2c_client *client)
{
    struct regmap *map;
    unsigned int val;

    map = devm_regmap_init_i2c(client, &demo_regmap_cfg);
    if (IS_ERR(map))
        return PTR_ERR(map);

    regmap_read(map, 0x00, &val);
    dev_info(&client->dev, "chip id: 0x%x\n", val);
    return 0;
}

확장 | 컨테이너/cgroups 카탈로그

컨테이너 경로는 namespace 격리와 cgroup 자원 제어가 결합되어 동작합니다. 계층별 책임을 분리해서 봐야 원인 분석이 빠릅니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
ns 생성copy_namespacesclone 시 namespace 복제참조 카운트 누락unshare_nsproxy_namespaces
pid nstask_active_pid_ns태스크 pid namespace 조회init ns 가정ns_of_pid
mnt nscopy_mnt_ns마운트 namespace 복제propagation 오해setns
net nsget_netnet namespace 참조 획득put 누락put_net
user nsmake_kuidid 매핑 변환invalid kuid 무시from_kuid_munged
cgroup attachcgroup_attach_task태스크 cgroup 이동권한 검사 누락cgroup_migrate
css 조회task_csssubsys state 조회rcu 규약 누락task_css_check
메모리 제어mem_cgroup_charge_skmemsock 메모리 과금uncharge 누락mem_cgroup_uncharge_skmem
메모리 reclaimmem_cgroup_try_charge페이지 과금 시도oom 경로 미처리try_charge_memcg
CPU 제어sched_cgroup_forkfork 시 CPU cgroup 연결weight 반영 누락cpu_cgroup_css_alloc
cpusetcpuset_cpus_allowed허용 CPU 집합 조회hotplug 변화 무시cpuset_cpus_allowed_fallback
io 제어blkcg_bio_issue_initbio cgroup 컨텍스트 초기화issue path 누락blkcg_set_ioprio
freezercgroup_freezingfreezing 상태 확인중단 불가 작업 누락try_to_freeze
psipsi_task_changepressure 상태 갱신상태 전이 누락psi_group_change
rstatcgroup_rstat_flush통계 플러시빈도 과다cgroup_rstat_updated
bpf cgroupcgroup_bpf_run_filter_skbcgroup BPF 필터 실행리턴 코드 무시BPF_CGROUP_RUN_PROG_*
네트워크 classidtask_cls_statetc classid 연계stale state 사용sock_cgroup_set_classid
OOMmem_cgroup_oom_synchronizememcg OOM 처리kthread 예외 누락out_of_memory
releasecss_putcss 참조 반환이중 putcss_get
debugcgroup_path현재 cgroup 경로 출력버퍼 크기 과소task_cgroup_path

확장 | 전원관리/열 카탈로그

전원과 열 관리는 성능/안정성/수명에 동시에 영향을 주므로 PM 런타임, cpufreq, thermal 정책의 연결을 함께 봐야 합니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
runtime PMpm_runtime_resume_and_get장치 활성화put 누락pm_runtime_put_autosuspend
runtime PMpm_runtime_enableruntime PM 시작disable 누락pm_runtime_disable
시스템 suspenddpm_suspend_startsuspend 진입순서 의존성 누락dpm_suspend_end
시스템 resumedpm_resume_startresume 복귀클럭 복구 누락dpm_resume_end
cpufreqcpufreq_register_drivercpufreq 드라이버 등록policy init 미흡cpufreq_unregister_driver
cpufreqcpufreq_update_policy정책 재평가과도 호출cpufreq_cpu_get
cpuidlecpuidle_register_driveridle state 등록target residency 부정확cpuidle_unregister_driver
열 구역thermal_zone_device_registerthermal zone 등록센서 단위 변환 오류thermal_zone_device_unregister
열 완화thermal_cdev_updatecooling device 상태 갱신state clamp 누락thermal_zone_device_update
파워캡powercap_register_control_typepowercap 타입 등록constraint 노출 누락powercap_unregister_control_type
OPPdev_pm_opp_set_rateOPP 기반 주파수 전환전압 스케일 누락dev_pm_opp_get_opp_count
EMem_dev_register_perf_domainEnergy Model 등록비용 테이블 부정확em_pd_get
wakeupdevice_set_wakeup_capablewakeup 능력 설정enable 경로 누락device_set_wakeup_enable
wakeup source__pm_stay_awake절전 지연 필요 구간relax 누락__pm_relax
QoScpu_latency_qos_add_request지연 요구 등록remove 누락cpu_latency_qos_update_request
regulatorregulator_set_voltage_triplet전압 범위 조정enable 상태 무시regulator_set_voltage
clockclk_bulk_prepare_enable다수 클럭 enabledisable 역순 누락clk_bulk_disable_unprepare
RAPLrapl_read_data_raw에너지 계측랩어라운드 처리 누락powercap sysfs
열 추적trace_thermal_temperature온도 이벤트 관측과도 추적 오버헤드tracefs 필터
진단pm_pr_dbgPM 경로 디버그운영 빌드 잔존dynamic_debug

확장 | 관측성(perf/ftrace/eBPF) 카탈로그

관측성은 "무엇을, 얼마나, 어디서" 수집할지 설계가 먼저입니다. 이벤트 스키마와 오버헤드 제어를 함께 정의해야 합니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
ftraceregister_ftrace_function함수 트레이스 훅필터 없이 전체 계측ftrace_set_filter
ftraceunregister_ftrace_function훅 해제모듈 언로드 전 누락ftrace_shutdown
tracepointtracepoint_probe_register정적 이벤트 구독unregister 누락register_trace_*
tracepointtracepoint_probe_unregister구독 해제콜백 수명주기 오류synchronize_rcu
perf eventperf_event_create_kernel_counter커널 PMU 카운터CPU hotplug 대응 누락perf_event_enable
perf samplingperf_output_sample샘플 출력ring buffer overflow 무시perf_event_overflow
bpf attachbpf_prog_attachcgroup/tc/xdp attachattach type 불일치bpf_link_create
bpf run (내부)bpf_prog_runBPF 내부 실행 경로 이해직접 호출 가능한 API로 오해bpf_prog_attach, bpf_link_create
bpf mapbpf_map_lookup_elem맵 조회per-cpu map 의미 오해bpf_map_update_elem
kproberegister_kprobe동적 함수 진입 계측핫패스 남용tracepoint
kretproberegister_kretprobe함수 반환 계측maxactive 과소 설정fentry/fexit BPF
uprobesuprobe_register유저 함수 계측symbol offset 오류perf probe
static keystatic_branch_enable런타임 분기 토글초기값/토글 불일치DEFINE_STATIC_KEY_FALSE
ringbufbpf_ringbuf_outputBPF 이벤트 전달큰 레코드 빈발perf buffer
scheduler tracetrace_sched_switch문맥 전환 추적전체 시스템 장시간 추적trace-cmd filter
lock tracelock_acquire trace락 경합 분석stack depth 과다lockstat
latencytrace_irqsoffirq-off 지연 추적운영 환경 장시간 사용osnoise
event filterset_event_pidpid 범위 축소필터 누락으로 오버헤드 증가tracefs filter
synthetic eventsynth_event_create복합 이벤트 생성필드 타입 불일치hist trigger
debugtrace_printk임시 추적운영 코드 잔존pr_debug, tracepoint
/* DEFINE_EVENT tracepoint 정의 패턴 */

/* include/trace/events/demo.h */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM demo

#if !defined(_TRACE_DEMO_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_DEMO_H
#include <linux/tracepoint.h>

TRACE_EVENT(demo_op,
    TP_PROTO(const char *name, int ret),
    TP_ARGS(name, ret),
    TP_STRUCT__entry(
        __string(name, name)
        __field(int, ret)
    ),
    TP_fast_assign(
        __assign_str(name);
        __entry->ret = ret;
    ),
    TP_printk("name=%s ret=%d", __get_str(name), __entry->ret)
);

#endif
#include <trace/define_trace.h>

/* 사용 측 */
trace_demo_op("write", ret);
# ftrace 실전 사용 예시

# 1. 함수 추적 활성화
echo demo_probe > /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on

# 2. tracepoint 이벤트 활성화
echo 1 > /sys/kernel/debug/tracing/events/demo/demo_op/enable
cat /sys/kernel/debug/tracing/trace_pipe

# 3. 인터럽트 off 지연 추적
echo irqsoff > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace  # 최대 지연 확인

확장 | 컴파일러/링커(GCC/binutils) 카탈로그

빌드 옵션은 런타임 동작을 바꾸므로 경고/최적화/보안 옵션과 ELF 결과물을 함께 점검해야 합니다.

분류핵심 옵션/도구언제 사용실수 패턴대체/보완
경고-Wall기본 경고 활성화경고 무시 문화-Wextra
경고-WerrorCI 엄격 모드로컬/CI 불일치-Wno-error=...
최적화-O2기본 커널 최적화무근거 -O3 변경per-file CFLAGS
디버그-g심볼 포함 빌드strip 후 분석 불가CONFIG_DEBUG_INFO
보안-fstack-protector-strong스택 보호핵심 파일 제외 빌드CONFIG_STACKPROTECTOR
보안-D_FORTIFY_SOURCE=2버퍼 검증 강화최적화 옵션과 충돌 오해CONFIG_FORTIFY_SOURCE
호환성-fno-strict-aliasing커널 alias 규약 유지부분 파일 누락READ_ONCE 규약
LTOCONFIG_LTO_CLANG_THIN링크단 최적화가시성 속성 누락__visible
CFICONFIG_CFI_CLANG제어흐름 무결성함수 포인터 캐스팅 남용__nocfi 최소 사용
objdumpobjdump -drS기계어/소스 대응 확인심볼 없는 바이너리 분석llvm-objdump
readelfreadelf -Ws심볼 테이블 점검가시성/바인딩 미확인nm -n
재배치readelf -rrelocation 확인section mismatch 간과objdump -r
사이즈size텍스트/데이터 크기 비교회귀 추적 누락bloat-o-meter
심볼 검색nm정의/참조 파악정렬 없는 비교nm -n
섹션 추출objcopy --only-section특정 섹션 분석디버그 섹션 유실llvm-objcopy
asm 확인-S -fverbose-asm코드 생성 검증최적화 레벨 차이 무시Compiler Explorer
링커 스크립트SECTIONS, PHDRS배치 제어정렬/경계 누락vmlinux.lds.h
버전 점검scripts/min-tool-version.sh최소 툴체인 확인개발기/CI 버전 불일치containerized build
경고 분석sparse주소공간/타입 검증C=2 누락smatch
패턴 리팩터링Coccinelle대규모 API 변환semantic patch 검증 부족git range-diff

확장 | 네트워크 프로토콜(TCP/UDP/Netfilter) 카탈로그

프로토콜 경로는 상태 전이, 타이머, conntrack, NAT 훅 순서가 복합적으로 얽혀 있습니다. 경로별 핵심 API를 함께 봐야 회귀를 줄일 수 있습니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
TCP 송신tcp_sendmsg스트림 데이터 송신잠금 범위 과대tcp_sendpage
TCP 수신tcp_recvmsg스트림 데이터 수신peek 플래그 오해tcp_read_sock
TCP 상태tcp_set_state상태 전이 갱신타이머 정리 누락tcp_done
TCP 타이머tcp_write_timer_handler재전송/지연 ACK 처리RTO 조정 오류tcp_retransmit_timer
TCP 혼잡tcp_cong_avoid_ai혼잡 회피 알고리즘 구현cwnd 단위 혼동tcp_slow_start
UDP 송신udp_sendmsg데이터그램 송신MTU 초과 처리 누락ip_append_data
UDP 수신udp_recvmsg데이터그램 수신trunc 처리 누락skb_copy_datagram_msg
IP 출력ip_local_outIPv4 local outputnetfilter 훅 우회 가정ip_output
IP 입력ip_local_deliver로컬 입력 전달fragments 처리 누락ip_rcv_finish
IPv6 출력ip6_local_outIPv6 local output확장헤더 길이 오해ip6_output
conntracknf_conntrack_in연결 추적 진입zone 고려 누락nf_ct_get
NATnf_nat_setup_infoNAT 매핑 설정충돌 처리 누락nf_nat_alloc_null_binding
Netfilter 훅nf_register_net_hook훅 등록우선순위 충돌nf_unregister_net_hook
rule 평가nf_hook_slow훅 체인 순회성능 경로 과부하flowtable offload
flowtablenf_flow_table_offload_add_cb플로우 오프로드 등록만료 처리 누락nf_flow_table_cleanup
socket lookup__inet_lookup_establishedTCP 소켓 조회ehash lock 누락inet_lookup_listener
큐 제어sk_stream_wait_memory송신 버퍼 대기signal 처리 누락sk_wait_event
ECNINET_ECN_set_ce혼잡 표시 비트 설정체크섬 재계산 누락INET_ECN_encapsulate
GROtcp_gro_receiveTCP GRO 병합옵션 파싱 누락udp_gro_receive
XFRMxfrm_lookup_routeIPsec 정책 경로 조회policy fallback 누락xfrm_policy_lookup

확장 | 파일시스템 심화(ext4/XFS/Btrfs) 카탈로그

파일시스템 심화 경로는 저널링/트랜잭션/지연할당/extent 트리 동작을 함께 이해해야 디스크 손상 없이 성능을 올릴 수 있습니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
ext4 트랜잭션ext4_journal_start메타데이터 변경 시작handle 크기 과소ext4_journal_stop
ext4 블록 매핑ext4_map_blockslogical->physical 매핑unwritten extent 처리 누락ext4_ext_map_blocks
ext4 할당ext4_mb_new_blocksmballoc 블록 할당goal block 오해ext4_claim_free_clusters
ext4 writebackext4_writepagesdirty page flushwbc sync 모드 누락mpage_map_and_submit_extent
ext4 fsyncext4_sync_file파일 동기화journal commit 순서 오류jbd2_log_start_commit
XFS 트랜잭션xfs_trans_alloc트랜잭션 확보reserve 과소xfs_trans_commit
XFS inodexfs_igetinode 조회ilock 모드 오류xfs_ilock
XFS extentxfs_bmapi_writeextent 할당cow fork 누락xfs_bmapi_convert_delalloc
XFS logxlog_cil_commitCIL commitpush 타이밍 과도 지연xfs_log_force
XFS reclaimxfs_reclaim_inodesinode reclaimAG 스캔 불균형xfs_icwalk
Btrfs 트랜잭션btrfs_start_transactiontree 변경 시작nested trans 과다btrfs_end_transaction
Btrfs extentbtrfs_reserve_extentextent 예약space info 오해btrfs_free_reserved_extent
Btrfs delayed refsbtrfs_run_delayed_refs지연 참조 처리flush 전략 미흡btrfs_start_delalloc_roots
Btrfs COWbtrfs_cow_block메타데이터 COWgeneration 체크 누락btrfs_search_slot
Btrfs balancebtrfs_balance청크 재배치필터 과도 설정btrfs_relocate_chunk
공통 iomapiomap_write_beginiomap 기반 쓰기 시작folio uptodate 처리 누락iomap_write_end
공통 daxdax_iomap_rwDAX direct accesscache flush 누락dax_writeback_mapping_range
공통 quotadquot_alloc_inodeinode quota 과금error unwind 누락dquot_free_inode
공통 freezefreeze_superfs freeze 진입unfreeze 누락thaw_super
공통 trimfstrim_rangediscard 전달정렬 제약 누락blkdev_issue_discard

확장 | 디바이스 특화(GPU/V4L2/ALSA) 카탈로그

미디어/그래픽/오디오 서브시스템은 사용자 ABI 호환성과 DMA 버퍼 공유 규약이 핵심입니다.

분류핵심 API/심볼언제 사용실수 패턴대체/보완
DRM 등록drm_dev_registerDRM 디바이스 공개unregister 누락drm_dev_unplug
DRM 메모리drm_gem_object_initGEM 객체 초기화refcount 누락drm_gem_private_object_init
KMS atomicdrm_atomic_helper_commit원자적 모드셋 적용fence 대기 누락drm_atomic_commit
DMA-BUFdma_buf_export버퍼 공유 핸들 제공map/unmap 불균형dma_buf_get
GPU 스케줄drm_sched_job_init작업 초기화entity 연결 누락drm_sched_entity_push_job
GPU 펜스dma_fence_signal작업 완료 신호중복 signaldma_fence_wait_timeout
V4L2 등록video_register_device비디오 노드 등록minor 충돌 처리 누락video_unregister_device
V4L2 queuevb2_queue_init버퍼 큐 초기화ops 구현 불일치vb2_reqbufs
V4L2 buffervb2_buffer_done프레임 완료 반환state 잘못 반환VB2_BUF_STATE_ERROR
V4L2 subdevv4l2_async_register_subdev비동기 센서 연결notifier 누락v4l2_async_nf_register
ALSA cardsnd_card_new카드 객체 생성free 경로 누락snd_card_free
ALSA PCMsnd_pcm_newPCM 디바이스 생성ops 등록 누락snd_pcm_set_ops
ALSA DMAsnd_pcm_lib_preallocate_pages버퍼 사전 할당크기 과소snd_pcm_lib_malloc_pages
ALSA triggersnd_pcm_period_elapsedperiod 완료 통지IRQ 경로 지연snd_timer_interrupt
ASoC 컴포넌트devm_snd_soc_register_componentcodec/platform 등록dai link 매칭 오류snd_soc_register_component
ASoC DAPMsnd_soc_dapm_new_controls전원 위젯 구성경로 이름 불일치snd_soc_dapm_add_routes
media requestmedia_request_object_bind요청 기반 동기화lifetime 누락media_request_put
ioctl 검증v4l2_ioctl_ops유저 ABI 처리compat_ioctl 누락video_usercopy
디버그drm_dbg, v4l2_dbg, dev_dbg서브시스템별 로그정적 로그 남용dynamic_debug
PM 연계pm_runtime_force_suspend시스템 suspend 보조resume 대칭 누락pm_runtime_force_resume

확장 | 문자열/버퍼 조작 카탈로그

커널 문자열 API는 사용자 공간 libc와 계약이 다릅니다. 특히 NUL 종료 보장, 버퍼 오버런 방지, 반환값 규약이 함수마다 다르므로 표의 "반환값" 열을 가장 먼저 확인해야 합니다. strscpystrlcpy/strncpy를 대체하는 커널 권장 API입니다.

실무 체크포인트: 새 코드에서 strcpy/strncpy/strlcpy를 발견하면 strscpy로 교체 가능 여부를 먼저 검토하세요.

문자열 복사가 필요한가? 고정 크기 버퍼 strscpy(dst, src, sz) 동적 할당 복제 kstrdup / kasprintf 포맷 문자열 출력 scnprintf / snprintf 메모리 비교/검색 memcmp / memchr / memscan 민감 데이터 소거 memzero_explicit 문자열 → 정수 변환 kstrtoul / kstrtoint 공통 규칙: 반환값 검사 + NUL 보장 확인 + FORTIFY_SOURCE 활성화
문자열 API 선택은 복사 방식(고정/동적/포맷)과 용도(비교/소거/변환)를 먼저 분류한 뒤 반환값 규약을 고정합니다.
분류핵심 API언제 사용실수 패턴대체/보완
복사strscpy고정 크기 버퍼 안전 복사반환값(-E2BIG) 무시strncpy 사용 금지
복사strscpy_pad나머지 영역 0 채움 복사패딩 필요 없는 곳에 사용strscpy
복제kstrdup힙에 문자열 복제kfree 누락kstrdup_const
복제kstrdup_const상수 문자열 중복 방지kfree_const 미사용kstrdup
복제kmemdup바이너리 데이터 복제길이 불일치memcpy + kmalloc
포맷scnprintf버퍼 내 실제 출력 길이 반환snprintf 반환값과 혼동snprintf
포맷snprintf포맷 문자열 안전 출력잘림 검사 누락(반환값 > size)scnprintf
포맷kasprintf동적 포맷 문자열 생성NULL 반환 미검사devm_kasprintf
비교strcmpNUL 종료 문자열 비교길이 미보장 문자열에 사용strncmp
비교strncasecmp대소문자 무시 비교로케일 가정strncmp
검색strstr부분 문자열 검색NUL 미종료 입력strnstr
검색strchr문자 위치 탐색NULL 반환 미검사strrchr
분리strsep토큰 분리원본 포인터 변경 인지 부족strtok 사용 금지
변환kstrtoul문자열→unsigned long에러 반환 무시kstrtouint
변환kstrtoint문자열→int오버플로 미처리kstrtol
변환kstrtobooly/n/1/0 해석빈 문자열 처리 누락strtobool deprecated
메모리memcpy비중첩 메모리 복사겹침 영역에 사용memmove
메모리memmove겹침 허용 복사불필요한 사용으로 오버헤드memcpy
메모리memset메모리 초기화kzalloc과 중복 사용memset_after
소거memzero_explicit민감 데이터 확실 소거일반 memset 사용 (최적화 제거됨)kfree_sensitive
해시xxhash비암호화 고속 해시보안 용도 사용siphash
해시siphash해시 테이블 보안 해시키 초기화 누락hsiphash
/* strscpy + scnprintf 조합 패턴 */
static ssize_t demo_show(struct device *dev,
                         struct device_attribute *attr,
                         char *buf)
{
    struct demo_dev *d = dev_get_drvdata(dev);
    char name[64];
    ssize_t ret;

    ret = strscpy(name, d->label, sizeof(name));
    if (ret < 0)
        strscpy(name, "(truncated)", sizeof(name));

    return scnprintf(buf, PAGE_SIZE,
                     "name=%s ver=%u\n", name, d->version);
}

/* kasprintf 동적 할당 패턴 */
char *label = kasprintf(GFP_KERNEL, "%s-%d", prefix, idx);
if (!label)
    return -ENOMEM;
/* ... 사용 ... */
kfree(label);

/* kstrtoul 변환 패턴 */
unsigned long val;
int ret = kstrtoul(buf, 0, &val);
if (ret)
    return ret;
if (val > MAX_LIMIT)
    return -ERANGE;

확장 | Per-CPU 변수 카탈로그

Per-CPU 변수는 락 없이 CPU별 독립 데이터를 관리하는 핵심 기법입니다. 성능 카운터, 통계, 캐시에 자주 사용되지만, 선점(preemption) 제어를 빠뜨리면 잘못된 CPU 데이터에 접근하는 미묘한 버그가 발생합니다. 이 표는 "어떤 접근 API를 쓸지"보다 "선점 보호를 어떻게 보장할지"를 먼저 고정하는 기준으로 읽어야 합니다.

실무 체크포인트: Per-CPU 변수 접근 시 get_cpu()/put_cpu() 또는 this_cpu_* 시리즈의 선점 보호 범위를 반드시 확인하세요.

Per-CPU 접근 필요 단일 연산 (원자적) this_cpu_inc / this_cpu_add 복합 연산 (선점 비활성) get_cpu_var / put_cpu_var 타 CPU 읽기 per_cpu(var, cpu) 선점 보호 없이 접근 → 잘못된 CPU 데이터 읽기 총합 수집: for_each_possible_cpu() 합산
Per-CPU 접근은 단일 원자 연산, 복합 연산(선점 비활성), 타 CPU 읽기로 분류하고 각각의 보호 규약을 고정합니다.
분류핵심 API언제 사용실수 패턴대체/보완
선언DEFINE_PER_CPU정적 per-cpu 변수 정의동적 할당과 혼동DEFINE_PER_CPU_SHARED_ALIGNED
선언DECLARE_PER_CPU외부 선언정의와 타입 불일치DEFINE_PER_CPU와 짝
동적 할당alloc_percpu런타임 per-cpu 할당free_percpu 누락alloc_percpu_gfp
동적 해제free_percpu동적 per-cpu 해제이중 해제none
단일 연산this_cpu_inc현재 CPU 카운터 증가오버플로 무시this_cpu_add
단일 연산this_cpu_read현재 CPU 값 읽기선점 후 stale 값 사용get_cpu_var
단일 연산this_cpu_write현재 CPU 값 쓰기복합 연산에서 사용this_cpu_xchg
단일 연산this_cpu_cmpxchgCAS 기반 갱신루프 재시도 누락this_cpu_xchg
복합 접근get_cpu_var선점 비활성+참조put_cpu_var 누락put_cpu_var
복합 접근get_cpu()선점 비활성+CPU 번호put_cpu() 누락put_cpu()
타 CPU 접근per_cpu(var, cpu)특정 CPU 데이터 접근동기화 없이 쓰기per_cpu_ptr
총합for_each_possible_cpu전체 CPU 합산online/possible 혼동for_each_online_cpu
포인터per_cpu_ptr동적 per-cpu 포인터 역참조NULL per-cpu 주소raw_cpu_ptr
IRQ safe__this_cpu_incIRQ 컨텍스트 내부 사용선점 가능 문맥에서 사용this_cpu_inc
/* Per-CPU 카운터 패턴 */
static DEFINE_PER_CPU(u64, demo_packets);
static DEFINE_PER_CPU(u64, demo_bytes);

static void demo_account(unsigned int len)
{
    this_cpu_inc(demo_packets);
    this_cpu_add(demo_bytes, len);
}

static u64 demo_total_packets(void)
{
    u64 sum = 0;
    int cpu;

    for_each_possible_cpu(cpu)
        sum += per_cpu(demo_packets, cpu);
    return sum;
}

/* 동적 per-cpu 할당 패턴 */
int __percpu *counters = alloc_percpu(int);
if (!counters)
    return -ENOMEM;

this_cpu_inc(*counters);

/* 정리 */
free_percpu(counters);

확장 | 알림 체인(Notifier Chain) 카탈로그

알림 체인은 커널 서브시스템 간 이벤트 전파 메커니즘입니다. CPU hotplug, 네트워크 인터페이스 상태 변화, reboot 등의 시스템 이벤트를 구독/통지하는 표준 패턴입니다. 체인 유형(blocking/atomic/raw/SRCU)에 따라 콜백 내부에서 sleep 가능 여부가 달라지므로, 등록 시 체인 유형을 먼저 확인해야 합니다.

실무 체크포인트: 콜백 내부에서 sleep이 필요하면 반드시 blocking notifier를 사용하고, atomic notifier 체인에 등록하지 마세요.

분류핵심 API언제 사용실수 패턴대체/보완
blocking 등록blocking_notifier_chain_registersleep 가능 콜백 등록unregister 누락blocking_notifier_chain_unregister
blocking 호출blocking_notifier_call_chainsleep 허용 통지리턴값 무시NOTIFY_STOP 검사
atomic 등록atomic_notifier_chain_registerIRQ 안전 콜백 등록sleep 콜백 등록atomic_notifier_chain_unregister
atomic 호출atomic_notifier_call_chainatomic 문맥 통지긴 처리 콜백raw_notifier_call_chain
SRCU 등록srcu_notifier_chain_registerSRCU 보호 콜백 등록체인 타입 혼동srcu_notifier_chain_unregister
raw 등록raw_notifier_chain_register직접 락 관리 필요동기화 누락raw_notifier_chain_unregister
netdevregister_netdevice_notifier네트워크 장치 이벤트 구독NETDEV_* 이벤트 필터 누락unregister_netdevice_notifier
inetaddrregister_inetaddr_notifierIPv4 주소 변경 구독net namespace 미고려register_inet6addr_notifier
rebootregister_reboot_notifier시스템 종료 전 정리우선순위 무시unregister_reboot_notifier
CPUcpuhp_setup_stateCPU hotplug 이벤트 구독teardown 콜백 누락cpuhp_remove_state
PMregister_pm_notifier시스템 suspend/resume 구독resume 경로 무시unregister_pm_notifier
OOMregister_oom_notifierOOM 이벤트 구독경합 증가unregister_oom_notifier
콜백 반환NOTIFY_DONE처리 완료, 계속 전파NOTIFY_OK과 혼동NOTIFY_STOP
콜백 반환NOTIFY_STOP처리 완료, 전파 중단불필요한 전파 차단NOTIFY_BAD
/* netdev notifier 최소 패턴 */
static int demo_netdev_event(struct notifier_block *nb,
                              unsigned long event, void *ptr)
{
    struct net_device *dev = netdev_notifier_info_to_dev(ptr);

    switch (event) {
    case NETDEV_UP:
        pr_info("device %s up\n", dev->name);
        break;
    case NETDEV_DOWN:
        pr_info("device %s down\n", dev->name);
        break;
    }
    return NOTIFY_DONE;
}

static struct notifier_block demo_nb = {
    .notifier_call = demo_netdev_event,
};

/* init */
register_netdevice_notifier(&demo_nb);
/* exit */
unregister_netdevice_notifier(&demo_nb);

확장 | sysfs/procfs/debugfs 인터페이스 카탈로그

커널-사용자 공간 인터페이스는 용도에 따라 sysfs(장치 속성), procfs(프로세스/시스템 정보), debugfs(디버그 전용)로 분리합니다. 인터페이스 선택을 잘못하면 ABI 유지 비용이 불필요하게 높아지거나, 디버그 전용 정보가 영구 ABI로 굳어지는 문제가 발생합니다.

실무 체크포인트: 새 인터페이스를 추가할 때 "이것이 안정 ABI여야 하는가?"를 먼저 결정하세요. 디버그/개발 전용이면 debugfs, 장치 속성이면 sysfs, 커널 전역 정보면 procfs가 원칙입니다.

사용자 공간에 정보 노출 필요 장치 속성 (안정 ABI) sysfs DEVICE_ATTR_* 커널/프로세스 정보 procfs proc_create / seq_file 디버그 전용 (비안정) debugfs debugfs_create_* 주의: sysfs는 한번 공개 시 영구 ABI debugfs는 lockdown 시 접근 불가 공통: one-value-per-file 원칙 (sysfs), seq_file 패턴 (procfs)
인터페이스 선택은 ABI 안정성 요구 여부로 먼저 분류하고, 대상(장치/프로세스/디버그)에 따라 최종 결정합니다.
분류핵심 API언제 사용실수 패턴대체/보완
sysfs 속성DEVICE_ATTR_RO읽기 전용 장치 속성show 반환값 오류DEVICE_ATTR_RW
sysfs 속성DEVICE_ATTR_RW읽기/쓰기 장치 속성store 검증 누락DEVICE_ATTR_WO
sysfs 그룹sysfs_create_group속성 그룹 일괄 생성remove 누락devm_device_add_group
sysfs 이진BIN_ATTR_RO바이너리 속성크기 불일치sysfs_create_bin_file
procfs 생성proc_createproc 엔트리 생성remove 누락proc_create_data
procfs 단일값proc_create_single단일 show 함수 proc불필요한 seq_file 사용proc_create
procfs netproc_create_netnet namespace aware procnamespace 미전달proc_create_net_single
seq_fileseq_printf순차적 데이터 출력버퍼 오버플로 가정seq_puts
seq_fileseq_openseq_file 연결stop에서 리소스 해제 누락single_open
single_opensingle_open단일 show 함수 seqsingle_release 미사용single_release
debugfs 생성debugfs_create_dir디버그 디렉터리NULL/ERR 혼동debugfs_remove_recursive
debugfs 파일debugfs_create_file커스텀 디버그 파일fops 불완전debugfs_create_u32
debugfs 단순debugfs_create_u32단일 정수 노출동기화 없이 변수 공유debugfs_create_u64
debugfs 불값debugfs_create_boolbool 토글false positive 의존debugfs_create_file
debugfs 제거debugfs_remove_recursive디렉터리+하위 전체 제거NULL dentry 처리 오류debugfs_remove
debugfs blobdebugfs_create_blob바이너리 덤프수명주기 불일치debugfs_create_file
configfsconfigfs_register_subsystem사용자 구성 객체 트리item ops 불완전configfs_unregister_subsystem
sysctlregister_sysctlsysctl 테이블 등록proc_handler 오류unregister_sysctl_table
/* sysfs 속성 패턴 */
static ssize_t status_show(struct device *dev,
                           struct device_attribute *attr,
                           char *buf)
{
    struct demo_dev *d = dev_get_drvdata(dev);
    return sysfs_emit(buf, "%u\n", READ_ONCE(d->status));
}
static DEVICE_ATTR_RO(status);

/* seq_file 패턴 */
static int demo_seq_show(struct seq_file *m, void *v)
{
    struct demo_entry *e = v;
    seq_printf(m, "%-16s %8u %8u\n", e->name, e->rx, e->tx);
    return 0;
}

/* debugfs 디렉터리+파일 패턴 */
static struct dentry *demo_debugfs;

static int __init demo_debugfs_init(void)
{
    demo_debugfs = debugfs_create_dir("demo", NULL);
    debugfs_create_u32("counter", 0444, demo_debugfs, &demo_cnt);
    debugfs_create_bool("enabled", 0644, demo_debugfs, &demo_en);
    return 0;
}

static void __exit demo_debugfs_exit(void)
{
    debugfs_remove_recursive(demo_debugfs);
}

확장 | 참조 카운팅(kref/kobject) 카탈로그

참조 카운팅은 커널 객체의 수명주기 계약을 코드로 표현하는 핵심 패턴입니다. kref는 단순 참조 카운팅, kobject는 sysfs 노출과 계층 관리를 추가합니다. 참조 카운팅 버그는 UAF(use-after-free)나 메모리 누수로 직결되므로, get/put 경로의 대칭성을 가장 먼저 검증해야 합니다.

실무 체크포인트: 모든 get 호출에 대응하는 put 경로가 정상/실패/종료 경로 모두에서 도달 가능한지 확인하세요.

kref_init refcount = 1 kref_get refcount++ kref_put refcount-- refcount == 0 release 콜백 사용 중 반복 kobject = kref + sysfs + 계층(parent/kset) kobject_init_and_add → kobject_put (release에서 kfree) 핵심: release 콜백에서만 메모리 해제 — 직접 kfree 금지
kref는 init(1) → get(++) → put(--) → release(0) 순서로 동작하며, kobject는 여기에 sysfs 연결과 계층 관계를 추가합니다.
분류핵심 API언제 사용실수 패턴대체/보완
kref 초기화kref_init참조 카운트 1로 초기화이중 초기화refcount_set
kref 획득kref_get참조 카운트 증가이미 0인 객체에 getkref_get_unless_zero
kref 안전 획득kref_get_unless_zeroUAF 방지 참조 획득반환값 무시refcount_inc_not_zero
kref 반납kref_put참조 감소 + 0이면 releaserelease 콜백 미구현kref_put_lock
kref 락+반납kref_put_mutex락 보호 하에 해제교착 위험kref_put_lock
kobject 생성kobject_init_and_add초기화+sysfs 등록실패 시 put 누락kobject_create_and_add
kobject 해제kobject_put참조 반납+sysfs 제거직접 kfree 호출kobject_del
kobject 삭제kobject_delsysfs만 제거(참조 유지)put 없이 del만 호출kobject_put
kset 생성kset_create_and_addkobject 그룹 관리unregister 누락kset_unregister
ktypekobj_type.release해제 콜백 정의release에서 자원 해제 누락none
refcountrefcount_set원시 참조 카운트 설정초기값 0으로 설정refcount_inc
refcountrefcount_dec_and_test감소+0 판별판별 결과 무시refcount_dec_and_lock
refcountrefcount_dec_and_mutex_lock감소+mutex 획득교착refcount_dec_and_lock
/* kref 기본 수명주기 패턴 */
struct demo_obj {
    struct kref refcount;
    char *name;
    struct list_head link;
};

static void demo_release(struct kref *ref)
{
    struct demo_obj *obj = container_of(ref, struct demo_obj, refcount);
    kfree(obj->name);
    kfree(obj);
}

static struct demo_obj *demo_create(const char *name)
{
    struct demo_obj *obj = kzalloc(sizeof(*obj), GFP_KERNEL);
    if (!obj)
        return NULL;

    obj->name = kstrdup(name, GFP_KERNEL);
    if (!obj->name) {
        kfree(obj);
        return NULL;
    }
    kref_init(&obj->refcount);
    return obj;
}

static void demo_get(struct demo_obj *obj) { kref_get(&obj->refcount); }
static void demo_put(struct demo_obj *obj) { kref_put(&obj->refcount, demo_release); }

확장 | MMIO/PIO 접근 카탈로그

MMIO(Memory-Mapped I/O)와 PIO(Port I/O)는 하드웨어 레지스터 접근의 두 축입니다. MMIO는 메모리 주소 공간에 매핑된 레지스터에 접근하고, PIO는 x86의 IN/OUT 명령어 기반입니다. 접근 너비(8/16/32/64비트), 순서 보장(relaxed 여부), 엔디안 변환이 핵심 체크 포인트입니다.

실무 체크포인트: MMIO 접근에는 readl/writel을 기본으로 사용하고, 핫패스에서만 readl_relaxed를 검토하세요. 직접 포인터 역참조로 레지스터를 읽지 마세요.

하드웨어 레지스터 접근 MMIO (메모리 매핑) ioremap → readl/writel → iounmap 순서 보장 / 엔디안 변환 포함 PIO (포트 I/O, x86 전용) inb/outb, inw/outw, inl/outl 느림 / 레거시 장치 전용 relaxed 변형 (핫패스) readl_relaxed / writel_relaxed 추상화 계층 (ioreadN/iowriteN) MMIO/PIO 자동 판별 금지: 직접 포인터 역참조 (*(volatile u32 *)addr) — 순서/가시성 미보장
MMIO는 ioremap+readl/writel, PIO는 inb/outb 계열이며, 추상화가 필요하면 ioreadN/iowriteN을 사용합니다.
분류핵심 API언제 사용실수 패턴대체/보완
MMIO 매핑ioremapMMIO 주소 매핑iounmap 누락devm_ioremap
MMIO 매핑devm_ioremap_resource관리형 매핑+검증ERR_PTR 검사 누락devm_platform_ioremap_resource
MMIO 읽기readb/readw/readl/readq8/16/32/64비트 MMIO 읽기크기 불일치ioread8~ioread64
MMIO 쓰기writeb/writew/writel/writeq8/16/32/64비트 MMIO 쓰기쓰기 후 읽기 확인 누락iowrite8~iowrite64
relaxed 읽기readl_relaxed순서 보장 불필요 핫패스DMA 동기화 필요 구간에서 사용readl
relaxed 쓰기writel_relaxed순서 보장 불필요 핫패스완료 보장 없이 종료writel
추상화 읽기ioread32MMIO/PIO 자동 판별불필요한 간접 호출readl
추상화 쓰기iowrite32MMIO/PIO 자동 판별성능 오버헤드writel
PIO 읽기inb/inw/inlx86 포트 I/O 읽기아키텍처 의존성ioread8
PIO 쓰기outb/outw/outlx86 포트 I/O 쓰기아키텍처 의존성iowrite8
블록 읽기memcpy_fromioMMIO 영역 블록 읽기비정렬 접근ioread32_rep
블록 쓰기memcpy_toioMMIO 영역 블록 쓰기비정렬 접근iowrite32_rep
블록 초기화memset_ioMMIO 영역 초기화일반 memset 사용none
비트필드FIELD_GET레지스터 필드 추출마스크 불일치FIELD_PREP
비트필드FIELD_PREP레지스터 필드 설정OR 연산 순서 실수GENMASK
write+확인writel+readl쓰기 완료 보장(posted write flush)flush 읽기 누락none
regmapregmap_read추상화된 레지스터 읽기endian 설정 오류regmap_write
regmapregmap_update_bitsread-modify-write 원자적 수행락 범위 오류regmap_write_bits
/* MMIO 레지스터 접근 기본 패턴 */
static void __iomem *base;

/* 레지스터 읽기/쓰기 래퍼 */
static inline u32 demo_read(u32 offset)
{
    return readl(base + offset);
}

static inline void demo_write(u32 offset, u32 val)
{
    writel(val, base + offset);
}

/* 비트필드 조작 패턴 */
#define REG_CTRL         0x00
#define CTRL_ENABLE      BIT(0)
#define CTRL_MODE_MASK   GENMASK(3, 1)

static void demo_set_mode(u32 mode)
{
    u32 val = demo_read(REG_CTRL);
    val &= ~CTRL_MODE_MASK;
    val |= FIELD_PREP(CTRL_MODE_MASK, mode);
    val |= CTRL_ENABLE;
    demo_write(REG_CTRL, val);
    /* posted write flush */
    (void)demo_read(REG_CTRL);
}

확장 | DMA 매핑 심화 카탈로그

DMA 매핑은 CPU와 디바이스 간 메모리 가시성 계약입니다. 스트리밍 매핑(map/unmap), 일관성 매핑(alloc_coherent), DMA 풀 패턴을 용도에 따라 분리해야 하며, 방향(direction) 플래그와 dma_mask 설정을 먼저 고정해야 합니다. 매핑 에러 검사를 생략하면 무효 DMA 주소로 인한 데이터 손상이 발생합니다.

실무 체크포인트: 모든 dma_map_* 호출 직후 dma_mapping_error를 검사하세요. 이 검사가 누락된 코드는 즉시 수정 대상입니다.

분류핵심 API언제 사용실수 패턴대체/보완
마스크 설정dma_set_mask_and_coherentDMA 주소폭 설정실패 무시dma_set_mask
일관성 할당dma_alloc_coherent일관성 메모리 (설명자 링)dma_handle 보관 누락dma_free_coherent
일관성 해제dma_free_coherent일관성 메모리 해제크기 불일치none
스트리밍 매핑dma_map_single단일 버퍼 스트리밍 매핑에러 검사 누락dma_unmap_single
스트리밍 해제dma_unmap_single스트리밍 매핑 해제방향(direction) 불일치none
에러 검사dma_mapping_error매핑 성공 확인검사 생략none
SG 매핑dma_map_sgscatter-gather 매핑nents 반환값 무시dma_unmap_sg
SG 해제dma_unmap_sgSG 매핑 해제원래 nents 전달(mapped 아닌)none
동기화dma_sync_single_for_cpuCPU 접근 전 동기화방향 불일치dma_sync_single_for_device
동기화dma_sync_single_for_device디바이스 접근 전 동기화크기 불일치dma_sync_single_for_cpu
DMA 풀dma_pool_create소형 DMA 객체 반복 할당destroy 누락dma_pool_destroy
DMA 풀 할당dma_pool_alloc풀에서 객체 할당GFP 문맥 오류dma_pool_free
방향DMA_TO_DEVICECPU→디바이스양방향 남용DMA_FROM_DEVICE
방향DMA_FROM_DEVICE디바이스→CPU방향 반대DMA_TO_DEVICE
방향DMA_BIDIRECTIONAL양방향 전송불필요한 양방향 사용단방향 우선
페이지 매핑dma_map_page페이지 단위 매핑에러 검사 누락dma_unmap_page
리소스 매핑dma_map_resource물리 주소 직접 매핑사용처 제한 미인지dma_unmap_resource
속성dma_alloc_attrs속성 지정 할당DMA_ATTR_* 오용dma_alloc_coherent
/* DMA 스트리밍 매핑 패턴 */
dma_addr_t dma_handle;
void *buf = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!buf)
    return -ENOMEM;

dma_handle = dma_map_single(dev, buf, BUF_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_handle)) {
    kfree(buf);
    return -EIO;
}

/* 디바이스에 DMA 전송 시작 */
start_dma_transfer(dma_handle, BUF_SIZE);

/* 완료 후 해제 */
dma_unmap_single(dev, dma_handle, BUF_SIZE, DMA_TO_DEVICE);
kfree(buf);

/* 일관성 메모리 패턴 (descriptor ring) */
dma_addr_t ring_dma;
struct demo_desc *ring;

ring = dma_alloc_coherent(dev, ring_size, &ring_dma, GFP_KERNEL);
if (!ring)
    return -ENOMEM;

/* ring 사용 — CPU와 디바이스 모두 동일 내용 참조 */
ring[0].addr = cpu_to_le64(data_dma);
ring[0].len  = cpu_to_le32(data_len);

/* 해제 */
dma_free_coherent(dev, ring_size, ring, ring_dma);

확장 | 펌웨어 로딩 카탈로그

펌웨어 로딩은 디바이스 초기화 경로의 외부 의존성을 관리하는 API입니다. 로딩 시점(probe/resume), 실패 정책(선택적/필수), 보안 검증을 함께 결정해야 합니다. 특히 initramfs에 펌웨어가 포함되지 않으면 시스템 부팅이 실패할 수 있으므로, fallback 전략이 필수입니다.

실무 체크포인트: 펌웨어 경로/이름을 하드코딩하지 말고 MODULE_FIRMWARE로 선언하여 빌드 시스템에서 추적 가능하게 하세요.

분류핵심 API언제 사용실수 패턴대체/보완
동기 로딩request_firmware펌웨어 필수 로딩release 누락release_firmware
선택적 로딩firmware_request_nowarn펌웨어 없어도 진행실패 시 fallback 누락request_firmware
비동기 로딩request_firmware_nowaitprobe 비차단콜백 수명주기 오류request_firmware
직접 로딩request_firmware_directuevent 비사용 로딩사용자 공간 fallback 기대request_firmware
캐시firmware_request_cachesuspend 전 캐시캐시 만료 관리 누락request_firmware
해제release_firmware펌웨어 버퍼 해제이중 해제none
선언MODULE_FIRMWARE의존 펌웨어 선언선언 누락 (배포 패키지에서 누락)none
내장CONFIG_EXTRA_FIRMWARE커널 이미지에 펌웨어 포함라이선스 충돌initramfs 포함
크기 검증fw->size펌웨어 크기 확인크기 미검증으로 오버런헤더 매직/CRC 검증
데이터 접근fw->data펌웨어 바이너리 접근해제 후 접근복사 후 사용
/* 펌웨어 로딩 기본 패턴 */
static int demo_load_fw(struct device *dev)
{
    const struct firmware *fw;
    int ret;

    ret = request_firmware(&fw, "demo/fw.bin", dev);
    if (ret) {
        dev_err(dev, "firmware load failed: %d\n", ret);
        return ret;
    }

    if (fw->size < sizeof(struct demo_fw_hdr)) {
        dev_err(dev, "firmware too small: %zu\n", fw->size);
        ret = -EINVAL;
        goto out;
    }

    /* 펌웨어를 디바이스에 전송 */
    ret = demo_upload_fw(dev, fw->data, fw->size);

out:
    release_firmware(fw);
    return ret;
}

MODULE_FIRMWARE("demo/fw.bin");

확장 | 실전 함정 패턴 모음

코드 리뷰에서 반복적으로 발견되는 실전 함정을 패턴화한 모음입니다. 각 항목은 "컴파일은 통과하지만 장애를 유발하는" 코드를 중심으로, 잘못된 코드와 올바른 수정을 나란히 제시합니다. 새 패치를 제출하기 전에 이 목록을 체크리스트로 역검증하면 리뷰 지적의 상당수를 선제적으로 제거할 수 있습니다.

실무 체크포인트: 아래 패턴 중 하나라도 현재 코드에 존재하면, 기능 동작과 무관하게 즉시 수정 우선순위로 분류하세요.

실전 함정 패턴 분류 수명주기 함정 UAF, 이중해제, 누수 문맥 위반 함정 sleep in atomic, 락 교착 반환값 함정 NULL/ERR_PTR 혼동 순서/가시성 함정 배리어 누락, 순서 역전 공통 대응: 정적 분석(sparse/smatch) + lockdep + KASAN/KCSAN 활성화
함정 패턴은 수명주기, 문맥 위반, 반환값, 순서/가시성의 4대 분류로 나뉘며, 정적 분석과 런타임 검증을 병행해야 탐지 가능합니다.
#함정 이름잘못된 코드올바른 수정분류
1ERR_PTR NULL 혼동if (!ptr)ERR_PTR 놓침if (IS_ERR(ptr))반환값
2krealloc 원본 유실p = krealloc(p, ...) → 실패 시 원본 유실tmp = krealloc(p, ...); if (!tmp) { ... } p = tmp;수명주기
3copy_from_user 반환값 무시copy_from_user(dst, src, n);if (copy_from_user(...)) return -EFAULT;반환값
4IRQ 내 mutexhardirq 핸들러에서 mutex_lock()spin_lock_irqsave() 또는 워크큐 defer문맥 위반
5devm+수동 혼용devm_kzallockfree 수동 호출devm_*만 사용 또는 수동만 사용수명주기
6순회 중 삭제list_for_each_entrylist_dellist_for_each_entry_safe 사용수명주기
7spin_lock 내 sleepspin_lock() 보유 중 kmalloc(GFP_KERNEL)GFP_ATOMIC 사용 또는 락 밖 할당문맥 위반
8WRITE_ONCE 게시 오해WRITE_ONCE(data, val); WRITE_ONCE(flag, 1);WRITE_ONCE(data, val); smp_store_release(&flag, 1);순서/가시성
9unwind 순서 역전할당 순서: A→B→C, 해제: A→B→C해제 역순: C→B→A수명주기
10dma_map 에러 무시dma_map_single() 후 검사 없이 사용if (dma_mapping_error(...)) return -EIO;반환값
11tasklet 긴 작업tasklet 콜백에서 수백 µs 처리workqueue로 이관문맥 위반
12cancel_work 자기 문맥워크큐 핸들러 내 cancel_work_sync(self)cancel_work_sync는 외부에서만 호출수명주기
13snprintf 반환값 혼동len = snprintf(buf, sz, ...) → len > sz 미검사scnprintf 사용 또는 잘림 검사반환값
14RCU read 내 sleeprcu_read_lock()mutex_lock()SRCU 사용 또는 구조 변경문맥 위반
15refcount 0에서 getkref_get() 이미 0인 객체에 호출kref_get_unless_zero() + 반환값 검사수명주기
16irqsave flags 전달flags를 다른 함수에 전달 후 restore같은 함수 내에서 save/restore 쌍 유지문맥 위반
17posted write 미완료writel(val, reg) 후 즉시 종료writel(val, reg); (void)readl(reg); 으로 flush순서/가시성
18GFP_KERNEL in softirqsoftirq 컨텍스트에서 kmalloc(GFP_KERNEL)GFP_ATOMIC 사용문맥 위반
19module_put 언밸런스try_module_get 없이 module_putget/put 쌍 유지수명주기
20ARRAY_SIZE 포인터 적용포인터에 ARRAY_SIZE 사용크기를 별도 인자로 전달반환값
/* 함정 #2 krealloc 원본 보호 패턴 */
void *tmp = krealloc(buf, new_size, GFP_KERNEL);
if (!tmp) {
    /* buf는 여전히 유효 — 기존 데이터 보존 */
    pr_warn("realloc failed, keeping old buffer\n");
    return -ENOMEM;
}
buf = tmp;

/* 함정 #9 올바른 unwind 순서 */
static int demo_init(void)
{
    int ret;

    ret = step_a_init();     /* 1번째 */
    if (ret)
        return ret;

    ret = step_b_init();     /* 2번째 */
    if (ret)
        goto undo_a;

    ret = step_c_init();     /* 3번째 */
    if (ret)
        goto undo_b;

    return 0;

undo_b:
    step_b_exit();           /* 역순: 2번 해제 */
undo_a:
    step_a_exit();           /* 역순: 1번 해제 */
    return ret;
}

/* 함정 #8 올바른 게시 순서 */
WRITE_ONCE(shared_data, new_value);
smp_store_release(&data_ready, 1);

/* 읽기 측 */
if (smp_load_acquire(&data_ready))
    use(READ_ONCE(shared_data));

운영 | 보안 취약점 대응 플레이북

취약점 대응은 탐지 속도보다 재현 가능성과 완화 우선순위가 중요합니다. 아래 표는 커널 취약점 대응의 실전 기준 절차입니다.

단계핵심 액션사용 API/도구실수 패턴완화 포인트
탐지이상 징후 수집audit_log*, tracepoint, dmesg단일 로그 소스만 의존증거 다중 수집
분류취약점 유형 판정(UAF/OOB/경합)KASAN/KCSAN/UBSAN재현 전 단정유형별 재현 템플릿 사용
재현최소 입력으로 재현 시나리오 고정syzkaller repro, kselftest환경 차이 방치커널 config/커밋 고정
영향도권한 상승/정보노출/DoS 범위 평가LSM hook 추적, capability 검토로컬/원격 경계 혼동CVSS/운영 맥락 병행
원인 추적문제 커밋/경로 식별git bisect, ftrace증상 커밋만 수정invariant 붕괴 지점 확인
즉시 완화공격 표면 축소sysctl, module blacklist서비스 영향 미검토가역적 완화 우선
패치 설계근본 원인 수정 + 방어코드refcount_t, READ_ONCE증상 우회 코드수명주기/동기화 재설계
검증회귀/성능/안정성 확인KUnit, kselftest, perf정상 경로만 테스트실패 경로 자동화 포함
배포백포트/릴리스 노트 반영stable queue, CVE 공지영향 버전 누락수정 대상 버전 명확화
사후탐지 룰/가드레일 강화Coccinelle, CI policy단발성 대응 종료재발 방지 룰 추가
/* UAF 완화 예시: refcount로 수명주기 보호 */
struct demo_obj {
    refcount_t refcnt;
    spinlock_t lock;
    struct rcu_head rcu;
};

static bool demo_get(struct demo_obj *o)
{
    return refcount_inc_not_zero(&o->refcnt);
}

static void demo_put(struct demo_obj *o)
{
    if (refcount_dec_and_test(&o->refcnt))
        kfree_rcu(o, rcu);
}

운영 | 커널 패닉/크래시 즉시 대응 절차

패닉 대응은 "재부팅"보다 "증거 보존"이 먼저입니다. 아래 절차는 운영 현장에서의 우선순위를 반영합니다.

시점즉시 조치수집 항목도구/명령주의점
T0자동 재부팅 정책 확인panic timeout, watchdogsysctl kernel.panic증거 수집 전 재부팅 방지
T0+콘솔 출력 확보Oops/Panic traceserial console, netconsole스크린샷만 남기지 말 것
T1메모리 덤프 경로 확인vmcorekdump/crashkernel덤프 공간 부족 점검
T1+pstore 수집ramoops 레코드/sys/fs/pstore재부팅 시 덮어쓰기 주의
T2락업 유형 분류softlockup/hardlockup/RCU stallwatchdog trace증상 명칭 혼동 금지
T2+모듈/커널 정보 고정빌드 ID, taint, configuname -a, /proc/sys/kernel/tainted버전 추정 금지
T3재현 조건 최소화입력/로드/시점stress, repro script동시 변경 최소화
T3+콜트레이스 해석faulting RIP/stackcrash, gdb, addr2lineinlined frame 누락 주의
T4완화 패치 적용hotfix 결과livepatch/quick patch근본 수정과 분리 관리
T5사후 리포트 작성원인/영향/재발방지incident template타임라인 정확성 유지

운영 | 성능 튜닝 체크리스트 (워크로드별)

성능 튜닝은 워크로드 유형별 병목이 다르므로 단일 "최적화 옵션"이 없습니다. 측정 지표를 먼저 고정한 뒤 조정해야 합니다.

워크로드주요 병목1차 측정튜닝 포인트회귀 체크
고QPS 네트워크irq/NAPI 경합, skb 할당perf top, /proc/softirqsRPS/XPS, napi budget, page_poolp99 지연, drop율
저지연 트레이딩스케줄 지터, irq-off 시간osnoise, trace_irqsoffCPU isolation, NO_HZ_FULLworst-case latency
대용량 스토리지큐 깊이, flush 비용iostat, blktracemq-deadline/bfq, io_uring iopolltail latency, write amp
DB OLTPlock 경합, page cache missperf c2c, vmstatNUMA binding, dirty ratio, hugepageTPS, 99p commit time
로그 수집/스트리밍context switch, copy overheadperf sched, bpftracebatching, zero-copy, busy-pollCPU/throughput 균형
컨테이너 멀티테넌트cgroup 경쟁, PSI 상승psi, cgroup statcpu.weight, memory.high, io.maxnoisy-neighbor 억제율
GPU 추론DMA-BUF 동기화, IRQ burstdrm trace, irqstatfence batching, cpuset 분리frame time, jitter
미디어 인코딩V4L2 queue underflowvb2 stats, ftracebuffer depth, dma burst 조정drop frame 비율
가상화 호스트VM-exit 폭증, dirty loggingperf kvm stathugepage, posted interrupt, halt pollguest steal time
파일 서버dentry/inode cache thrashslabtop, vfsstatreadahead, writeback, nfsd threadops/sec, cache hitrate
튜닝 원칙: 설정을 한 번에 많이 바꾸지 말고, 한 번에 하나의 변수만 바꾸세요. 변경마다 기준 지표(p50/p99, CPU, 에러율)를 같은 부하로 재측정해야 의미 있는 결론을 얻을 수 있습니다.

운영 | 적용 순서 해설

문서를 읽고 실제 코드/시스템에 반영할 때는 아래 순서를 추천합니다. 이 순서는 장애 위험을 낮추고 롤백을 쉽게 만듭니다.

  1. 관측 먼저
    로그 레벨, tracepoint, 기본 메트릭을 먼저 켭니다. 관측 없는 최적화는 회귀를 숨깁니다.
  2. 안전성 다음
    반환값 규약, 락 순서, 해제 경로를 고정합니다. 성능 이전에 crash/UAF를 먼저 제거합니다.
  3. 성능 마지막
    핫패스에 한정해서 튜닝합니다. 모든 경로를 동시에 건드리면 원인 분리가 불가능해집니다.
  4. 롤백 준비
    변경 전/후 수치를 저장하고 즉시 되돌릴 스위치(sysctl/module param)를 남깁니다.

운영 | 실전 체크리스트

이 체크리스트는 문서 전반의 규약을 배포 직전 점검 항목으로 재구성한 것입니다. 각 항목은 단독 통과보다 항목 간 정합성이 중요하며, 하나라도 누락되면 장애 전파 경로가 열릴 수 있습니다. 운영 반영 전에는 기능 테스트와 별도로 이 목록을 독립 검증해야 합니다.

실무 체크포인트: 체크리스트 점검 결과를 릴리스 노트에 남겨 회귀 시점 추적이 가능하도록 관리하세요.

다음 학습: