뮤텍스 개체 소개

이름에서 알 수 있듯이 뮤텍스 개체는 커널 모드 스레드 집합 간에 공유되는 단일 리소스에 대한 상호 배타적 액세스를 보장하도록 설계된 동기화 메커니즘입니다. 임원 작업자 스레드를 사용하는 FSD(파일 시스템 드라이버)와 같은 최상위 드라이버만 뮤텍스 개체를 사용할 수 있습니다.

드라이버에서 만든 스레드 또는 작업자 스레드 콜백 루틴이 있는 최상위 드라이버는 뮤텍스 개체를 사용할 수 있습니다. 그러나 페이지가 지정 가능한 스레드 또는 작업자 스레드 콜백 루틴이 있는 모든 드라이버는 뮤텍스 개체의 획득, 대기 및 릴리스를 매우 신중하게 관리해야 합니다.

Mutex 개체에는 SMP 컴퓨터의 공유 리소스에 대한 시스템(커널 모드 전용) 스레드를 상호 배타적이고 교착 상태가 없는 액세스를 제공하는 기본 제공 기능이 있습니다. 커널은 한 번에 단일 스레드에 뮤텍스의 소유권을 할당합니다.

뮤텍스의 소유권을 획득하면 일반 커널 모드 APC(비동기 프로시저 호출)가 전달되지 않습니다. 커널이 I/O 작업의 원래 요청자에게 결과를 반환하는 I/O 관리자의 IRP 완료 루틴과 같은 특수 커널 APC를 실행하기 위해 APC_LEVEL 소프트웨어 인터럽트를 실행하지 않는 한 스레드는 APC에 의해 선점되지 않습니다.

스레드는 이미 소유하고 있는 뮤텍스 개체의 소유권(재귀 소유권)을 획득할 수 있지만 재귀적으로 획득한 뮤텍스 개체는 스레드가 소유권을 완전히 해제할 때까지 Signaled 상태로 설정되지 않습니다. 이러한 스레드는 다른 스레드가 뮤텍스를 획득하기 전에 소유권을 획득한 횟수만큼 뮤텍스를 명시적으로 해제해야 합니다.

커널은 뮤텍스를 소유한 스레드가 먼저 뮤텍스를 해제하고 신호됨 상태로 설정하지 않고 사용자 모드로 전환하도록 허용하지 않습니다. 뮤텍스를 소유하는 FSD에서 만든 스레드 또는 드라이버에서 만든 스레드가 뮤텍스의 소유권을 해제하기 전에 I/O 관리자에게 컨트롤을 반환하려고 하면 커널이 시스템을 다운합니다.

뮤텍스 개체를 사용하는 모든 드라이버는 뮤텍스 개체를 대기하거나 해제하기 전에 KeInitializeMutex 를 한 번 호출해야 합니다. 다음 그림에서는 두 시스템 스레드가 뮤텍스 개체를 사용하는 방법을 보여 줍니다.

뮤텍스 개체 대기를 보여 주는 다이어그램

이전 그림과 같이 뮤텍스 개체를 사용하는 드라이버는 상주해야 하는 뮤텍스 개체에 대한 스토리지를 제공해야 합니다. 드라이버는 드라이버에서 만든 디바이스 개체의 디바이스 확장 , 컨트롤러 개체를 사용하는 경우 컨트롤러 확장 또는 드라이버가 할당한 비페이지 풀을 사용할 수 있습니다.

드라이버는 일반적으로 AddDevice 루틴에서 KeInitializeMutex를 호출할 때 커널이 Signaled 상태로 초기화하는 뮤텍스 개체에 대한 드라이버 스토리지에 대한 포인터를 전달해야 합니다.

이러한 최고 수준의 드라이버가 초기화되면 이전 그림과 같이 공유 리소스에 대한 상호 배타적 액세스를 관리할 수 있습니다. 예를 들어 기본적으로 동기 작업 및 스레드에 대한 드라이버의 디스패치 루틴은 뮤텍스를 사용하여 IRP에 대해 드라이버에서 만든 큐를 보호할 수 있습니다.

KeInitializeMutex는 항상 뮤텍스 개체의 초기 상태를 Signaled(이전 그림과 같이)로 설정하기 때문입니다.

  1. 뮤텍스 포인터를 사용하여 KeWaitForSingleObject에 대한 디스패치 루틴의 초기 호출은 현재 스레드를 즉시 준비 상태로 전환하고, 뮤텍스의 스레드 소유권을 부여하고, 뮤텍스 상태를 Not-Signaled로 다시 설정합니다. 디스패치 루틴 실행이 다시 시작되는 즉시 IRP를 뮤텍스로 보호된 큐에 안전하게 삽입할 수 있습니다.

  2. 두 번째 스레드(다른 디스패치 루틴, 드라이버 제공 작업자 스레드 콜백 루틴 또는 드라이버에서 만든 스레드)가 Mutex 포인터를 사용하여 KeWaitForSingleObject를 호출하면 두 번째 스레드가 대기 상태로 전환됩니다.

  3. 디스패치 루틴이 1단계에 설명된 대로 IRP 큐를 완료하면 뮤텍스 포인터와 부울 대기 값을 사용하여 KeReleaseMutex를 호출합니다. 이 값은 KeReleaseMutex가 컨트롤을 반환하는 즉시 뮤텍스를 사용하여 KeWaitForSingleObject(또는 KeWaitForMutexObject)를 호출할지 여부를 나타냅니다.

  4. 디스패치 루틴이 3단계(대기FALSE로 설정됨)에서 뮤텍스의 소유권을 해제했다고 가정하면 뮤텍스는 KeReleaseMutex에 의해 Signaled 상태로 설정됩니다. 뮤텍스에는 현재 소유자가 없으므로 커널은 다른 스레드가 해당 뮤텍스를 기다리고 있는지 여부를 결정합니다. 이 경우 커널은 두 번째 스레드(2단계 참조)를 뮤텍스 소유자로 만들고 스레드의 우선 순위를 가장 낮은 실시간 우선 순위 값으로 높이고 상태를 준비 상태로 변경합니다.

  5. 커널은 프로세서를 사용할 수 있는 즉시 실행을 위해 두 번째 스레드를 디스패치합니다. 즉, 우선 순위가 높은 다른 스레드가 현재 준비 상태이고 더 높은 IRQL에서 실행할 커널 모드 루틴이 없는 경우 입니다. 두 번째 스레드(IRP 또는 드라이버의 작업자 스레드 콜백 루틴 또는 IRP를 큐에서 해제하는 드라이버에서 만든 스레드를 큐에 대기하는 디스패치 루틴)는 이제 KeReleaseMutex를 호출할 때까지 뮤텍스로 보호되는 IRP 큐에 안전하게 액세스할 수 있습니다.

스레드가 뮤텍스 개체의 소유권을 재귀적으로 획득하는 경우 해당 스레드는 뮤텍스 개체를 Signaled 상태로 설정하기 위해 뮤텍스에서 대기한 횟수만큼 KeReleaseMutex 를 명시적으로 호출해야 합니다. 예를 들어 스레드가 동일한 Mutex 포인터를 사용하여 KeWaitForSingleObject를 호출한 다음 KeWaitForMutexObject를 호출하는 경우 해당 뮤텍스 개체를 Signaled 상태로 설정하기 위해 뮤텍스를 획득할 때 KeReleaseMutex를 두 번 호출해야 합니다.

Wait 매개 변수가 TRUE로 설정된 KeReleaseMutex를 호출하는 것은 KeReleaseMutex에서 반환할 때 KeWaitXxx 지원 루틴을 즉시 호출하려는 호출자의 의도를 나타냅니다.

Wait 매개 변수를 KeReleaseMutex로 설정하기 위한 다음 지침을 고려합니다.

IRQL PASSIVE_LEVEL 실행되는 페이지 가능한 스레드 또는 페이즈블 드라이버 루틴은 Wait 매개 변수가 TRUE로 설정된 KeReleaseMutex를 호출해서는 안 됩니다. 이러한 호출은 호출자가 KeReleaseMutexKeWaitXxx개체에 대한 호출 간에 페이징되는 경우 치명적인 페이지 오류를 발생합니다.

PASSIVE_LEVEL보다 큰 IRQL에서 실행되는 표준 드라이버 루틴은 시스템을 중단하지 않고 디스패처 개체에서 0이 아닌 간격을 기다릴 수 없습니다. 그러나 이러한 루틴은 DISPATCH_LEVEL 이하의 IRQL에서 실행되는 동안 뮤텍스를 소유하는 경우 KeReleaseMutex 를 호출할 수 있습니다.

표준 드라이버 루틴이 실행되는 IRQL에 대한 요약은 하드웨어 우선 순위 관리를 참조하세요.