일단 함수 이름을 보면 디바이스 함수들이 무엇을 하는지 알 수 있다.
간단하게 잘라서 보면, 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
);
- 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.
Parameters
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
);
- 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.
Parameters
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
);
- SpinLock
- Pointer to an initialized spin lock for which the caller must provide the storage.
Parameters
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
여기서 이 구문을
보자.
for better driver performance if and only
if they are already running at IRQL = DISPATCH_LEVEL.
확실히
이해가 가는 부분이다. 그리고 나머지 내용을 보면 IRQL이 APC레벨 보다 작을 경우에는
KeAcquireSpinLock()을
쓰라고 권장하고 있다. KeAcquireSpinLockAtDpcLevel() 같은 경우에는
DISPATCH_LEVEL에서 사용되고
있다고 가정하고 있다고 한다.
사용법은 동일하다. 잠궈주고 풀어주고, 다음에는 변화된 디바이스 루틴을 확인해보자.