[DDK] IoStartPacket(), KeAcquireSpinLock() 함수 사용법

앞서 말한 동기화 문제 와 StartPacket() 함수 처리 방법을 알아보자,

일단 함수 이름을 보면 디바이스 함수들이 무엇을 하는지 알 수 있다.

간단하게 잘라서 보면, Io 로 시작하는것은 I/O Manager가 관리하는 함수이다.

그리고 Ke 는 Kernel Execute 이다. 이렇게 보면 함수 이름은 어절 그대로 해석하면,

정확한 답을 얻을 수 있다.

동기화 문제가 디바이스 커널 단에서는 더욱 심각하다. 어길 시에는 BSOD를 볼 것이다.

멀티 프로세서 시스템의 경우에 DISPATCH_LEVEL의 IRQL에서 구동되는 StartIo 루틴이

2개의 프로세서에서 동시에 실행되게되면 대부분의 하드웨어 디바이스는 일련의

포트/레지스터 입출력 작업시 재진입이 불가능해진다.

 아니면, 2개의 StartIo 루틴( Arbitrary Thread에서 동시 실행 시가 될 수 있겠다. )이 동시에

같은 자원의 Context를 변경할 수 있게 된다.

 이제 IoStartPacket() 의 사용법을 보자.


IoStartPacket

The IoStartPacket routine calls the driver's StartIo routine with the given IRP or inserts the IRP into the device queue associated with the given device object if the device is already busy.

VOID
  IoStartPacket(
    IN PDEVICE_OBJECT  DeviceObject,
    IN PIRP  Irp,
    IN PULONG  Key  OPTIONAL,
    IN PDRIVER_CANCEL  CancelFunction  OPTIONAL
    );

Parameters

DeviceObject
Pointer to the target device object for the IRP.
Irp
Pointer to the IRP to be processed.
Key
Pointer to a value that determines where to insert the packet into the device queue. If this is zero, the packet is inserted at the tail of the device queue.
CancelFunction
Specifies the entry point for a driver-supplied Cancel routine.

Return Value

None

그렇다. 그리고 동기화 문제를 해결하기 위해서는 Semaphore와 같은 작업을 해줘야한다.

그때 사용하는 함수가 KeAcquireSpinLock() 함수이다.

KeAcquireSpinLock

The KeAcquireSpinLock routine acquires a spin lock so the caller can synchronize access to shared data in a multiprocessor-safe way by raising IRQL.

VOID
  KeAcquireSpinLock(
    IN PKSPIN_LOCK  SpinLock,
    OUT PKIRQL  OldIrql
    );

Parameters

SpinLock
Pointer to an initialized spin lock for which the caller provides the storage.
OldIrql
Pointer to a variable that is set to the current IRQL when this call occurs.

Return Value

None

그리고 DISPATCH_LEVEL에서 돌고있는 것이 확실한 루틴( StartIo 같은 루틴 )이 실행될 경우에는

다른 함수를 실행할 수 있다.

KeAcquireSpinLockAtDpcLevel

The KeAcquireSpinLockAtDpcLevel routine acquires a spin lock when the caller is already running at IRQL = DISPATCH_LEVEL.

VOID
  KeAcquireSpinLockAtDpcLevel(
    IN PKSPIN_LOCK  SpinLock
    );

Parameters

SpinLock
Pointer to an initialized spin lock for which the caller must provide the storage.

Return Value

None

이 함수를 실행하게 되면, 리소스의 낭비가 줄어들게 된다.

그리고 함수의 설명을 주의깊게 볼 필요가 있다.

Comments

Drivers call KeAcquireSpinLockAtDpcLevel instead of KeAcquireSpinLock for better driver performance if and only if they are already running at IRQL = DISPATCH_LEVEL.

If a driver is running at IRQL <= APC_LEVEL, it should call KeAcquireSpinLock to have IRQL raised by that routine. KeAcquireSpinLockAtDpcLevel assumes the caller is already running at IRQL = DISPATCH_LEVEL, so no raise is necessary.

The caller should release the spin lock with KeReleaseSpinLockFromDpcLevel as quickly as possible.

For more information about spin locks, see Spin Locks.

여기서 이 구문을 보자.

for better driver performance if and only if they are already running at IRQL = DISPATCH_LEVEL.

확실히 이해가 가는 부분이다. 그리고 나머지 내용을 보면 IRQL이 APC레벨 보다 작을 경우에는

KeAcquireSpinLock()을 쓰라고 권장하고 있다. KeAcquireSpinLockAtDpcLevel() 같은 경우에는

DISPATCH_LEVEL에서 사용되고 있다고 가정하고 있다고 한다.

사용법은 동일하다. 잠궈주고 풀어주고, 다음에는 변화된 디바이스 루틴을 확인해보자.

 

출처 : http://ssmhz.tistory.com

위로 스크롤