다음을 통해 공유


디바이스 큐 관리

The I/O manager usually (except for FSDs) creates an associated device queue object when a driver calls IoCreateDevice. It also provides IoStartPacket and IoStartNextPacket, which drivers can call to have the I/O manager insert IRPs into the associated device queue or call their StartIo routines.

따라서 드라이버가 IRP에 대한 자체 디바이스 큐 개체를 설정하는 것은 거의 필요하거나 유용하지 않습니다. 가능성이 있는 후보는 단일 컨트롤러 또는 버스 어댑터를 통해 서비스되는 다른 유형의 디바이스에 대해 몇 가지 밀접하게 결합된 클래스 드라이버에서 들어오는 IRP를 조정해야 하는 드라이버입니다. 이러한 드라이버의 예로 SCSI 포트 드라이버가 있습니다.

즉, 디스크 배열 컨트롤러용 드라이버는 추가 디바이스 큐 개체를 설정하는 것보다 드라이버에서 만든 컨트롤러 개체를 사용할 가능성이 더 높으며, 추가 기능 버스 어댑터 및 클래스 드라이버 집합의 드라이버는 보조 디바이스 큐를 사용할 가능성이 약간 더 높습니다.

추가적인 디바이스 큐를 StartIo 루틴과 함께 사용하는 것

By calling IoStartPacket and IoStartNextPacket, a driver's Dispatch and DpcForIsr (or CustomDpc) routines synchronize calls to its StartIo routine using the device queue that the I/O manager created when the driver created the device object. For a port driver with a StartIo routine, IoStartPacket and IoStartNextPacket insert and remove IRPs in the device queue for the port driver's shared device controller/adapter. If the port driver also sets up supplemental device queues to hold requests coming in from closely coupled higher-level class drivers, it must "sort" incoming IRPs into its supplemental device queues, usually in its StartIo routine.

포트 드라이버는 해당 IRP를 적절한 큐에 삽입하기 전에 각 IRP가 속한 추가 디바이스 큐를 결정해야 합니다. 대상 디바이스 개체에 대한 포인터는 IRP와 함께 드라이버의 디스패치 루틴에 전달됩니다. 드라이버는 들어오는 IRP를 "정렬"하는 데 사용할 포인터를 저장해야 합니다. The device object pointer passed to the StartIo routine is the driver's own device object, which represents the device controller/adapter, so it can't be used for this purpose.

드라이버가 IRP를 큐에 넣은 후 공유 컨트롤러/어댑터를 프로그래밍하여 요청을 수행합니다. Thus, the port driver can process incoming requests for all devices on a first-come, first-served basis until a call to KeInsertDeviceQueue puts an IRP into a particular class driver's device queue.

By using its own device queue for all IRPs to be processed through its StartIo routine, the underlying port driver serializes operations through the shared device (or bus) controller/adapter to all attached devices. 지원되는 각 디바이스에 대한 IRP를 별도의 디바이스 큐에 보관하는 경우 이 포트 드라이버는 공유 하드웨어를 통해 I/O를 수행하는 다른 모든 디바이스에 대한 I/O 처리량을 늘리면서 이미 사용 중인 디바이스에 대한 IRP 처리를 억제합니다.

In response to the call to IoStartPacket from the port driver's Dispatch routine, the I/O manager either calls that driver's StartIo routine immediately or puts the IRP into the device queue associated with the device object for the port driver's shared controller/adapter.

포트 드라이버는 공유 디바이스 컨트롤러/어댑터를 통해 서비스하는 다른 유형의 각 디바이스에 대한 자체 상태 정보를 유지해야 합니다.

보조 디바이스 큐를 사용하여 클래스/포트 드라이버를 디자인할 때 다음 사항에 유의하세요.

  • 드라이버는 디바이스 스택의 맨 위에 있는 디바이스 개체를 제외하고 자체 위에 계층화된 드라이버에서 만든 디바이스 개체에 대한 포인터를 쉽게 가져올 수 없습니다.

    기본적으로 I/O 관리자는 이러한 포인터를 가져오기 위한 지원 루틴을 제공하지 않습니다. 또한 드라이버가 로드되는 순서를 사용하면 하위 드라이버가 상위 수준 드라이버의 디바이스 개체에 대한 포인터를 가져올 수 없으며, 하위 수준 드라이버가 디바이스를 추가할 때 아직 생성되지 않습니다.

    Although IoGetAttachedDeviceReference returns a pointer to the highest-level device object in a driver's stack, a driver should use this pointer only to designate a target for I/O requests to its stack. 드라이버는 디바이스 개체를 읽거나 쓰려고 시도해서는 안 됩니다.

  • 드라이버는 자체 디바이스 스택의 맨 위에 요청을 보내는 것을 제외하고는 자체 위에 계층화된 드라이버에서 만든 디바이스 개체에 대한 포인터를 사용할 수 없습니다.

    다중 프로세서로부터 안전한 방식으로 두 드라이버 간에 단일 디바이스 개체(및 해당 디바이스 확장)에 대한 액세스를 동기화할 수 있는 방법은 없습니다. 두 드라이버 모두 다른 드라이버가 현재 수행하는 I/O 처리에 대해 어떠한 가정도 할 수 없습니다.

Even for closely coupled class/port drivers, each class driver should use the pointer to the port driver's device objects only to pass on IRPs using IoCallDriver. 기본 포트 드라이버는 밀접하게 결합된 클래스 드라이버의 디바이스에 대해 처리하는 요청에 대해 포트 드라이버의 디바이스 확장에서 자체 상태를 유지해야 합니다.

드라이버 루틴을 통한 보조 디바이스 큐 관리

클래스 드라이버가 밀접하게 결합된 집합의 보조 디바이스 큐에 IRP를 대기시키는 모든 포트 드라이버는 다음과 같은 상황을 효율적으로 처리해야 합니다.

  1. 디스패치 루틴은 해당 디바이스에 대해 드라이버에서 만든 디바이스 큐에 특정 디바이스에 대한 IRP를 삽입합니다.

  2. IRPs for other devices continue to come in, to be queued to the driver's StartIo routine with IoStartPacket, and to be processed through the shared device controller.

  3. The device controller doesn't become idle, but each IRP held in the driver-created device queue also must be queued to the driver's StartIo routine as soon as possible.

So, the port driver's DpcForIsr routine must attempt to transfer an IRP from the driver's internal device queue for a particular device into the device queue for the shared adapter/controller whenever the port driver completes an IRP, as follows:

  1. The DpcForIsr routine calls IoStartNextPacket to have the StartIo routine begin processing the next IRP queued to the shared device controller.

  2. The DpcForIsr routine calls KeRemoveDeviceQueue to dequeue the next IRP (if any) that it's holding in its internal device queue for the device on whose behalf it's about to complete an IRP.

  3. If KeRemoveDeviceQueue returns a non-NULL pointer, the DpcForIsr routine calls IoStartPacket with the dequeued IRP to have it queued to the shared device controller/adapter. Otherwise, the call to KeRemoveDeviceQueue simply resets the state of the device queue object to Not-Busy, and the DpcForIsr routine omits the call to IoStartPacket.

  4. Then, the DpcForIsr routine calls IoCompleteRequest with the input IRP for which the port driver has just completed I/O processing, either by setting the I/O status block with an error or by satisfying the I/O request.

The preceding sequence implies that the DpcForIsr routine also must determine the device for which it's completing the current (input) IRP in order to manage internal queuing of IRPs efficiently.

포트 드라이버가 보조 장치 큐에 보관된 IRP를 제거하기 전에 공유된 컨트롤러/어댑터가 유휴 상태가 될 때까지 기다리려 하면, I/O 수요가 높은 장치에는 필요한 서비스를 충분히 제공하지 못할 수 있으며, 반면에 현재 I/O 수요가 실제로 훨씬 적은 모든 다른 장치는 즉시 처리될 수 있습니다.