본문 바로가기
Programming/7. Device Driver

드라이버 개발 핵심 - 15화 스핀락과 동기화 기법

by S.W 2026. 6. 3.
Windows 커널 & 드라이버 시리즈 — 15화

스핀락과 동기화 기법 — 멀티코어 환경에서 안전하게 데이터 공유하기

📅 2026 ⏱ 읽기 약 14분 🏷 SpinLock / Mutex / ERESOURCE / 동기화
오늘날 서버는 수십 개의 CPU 코어가 동시에 돌아가고, 드라이버 코드는 여러 코어에서 동시에 실행될 수 있어요. 그 말은 "공유 데이터를 동시에 두 코어가 건드리는" 상황이 언제든 생길 수 있다는 뜻이에요. 이번 화에서는 커널 레벨 동기화 기법들을 살펴보겠습니다.

왜 동기화가 필요한가

아래 코드를 보면 문제가 보일 거예요:

// 전역 카운터 (공유 데이터) LONG g_RequestCount = 0; // 두 코어가 동시에 이 코드를 실행하면? NTSTATUS DispatchRead(PDEVICE_OBJECT DevObj, PIRP Irp) { g_RequestCount++; // 1) 읽기 → 2) +1 → 3) 쓰기 (3단계!) // ... }

g_RequestCount++는 소스 코드 한 줄이지만 어셈블리로는 읽기→증가→쓰기의 세 단계예요. 두 코어가 동시에 "읽기"를 실행하고 둘 다 같은 값을 읽으면, 두 번 증가했어야 할 카운터가 한 번만 증가하는 버그가 생겨요. 이걸 경쟁 조건(Race Condition)이라고 합니다.

스핀락 (KSPIN_LOCK)

스핀락은 DISPATCH_LEVEL 이상에서도 사용할 수 있는 동기화 기법이에요. 락을 얻지 못한 쪽은 계속 "됐어?" 하고 확인하면서 대기(spin)합니다.

// 드라이버 전역 또는 DeviceExtension KSPIN_LOCK g_Lock; LONG g_RequestCount = 0; // 초기화 (DriverEntry 또는 DeviceExtension 초기화 시) KeInitializeSpinLock(&g_Lock); // 사용 패턴 NTSTATUS DispatchRead(PDEVICE_OBJECT DevObj, PIRP Irp) { KIRQL oldIrql; // 락 획득 (IRQL이 DISPATCH_LEVEL로 올라감) KeAcquireSpinLock(&g_Lock, &oldIrql); g_RequestCount++; // 이제 안전! // 락 해제 (IRQL이 원래대로 복귀) KeReleaseSpinLock(&g_Lock, oldIrql); // ... }
⚠️ 스핀락 보유 중에는 Paged Pool 접근 금지 KeAcquireSpinLock이 IRQL을 DISPATCH_LEVEL로 올리기 때문에, 락을 보유한 동안에는 14화에서 배운 DISPATCH_LEVEL 제약이 그대로 적용돼요. 가능한 한 짧게 보유하고, 그 사이에 복잡한 작업은 하지 마세요.

IRQL이 이미 DISPATCH_LEVEL 이상이면?

DPC 루틴처럼 이미 DISPATCH_LEVEL에서 실행 중이라면, IRQL 조작 없이 SpinLock만 획득하는 버전을 씁니다:

// 이미 DISPATCH_LEVEL 이상인 경우 KeAcquireSpinLockAtDpcLevel(&g_Lock); // ... 짧은 임계 구역 KeReleaseSpinLockFromDpcLevel(&g_Lock);

단순 카운터는 인터락 연산으로

카운터 하나를 보호하려고 스핀락을 쓰는 건 좀 무거워요. 원자적 연산(Interlocked)을 쓰면 더 가볍게 해결할 수 있어요:

LONG g_RequestCount = 0; // 원자적 증가 (락 불필요!) InterlockedIncrement(&g_RequestCount); // 원자적 감소 InterlockedDecrement(&g_RequestCount); // 원자적 교환 LONG old = InterlockedExchange(&g_RequestCount, 0); // 원자적 비교-교환 (CAS) LONG expected = 5; InterlockedCompareExchange(&g_RequestCount, 10, expected); // g_RequestCount == 5이면 10으로 바꾸고, 아니면 그대로 둠

PASSIVE_LEVEL에서 쓸 수 있는 동기화 기법

스핀락은 대기 중에 CPU를 계속 점유하기 때문에 비효율적이에요. PASSIVE_LEVEL(일반 드라이버 루틴)에서는 더 효율적인 방법들을 쓸 수 있습니다.

KMUTEX — 상호 배제

KMUTEX g_Mutex; KeInitializeMutex(&g_Mutex, 0); // 락 획득 (대기 가능 — PASSIVE_LEVEL에서만!) KeWaitForSingleObject(&g_Mutex, Executive, KernelMode, FALSE, NULL); // ... 임계 구역 // 락 해제 KeReleaseMutex(&g_Mutex, FALSE);

KEVENT — 신호 메커니즘

KEVENT g_Event; // 알림 이벤트 (모든 대기 스레드를 깨움) vs 동기 이벤트 (하나만 깨움) KeInitializeEvent(&g_Event, NotificationEvent, FALSE); // 이벤트 대기 (PASSIVE_LEVEL에서) KeWaitForSingleObject(&g_Event, Executive, KernelMode, FALSE, NULL); // 이벤트 발생시키기 (다른 스레드/DPC에서) KeSetEvent(&g_Event, IO_NO_INCREMENT, FALSE); // 이벤트 리셋 KeClearEvent(&g_Event);

ERESOURCE — 읽기/쓰기 락

읽기는 여럿이 동시에 해도 되고, 쓰기만 단독으로 해야 할 때 씁니다. 데이터베이스의 RW 락과 같은 개념이에요:

ERESOURCE g_Resource; ExInitializeResourceLite(&g_Resource); // 읽기 락 (여러 스레드 동시 획득 가능) ExAcquireResourceSharedLite(&g_Resource, TRUE); // ... 읽기 작업 ExReleaseResourceLite(&g_Resource); // 쓰기 락 (단독 접근) ExAcquireResourceExclusiveLite(&g_Resource, TRUE); // ... 쓰기 작업 ExReleaseResourceLite(&g_Resource); // 정리 ExDeleteResourceLite(&g_Resource);
📌 ERESOURCE는 PASSIVE_LEVEL 전용 ERESOURCE는 내부적으로 대기 연산을 사용하므로 반드시 PASSIVE_LEVEL에서만 사용해야 해요. 읽기가 많고 쓰기가 드문 데이터(설정 값, 디바이스 목록 등)를 보호하는 데 이상적입니다.

동기화 기법 선택 가이드

상황권장 기법사용 가능 IRQL
단순 카운터 증감InterlockedIncrement/Decrement모든 레벨
짧은 임계 구역, DPC에서 공유KSPIN_LOCK≤ DISPATCH_LEVEL
상호 배제, 대기 가능KMUTEXPASSIVE_LEVEL
스레드 간 신호 전달KEVENTPASSIVE_LEVEL (대기 시)
읽기 많고 쓰기 드문 공유 데이터ERESOURCEPASSIVE_LEVEL

데드락 방지 원칙

  • 여러 락을 획득해야 한다면 항상 같은 순서로 획득하세요. A→B 순서로 획득했으면, 어디서든 A→B 순서를 지켜야 해요. 반대로 하면 데드락이 생겨요.
  • 락을 보유한 채로 외부 코드(콜백 등)를 호출하지 마세요. 그 코드가 같은 락을 요청할 수도 있어요.
  • 스핀락은 가능한 한 짧게 보유하세요. 복잡한 계산은 락 밖에서 하세요.

✅ 15화 요약
  • KSPIN_LOCK: DISPATCH_LEVEL 이상에서 사용 가능. 대기 중 CPU를 점유(spin)합니다.
  • Interlocked 연산: 단순 정수 연산에 락 없이 원자성을 보장합니다.
  • KMUTEX, KEVENT, ERESOURCE: PASSIVE_LEVEL에서 사용. 스레드를 슬립 상태로 대기시킵니다.
  • 데드락 방지를 위해 항상 같은 순서로 락을 획득하세요.
다음 화
16화 — 커널 메모리 풀 관리: NonPaged Pool vs Paged Pool, 올바른 할당과 해제 →

#SpinLock #KMUTEX #KEVENT #동기화 #RaceCondition