자동 동기화 사용

프레임워크 기반 드라이버의 거의 모든 코드는 이벤트 콜백 함수에 상주합니다. 프레임워크는 다음과 같이 대부분의 드라이버 콜백 함수를 자동으로 동기화합니다.

프레임워크는 내부 동기화 잠금 집합을 사용하여 이 자동 동기화를 구현합니다. 프레임워크는 두 개 이상의 스레드가 동일한 콜백 함수를 동시에 호출할 수 없도록 합니다. 각 스레드는 콜백 함수를 호출하기 전에 동기화 잠금을 획득할 때까지 기다려야 하기 때문입니다. (필요에 따라 드라이버는 필요한 경우 이러한 동기화 잠금을 획득할 수도 있습니다. 자세한 내용은 프레임워크 잠금 사용을 참조하세요.)

드라이버는 개체 관련 데이터를 개체 컨텍스트 공간에 저장해야 합니다. 드라이버가 프레임워크 정의 인터페이스만 사용하는 경우 개체에 대한 핸들을 받는 콜백 함수만 이 데이터에 액세스할 수 있습니다. 프레임워크가 드라이버의 콜백 함수에 대한 호출을 동기화하는 경우 한 번에 하나의 콜백 함수만 호출되고 개체의 컨텍스트 공간은 한 번에 하나의 콜백 함수에만 액세스할 수 있습니다.

드라이버가 수동 수준 인터럽트 처리를 구현하지 않는 한 서비스가 인터럽트 및 인터럽트 데이터에 액세스하는 코드는 디바이스의 IRQL(DIRQL)에서 실행되어야 하며 추가 동기화가 필요합니다. 자세한 내용은 인터럽트 코드 동기화를 참조하세요.

드라이버가 I/O 요청을 처리하는 콜백 함수의 자동 동기화를 사용하도록 설정하는 경우 프레임워크는 이러한 콜백 함수를 한 번에 하나씩 실행하도록 동기화합니다. 다음 표에서는 프레임워크가 동기화하는 콜백 함수를 나열합니다.

개체 유형 동기화된 콜백 함수

Queue 개체

요청 처리기, EvtIoQueueState, EvtIoResume, EvtIoStop

File 개체

모든 콜백 함수

요청 개체

EvtRequestCancel

필요에 따라 프레임워크는 드라이버가 디바이스에 제공하는 인터럽트, DPC, 작업 항목 및 타이머 개체 콜백 함수와 이러한 콜백 함수를 동기화할 수도 있습니다(인터럽트 개체의 EvtInterruptIsr 콜백 함수 제외). 이 추가 동기화를 사용하도록 설정하려면 드라이버가 이러한 개체 구성 구조의 AutomaticSerialization 멤버를 TRUE로 설정해야 합니다.

요약하면 프레임워크의 자동 동기화 기능은 다음과 같은 기능을 제공합니다.

  • 프레임워크는 항상 각 디바이스의 PnP 및 전원 관리 콜백 함수를 동기화합니다.

  • 필요에 따라 프레임워크는 I/O 큐의 요청 처리기와 몇 가지 추가 콜백 함수를 동기화할 수 있습니다(이전 표 참조).

  • 드라이버는 프레임워크에 인터럽트, DPC, 작업 항목 및 타이머 개체에 대한 콜백 함수를 동기화하도록 요청할 수 있습니다.

  • 드라이버는 인터럽트 코드 동기화에 설명된 기술을 사용하여 서비스가 인터 럽트 데이터를 인터럽트하고 액세스하는 코드를 동기화해야 합니다.

  • 프레임워크는 드라이버의 CompletionRoutine 콜백 함수 또는 I/O 대상 개체가 정의하는 콜백 함수와 같은 드라이버의 다른 콜백 함수를 동기화하지 않습니다. 대신 프레임워크는 드라이버가 이러한 콜백 함수를 동기화하는 데 사용할 수 있는 추가 잠금 을 제공합니다.

동기화 범위 선택

프레임워크가 디바이스의 모든 I/O 큐와 연결된 모든 콜백 함수를 동기화하도록 선택할 수 있습니다. 또는 프레임워크가 각 디바이스의 I/O 큐에 대한 콜백 함수를 별도로 동기화하도록 선택할 수 있습니다. 드라이버에서 사용할 수 있는 동기화 옵션은 다음과 같습니다.

  • 디바이스 수준 동기화

    프레임워크는 디바이스의 모든 I/O 큐에 대해 이전 테이블에 포함된 콜백 함수를 동기화하여 한 번에 하나씩 실행합니다. 프레임워크는 콜백 함수를 호출하기 전에 디바이스의 동기화 잠금을 획득하여 이 동기화를 수행합니다.

  • 큐 수준 동기화

    프레임워크는 각 개별 I/O 큐에 대해 이전 테이블에 포함된 콜백 함수를 동기화하여 한 번에 하나씩 실행합니다. 프레임워크는 콜백 함수를 호출하기 전에 큐의 동기화 잠금을 획득하여 이 동기화를 수행합니다.

  • 동기화 없음

    프레임워크는 이전 테이블에 포함된 콜백 함수의 실행을 동기화하지 않으며 콜백 함수를 호출하기 전에 동기화 잠금을 획득하지 않습니다. 동기화가 필요한 경우 드라이버에서 제공해야 합니다.

프레임워크에서 디바이스 수준 동기화, 큐 수준 동기화 또는 드라이버에 대한 동기화를 제공할지 여부를 지정하려면 드라이버 개체, 디바이스 개체 또는 큐 개체에 대한 동기화 scope 지정할 수 있습니다. 개체의 WDF_OBJECT_ATTRIBUTES 구조체의 SynchronizationScope 멤버는 개체의 동기화 scope 식별합니다. 드라이버에서 지정할 수 있는 동기화 scope 값은 다음과 같습니다.

WdfSynchronizationScopeDevice
프레임워크는 디바이스 개체의 동기화 잠금을 가져와 동기화합니다.

WdfSynchronizationScopeQueue
프레임워크는 큐 개체의 동기화 잠금을 가져와 동기화합니다.

WdfSynchronizationScopeNone
프레임워크는 동기화되지 않으며 동기화 잠금을 가져오지 않습니다.

WdfSynchronizationScopeInheritFromParent
프레임워크는 개체의 부모 개체에서 개체의 SynchronizationScope 값을 가져옵니다.

일반적으로 디바이스 수준 동기화를 사용하지 않는 것이 좋습니다.

동기화 scope 값에 대한 자세한 내용은 WDF_SYNCHRONIZATION_SCOPE 참조하세요.

드라이버 개체에 대한 기본 동기화 scope WdfSynchronizationScopeNone입니다. 디바이스 및 큐 개체에 대한 기본 동기화 scope WdfSynchronizationScopeInheritFromParent입니다.

프레임워크가 모든 디바이스에 디바이스 수준 동기화를 제공하려면 다음 단계를 사용할 수 있습니다.

  1. 드라이버의 드라이버 개체의 WDF_OBJECT_ATTRIBUTES 구조에서 SynchronizationScopeWdfSynchronizationScopeDevice로 설정합니다.

  2. 디바이스 개체에 대해 기본 WdfSynchronizationScopeInheritFromParent 값을 사용합니다.

또는 개별 디바이스에 대한 디바이스 수준 동기화를 제공하려면 다음 단계를 사용할 수 있습니다.

  1. 드라이버 개체에 대한 기본 WdfSynchronizationScopeNone 값을 사용합니다.

  2. 개별 디바이스 개체의 WDF_OBJECT_ATTRIBUTES 구조에서 SynchronizationScopeWdfSynchronizationScopeDevice로 설정합니다.

프레임워크가 디바이스에 대한 큐 수준 동기화를 제공하려는 경우 다음 기술을 사용할 수 있습니다.

  • 프레임워크 버전 1.9 이상에서는 큐 개체의 WDF_OBJECT_ATTRIBUTES 구조에서 WdfSynchronizationScopeQueue를 설정하여 개별 큐에 대해 큐 수준 동기화를 사용하도록 설정해야 합니다. 이것이 기본 설정 기술입니다.

  • 또는 모든 프레임워크 버전에서 다음 단계를 사용할 수 있습니다.

    1. 디바이스 개체의 WDF_OBJECT_ATTRIBUTES 구조에서 SynchronizationScopeWdfSynchronizationScopeQueue로 설정합니다.
    2. 각 디바이스의 개체에 대해 기본 WdfSynchronizationScopeInheritFromParent 값을 사용합니다.

프레임워크가 드라이버의 I/O 요청을 처리하는 콜백 함수를 동기화하지 않으려면 드라이버의 드라이버, 디바이스 및 큐 개체에 대한 기본 SynchronizationScope 값을 사용합니다. 이 경우 프레임워크는 드라이버의 I/O 요청 관련 콜백 함수를 자동으로 동기화하지 않으며 콜백 함수는 IRQL <= DISPATCH_LEVEL 호출할 수 있습니다.

SynchronizationScope 값을 설정하면 이전 테이블에 포함된 콜백 함수만 동기화됩니다. 프레임워크가 드라이버의 인터럽트, DPC, 작업 항목 및 타이머 개체 콜백 함수를 동기화하려면 드라이버가 이러한 개체 구성 구조의 AutomaticSerialization 멤버를 TRUE로 설정해야 합니다.

그러나 동기화하려는 모든 콜백 함수가 동일한 IRQL에서 실행되는 경우에만 AutomaticSerializationTRUE 로 설정할 수 있습니다. 다음에 설명된 실행 수준을 선택하면 호환되지 않는 IRQL 수준이 발생할 수 있습니다. 이러한 상황에서 드라이버는 AutomaticSerialization을 설정하는 대신 프레임워크 잠금을 사용해야 합니다. 인터럽트, DPC, 작업 항목 및 타이머 개체의 구성 구조에 대한 자세한 내용과 이러한 구조에서 AutomaticSerialization 설정에 적용되는 제한 사항에 대한 자세한 내용은 WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIGWDF_TIMER_CONFIG.

AutomaticSerializationTRUE로 설정하면 큐 수준 동기화를 선택해야 합니다.

실행 수준 선택

드라이버는 프레임워크 개체의 일부 형식을 만들 때 개체에 대한 실행 수준을 지정할 수 있습니다. 실행 수준은 프레임워크가 드라이버의 I/O 요청을 처리하는 개체의 이벤트 콜백 함수를 호출할 IRQL을 지정합니다.

드라이버가 실행 수준을 제공하는 경우 제공된 수준은 큐 및 파일 개체에 대한 콜백 함수에 영향을 줍니다. 일반적으로 드라이버가 자동 동기화를 사용하는 경우 프레임워크는 IRQL = DISPATCH_LEVEL 이러한 콜백 함수를 호출합니다. 실행 수준을 지정하여 드라이버는 프레임워크가 IRQL = PASSIVE_LEVEL 이러한 콜백 함수를 호출하도록 강제할 수 있습니다. 프레임워크는 큐 및 파일 개체 콜백 함수가 호출되는 IRQL을 설정할 때 다음 규칙을 사용합니다.

  • 드라이버가 자동 동기화를 사용하는 경우 드라이버가 IRQL = PASSIVE_LEVEL 콜백 함수를 호출하도록 프레임워크에 요청하지 않는 한 해당 큐 및 파일 개체 콜백 함수는 IRQL = DISPATCH_LEVEL 호출됩니다.

  • 드라이버가 자동 동기화를 사용하지 않고 실행 수준을 지정하지 않으면 IRQL <= DISPATCH_LEVEL 드라이버의 큐 및 파일 개체 콜백 함수를 호출할 수 있습니다.

드라이버에서 파일 개체 콜백 함수를 제공하는 경우 파일 이름과 같은 일부 파일 데이터를 페이지할 수 있으므로 프레임워크가 IRQL = PASSIVE_LEVEL 이러한 콜백 함수를 호출하려고 할 가능성이 큽니다.

실행 수준을 제공하려면 드라이버가 개체의 WDF_OBJECT_ATTRIBUTES 구조체의 ExecutionLevel 멤버에 대한 값을 지정해야 합니다. 드라이버에서 지정할 수 있는 실행 수준 값은 다음과 같습니다.

WdfExecutionLevelPassive
프레임워크는 IRQL = PASSIVE_LEVEL 개체의 콜백 함수를 호출합니다.

WdfExecutionLevelDispatch
프레임워크는 IRQL <= DISPATCH_LEVEL 개체의 콜백 함수를 호출할 수 있습니다. (드라이버가 자동 동기화를 사용하는 경우 프레임워크는 항상 IRQL = DISPATCH_LEVEL 콜백 함수를 호출합니다.)

WdfExecutionLevelInheritFromParent
프레임워크는 개체의 부모로부터 개체의 ExecutionLevel 값을 가져옵니다.

드라이버 개체의 기본 실행 수준은 WdfExecutionLevelDispatch입니다. 다른 모든 개체의 기본 실행 수준은 WdfExecutionLevelInheritFromParent입니다.

실행 수준 값에 대한 자세한 내용은 WDF_EXECUTION_LEVEL 참조하세요.

다음 표에서는 프레임워크가 큐 개체 및 파일 개체에 대한 드라이버의 콜백 함수를 호출할 수 있는 IRQL 수준을 보여 줍니다.

동기화 범위 실행 수준 큐 및 파일 콜백 함수의 IRQL

WdfSynchronizationScopeDevice

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelDispatch

<= DISPATCH_LEVEL

드라이버, 디바이스, 파일, 큐, 타이머 및 일반 개체에 대해 실행 수준을 WdfExecutionLevelPassive 또는 WdfExecutionLevelDispatch 로 설정할 수 있습니다. 다른 개체의 경우 WdfExecutionLevelInheritFromParent 만 허용됩니다.

다음과 같은 경우 WdfExecutionLevelPassive 를 지정해야 합니다.

  • 드라이버의 콜백 함수는 IRQL = PASSIVE_LEVEL 호출할 수 있는 프레임워크 메서드 또는 WDM(Windows 드라이버 모델) 루틴을 호출해야 합니다.

  • 드라이버의 콜백 함수는 페이지 가능한 코드 또는 데이터에 액세스해야 합니다. (예를 들어 파일 개체 콜백 함수는 일반적으로 페이지가 지정 가능한 데이터에 액세스합니다.)

드라이버는 WdfExecutionLevelPassive를 설정하는 대신 WdfExecutionLevelDispatch 를 설정하고 IRQL = PASSIVE_LEVEL 일부 작업을 처리해야 하는 경우 작업 항목을 만드는 콜백 함수를 제공할 수 있습니다.

드라이버가 개체의 실행 수준을 WdfExecutionLevelPassive로 설정해야 하는지 여부를 결정하기 전에 드라이버 및 드라이버 스택의 다른 드라이버가 호출되는 IRQL을 결정해야 합니다. 다음 상황을 고려합니다.

  • 드라이버가 커널 모드 드라이버 스택의 맨 위에 있는 경우 시스템은 일반적으로 IRQL = PASSIVE_LEVEL 드라이버를 호출합니다. 이러한 드라이버의 클라이언트는 UMDF 기반 드라이버 또는 사용자 모드 애플리케이션일 수 있습니다. WdfExecutionLevelPassive를 지정해도 드라이버 성능에 부정적인 영향을 주지 않습니다. 프레임워크가 IRQL = PASSIVE_LEVEL 호출되는 작업 항목에 대한 드라이버 호출을 큐에 대기할 필요가 없기 때문입니다.

  • 드라이버가 스택의 맨 위에 있지 않으면 시스템에서 IRQL = PASSIVE_LEVEL 드라이버를 호출하지 않을 수 있습니다. 따라서 프레임워크는 나중에 IRQL = PASSIVE_LEVEL 호출되는 작업 항목에 대한 드라이버 호출을 큐에 대기해야 합니다. 이 프로세스는 드라이버의 콜백 함수를 IRQL <= DISPATCH_LEVEL 호출할 수 있도록 허용하는 것에 비해 드라이버 성능이 저하될 수 있습니다.

DPC 개체 및 수동 수준 타이머를 나타내지 않는 타이머 개체의 경우 부모 디바이스의 실행 수준을 WdfExecutionLevelPassive로 설정한 경우 구성 구조의 AutomaticSerialization 멤버를 TRUE로 설정할 수 없습니다. 이는 프레임워크가 IRQL = PASSIVE_LEVEL 디바이스 개체의 콜백 동기화 잠금 을 획득하므로 잠금을 사용하여 IRQL = DISPATCH_LEVEL 실행해야 하는 DPC 또는 타이머 개체 콜백 함수를 동기화할 수 없기 때문입니다. 이러한 경우 드라이버는 서로 동기화해야 하는 모든 디바이스, DPC 또는 타이머 개체 콜백 함수에서 프레임워크 스핀 잠금 을 사용해야 합니다.

또한 수동 수준 타이머를 나타내는 타이머 개체의 경우 부모 디바이스의 실행 수준이 WdfExecutionLevelPassive로 설정된 경우에만 구성 구조의 AutomaticSerialization 멤버를 TRUE로 설정할 수 있습니다.