Mengelola Prioritas Perangkat Keras
IRQL tempat rutinitas driver dijalankan menentukan rutinitas dukungan driver mode kernel mana yang dapat dipanggilnya. Misalnya, beberapa rutinitas dukungan driver mengharuskan pemanggil berjalan di IRQL = DISPATCH_LEVEL. Yang lain tidak dapat dipanggil dengan aman jika pemanggil berjalan di IRQL apa pun yang lebih tinggi dari PASSIVE_LEVEL.
Berikut ini adalah daftar IRQL di mana rutinitas driver standar yang paling umum diterapkan dipanggil. IRQL tercantum dari prioritas terendah hingga tertinggi.
PASSIVE_LEVEL
Mengganggu Masked Off — None.
Rutinitas Driver Dipanggil pada PASSIVE_LEVEL — DriverEntry, AddDevice, Reinitialize, Unload rutinitas, sebagian besar rutinitas pengiriman, utas yang dibuat driver, panggilan balik worker-thread.
APC_LEVEL
Mengganggu Masked Off — gangguan APC_LEVEL ditutupi.
Rutinitas Driver Dipanggil pada APC_LEVEL — Beberapa rutinitas pengiriman (lihat Mengirimkan Rutinitas dan RUNQL).
DISPATCH_LEVEL
Gangguan Masked Off — gangguan DISPATCH_LEVEL dan APC_LEVEL ditutupi. Gangguan perangkat, jam, dan kegagalan daya dapat terjadi.
Rutinitas Driver Dipanggil pada DISPATCH_LEVEL — StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (sambil menahan kunci putar batal), rutinitas DpcForIsr, CustomTimerDpc, CustomDpc .
DIRQL
Mengganggu Masked Off — Semua gangguan di IRQL <= DIRQL objek interupsi driver. Gangguan perangkat dengan nilai DIRQL yang lebih tinggi dapat terjadi, bersama dengan interupsi jam dan kegagalan daya.
Rutinitas Driver Dipanggil di DIRQL — Rutinitas InterruptService, SynchCritSection .
Satu-satunya perbedaan antara APC_LEVEL dan PASSIVE_LEVEL adalah bahwa proses yang dijalankan pada APC_LEVEL tidak bisa mendapatkan gangguan APC. Tetapi kedua IRQL menyiratkan konteks utas dan keduanya menyiratkan bahwa kode dapat di-page out.
Driver tingkat terendah memproses runtime integrasi saat berjalan di salah satu dari tiga IRQL:
PASSIVE_LEVEL, tanpa gangguan yang ditutupi pada prosesor, dalam rutinitas Pengiriman pengemudi
Rutinitas DriverEntry, AddDevice, Reinitialize, dan Unload juga dijalankan pada PASSIVE_LEVEL, seperti halnya utas sistem yang dibuat driver.
DISPATCH_LEVEL, dengan gangguan DISPATCH_LEVEL dan APC_LEVEL ditutupi pada prosesor, dalam rutinitas StartIo
AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (saat menahan kunci spin pembatalan), dan rutinitas CustomTimerDpc juga dijalankan pada DISPATCH_LEVEL, seperti halnya rutinitas DpcForIsr dan CustomDpc .
IRQL Perangkat (DIRQL), dengan semua gangguan kurang dari atau sama dengan SynchronizeIrql objek gangguan driver yang ditutupi pada prosesor, dalam rutinitas ISR dan SynchCritSection
Sebagian besar driver tingkat yang lebih tinggi memproses IRP saat berjalan di salah satu dari dua IRQL:
PASSIVE_LEVEL, tanpa gangguan ditutupi pada prosesor, dalam rutinitas pengiriman pengemudi
Rutinitas DriverEntry, Reinitialize, AddDevice, dan Unload juga dijalankan pada PASSIVE_LEVEL, seperti halnya utas sistem yang dibuat driver atau rutinitas panggilan balik alur pekerja atau driver sistem file.
DISPATCH_LEVEL, dengan DISPATCH_LEVEL dan APC_LEVEL mengganggu prosesor, dalam rutinitas IoCompletion pengemudi
Rutinitas IoTimer, Cancel, dan CustomTimerDpc juga dijalankan pada DISPATCH_LEVEL.
Dalam beberapa keadaan, driver perangkat penyimpanan massal tingkat menengah dan terendah dipanggil di IRQL APC_LEVEL. Secara khusus, ini dapat terjadi pada kesalahan halaman di mana driver sistem file mengirim permintaan IRP_MJ_READ ke driver yang lebih rendah.
Sebagian besar rutinitas driver standar dijalankan pada IRQL yang memungkinkan mereka hanya untuk memanggil rutinitas dukungan yang sesuai. Misalnya, driver perangkat harus memanggil AllocateAdapterChannel saat berjalan di IRQL DISPATCH_LEVEL. Karena sebagian besar driver perangkat memanggil rutinitas ini dari rutinitas StartIo , biasanya mereka sudah berjalan pada DISPATCH_LEVEL.
Perhatikan bahwa driver perangkat yang tidak memiliki rutinitas StartIo karena mengatur dan mengelola antrean runtime integrasinya sendiri belum tentu berjalan di DISPATCH_LEVEL IRQL ketika harus memanggil AllocateAdapterChannel. Driver seperti itu harus menumpuk panggilannya ke AllocateAdapterChannel antara panggilan ke KeRaiseIrql dan KeLowerIrql sehingga berjalan pada IRQL yang diperlukan ketika memanggil AllocateAdapterChannel dan memulihkan IRQL asli ketika panggilan rutin mendapatkan kembali kontrol.
Saat memanggil rutinitas dukungan driver, perhatikan hal-hal berikut.
Memanggil KeRaiseIrql dengan nilai input NewIrql yang kurang dari IRQL saat ini menyebabkan kesalahan fatal. Memanggil KeLowerIrql kecuali untuk memulihkan IRQL asli (yaitu, setelah panggilan ke KeRaiseIrql) juga menyebabkan kesalahan fatal.
Saat berjalan di IRQL >= DISPATCH_LEVEL, memanggil KeWaitForSingleObject atau KeWaitForMultipleObjects untuk objek dispatcher yang ditentukan kernel untuk menunggu interval nonzero menyebabkan kesalahan fatal.
Satu-satunya rutinitas driver yang dapat dengan aman menunggu peristiwa, semaphores, mutex, atau timer yang akan diatur ke status sinyal adalah yang berjalan dalam konteks utas nonarbitrer di IRQL PASSIVE_LEVEL, seperti utas yang dibuat driver, DriverEntry dan Menginisialisasi ulang rutinitas, atau mengirimkan rutinitas untuk operasi I/O yang sinkron secara inheren (seperti sebagian besar permintaan kontrol I/O perangkat).
Bahkan saat berjalan di IRQL PASSIVE_LEVEL, kode driver yang dapat di-pageable tidak boleh memanggil KeSetEvent, KeReleaseSemaphore, atau KeReleaseMutex dengan parameter Tunggu input diatur ke TRUE. Panggilan seperti itu dapat menyebabkan kesalahan halaman fatal.
Rutinitas apa pun yang berjalan lebih besar dari IRQL APC_LEVEL tidak dapat mengalokasikan memori dari kumpulan halaman atau mengakses memori di kumpulan halaman dengan aman. Jika rutinitas yang berjalan di IRQL lebih besar dari APC_LEVEL menyebabkan kesalahan halaman, itu adalah kesalahan fatal.
Driver harus berjalan di IRQL DISPATCH_LEVEL ketika memanggil KeAcquireSpinLockAtDpcLevel dan KeReleaseSpinLockFromDpcLevel.
Driver dapat berjalan di IRQL <= DISPATCH_LEVEL ketika memanggil KeAcquireSpinLock tetapi harus melepaskan kunci spin tersebut dengan memanggil KeReleaseSpinLock. Dengan kata lain, ini adalah kesalahan pemrograman untuk melepaskan kunci putar yang diperoleh dengan KeAcquireSpinLock dengan memanggil KeReleaseSpinLockFromDpcLevel.
Driver tidak boleh memanggil KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock, atau KeReleaseSpinLock saat berjalan di IRQL > DISPATCH_LEVEL.
Memanggil rutinitas dukungan yang menggunakan kunci putar, seperti rutinitas Xxx exInterlocked, meningkatkan IRQL pada prosesor saat ini baik untuk DISPATCH_LEVEL atau ke DIRQL jika pemanggil belum berjalan pada IRQL yang dinaikkan.
Kode driver yang berjalan di IRQL > PASSIVE_LEVEL harus dijalankan secepat mungkin. Semakin tinggi IRQL di mana rutin berjalan, semakin penting bagi performa keseluruhan yang baik untuk menyetel rutinitas itu untuk dijalankan secepat mungkin. Misalnya, setiap driver yang memanggil KeRaiseIrql harus melakukan panggilan timbal balik ke KeLowerIrql sesegera mungkin.
Untuk informasi selengkapnya tentang menentukan prioritas, lihat laporan resmi Penjadwalan, Konteks Utas, dan IRQL .