Bagikan melalui


Menggunakan sinkronisasi otomatis

Hampir semua kode dalam driver berbasis kerangka kerja berada dalam fungsi panggilan balik peristiwa. Kerangka kerja secara otomatis menyinkronkan sebagian besar fungsi panggilan balik driver, sebagai berikut:

  • Kerangka kerja selalu menyinkronkan objek perangkat umum, objek perangkat fungsional (FDO), dan objek perangkat fisik (PDO) dengan fungsi panggil balik acara satu sama lain. Hanya salah satu fungsi panggilan balik yang dapat dipanggil pada satu waktu untuk setiap perangkat. Pengecualiannya adalah EvtDeviceSurpriseRemoval, EvtDeviceQueryRemove, dan EvtDeviceQueryStop. Fungsi panggilan balik ini mendukung Plug and Play (PnP) dan peristiwa pengelolaan daya serta dipanggil pada IRQL = PASSIVE_LEVEL.

  • Secara opsional, kerangka kerja dapat menyinkronkan eksekusi fungsi panggilan balik yang menangani permintaan I/O driver, sehingga fungsi panggilan balik ini berjalan satu per satu. Secara khusus, kerangka kerja dapat menyinkronkan fungsi panggilan balik untuk antrian, interupsi, panggilan prosedur yang ditangguhkan (DPC), timer, item kerja, dan objek file, bersama dengan fungsi panggilan balik EvtRequestCancel untuk objek permintaan. Kerangka kerja memanggil sebagian besar fungsi panggilan balik ini di IRQL = DISPATCH_LEVEL, tetapi Anda dapat memaksa fungsi panggilan balik objek antrean dan file berjalan di IRQL = PASSIVE_LEVEL. (Fungsi panggilan balik item kerja selalu berjalan pada PASSIVE_LEVEL.)

Kerangka kerja mengimplementasikan sinkronisasi otomatis ini dengan menggunakan serangkaian kunci sinkronisasi internal. Kerangka kerja memastikan bahwa dua utas atau lebih tidak dapat memanggil fungsi panggilan balik yang sama secara bersamaan. Setiap utas harus menunggu hingga dapat memperoleh kunci sinkronisasi sebelum memanggil fungsi panggilan balik. (Secara opsional, driver juga dapat memperoleh kunci sinkronisasi ini jika diperlukan. Untuk informasi selengkapnya, lihat Menggunakan Kunci Kerangka Kerja.)

Driver Anda harus menyimpan data khusus objek di ruang konteks objek. Jika driver Anda hanya menggunakan antarmuka yang ditentukan kerangka kerja, hanya fungsi panggilan balik yang menerima handel ke objek yang dapat mengakses data ini. Jika kerangka kerja menyinkronkan panggilan ke fungsi panggilan balik driver, hanya satu fungsi panggilan balik yang dipanggil pada satu waktu. Ruang konteks objek hanya dapat diakses oleh satu fungsi panggilan balik pada satu waktu.

Kecuali driver Anda menerapkan penanganan gangguan pada level pasif, kode yang melayani dan mengakses data gangguan harus berjalan di IRQL perangkat (DIRQL) dan memerlukan sinkronisasi tambahan. Untuk informasi selengkapnya, lihat Menyinkronkan Kode Interupsi.

Jika driver Anda mengaktifkan sinkronisasi otomatis fungsi panggilan balik yang menangani permintaan I/O, kerangka kerja menyinkronkan fungsi panggilan balik ini sehingga berjalan satu per satu. Tabel berikut mencantumkan fungsi panggilan balik yang disinkronkan oleh kerangka kerja.

Jenis objek Fungsi Panggilan Balik yang Disinkronkan

Objek antrean

Pengendali permintaan, EvtIoQueueState, EvtIoResume, EvtIoStop

Objek file

Semua fungsi panggilan balik

Objek permintaan

#B0 #A1 EvtRequestCancel #A2 #C3

Secara opsional, kerangka kerja juga dapat menyinkronkan fungsi-fungsi panggilan balik ini dengan fungsi panggilan balik objek interupsi, DPC, item kerja, dan timer yang disediakan oleh driver Anda untuk perangkat, kecuali untuk fungsi panggilan balik objek interupsi EvtInterruptIsr. Untuk mengaktifkan sinkronisasi tambahan ini, driver harus mengatur AutomaticSerialization anggota struktur konfigurasi objek ini ke TRUE.

Singkatnya, kemampuan sinkronisasi otomatis kerangka kerja menyediakan fitur berikut:

  • Kerangka kerja selalu menyinkronkan fungsi panggilan balik PnP dan manajemen daya setiap perangkat.

  • Secara opsional, kerangka kerja dapat menyinkronkan penangan permintaan antrean I/O, dan beberapa fungsi panggilan balik tambahan (lihat tabel sebelumnya).

  • Driver dapat meminta kerangka kerja untuk menyinkronkan fungsi panggilan balik untuk interupsi, DPC, item kerja, dan objek timer.

  • Pengemudi harus menyinkronkan kode yang melayani interupsi dan mengakses data interupsi dengan menggunakan teknik yang dijelaskan dalam Menyinkronkan Kode Interupsi.

  • Kerangka kerja tidak menyinkronkan fungsi panggilan balik driver lainnya, seperti fungsi panggilan balik driver yang disebut CompletionRoutine, atau fungsi panggilan balik yang ditentukan oleh objek target I/O. Sebaliknya, kerangka kerja menyediakan kunci tambahan yang dapat digunakan driver untuk menyinkronkan fungsi callback ini.

Memilih cakupan sinkronisasi

Anda dapat memilih agar kerangka kerja menyinkronkan semua fungsi panggilan balik yang terkait dengan semua antrean I/O perangkat. Atau, Anda dapat memilih agar kerangka kerja secara terpisah menyinkronkan fungsi panggilan balik untuk setiap antrean I/O perangkat. Opsi sinkronisasi yang tersedia untuk driver Anda adalah sebagai berikut:

  • Sinkronisasi tingkat perangkat

    Kerangka kerja menyinkronkan fungsi panggilan balik untuk semua antrean I/O perangkat, sehingga dijalankan satu per satu. Kerangka kerja mencapai sinkronisasi ini dengan memperoleh kunci sinkronisasi perangkat sebelum memanggil fungsi panggilan balik.

  • Sinkronisasi tingkat antrean

    Kerangka kerja menyinkronkan fungsi panggilan balik untuk setiap antrean I/O individu, sehingga dijalankan satu per satu. Kerangka kerja mencapai sinkronisasi ini dengan memperoleh kunci sinkronisasi antrean sebelum memanggil fungsi panggilan balik.

  • Tidak ada sinkronisasi

    Kerangka kerja tidak menyinkronkan eksekusi fungsi panggilan balik dalam tabel sebelumnya dan tidak memperoleh kunci sinkronisasi sebelum memanggil fungsi panggilan balik. Jika sinkronisasi diperlukan, driver harus menyediakannya.

Untuk menentukan apakah Anda ingin kerangka kerja menyediakan sinkronisasi tingkat perangkat, sinkronisasi tingkat antrean, atau tidak ada sinkronisasi untuk driver Anda, tentukan cakupan sinkronisasi untuk objek driver, objek perangkat, atau objek antrean Anda. SynchronizationScope anggota struktur WDF_OBJECT_ATTRIBUTES objek mengidentifikasi cakupan sinkronisasi objek. Nilai cakupan sinkronisasi yang dapat ditentukan driver Anda adalah:

WdfSynchronizationScopeDevice
Kerangka tersebut menyinkronkan dengan memperoleh kunci sinkronisasi dari objek perangkat.

WdfSynchronizationScopeQueue
Kerangka kerja disinkronkan dengan mendapatkan kunci sinkronisasi objek antrean.

WdfSynchronizationScopeNone
Kerangka kerja tidak disinkronkan dan tidak mendapatkan kunci sinkronisasi.

WdfSynchronizationScopeInheritFromParent
Kerangka kerja memperoleh nilai SynchronizationScope dari objek induknya.

Secara umum, hindari menggunakan sinkronisasi tingkat perangkat.

Untuk informasi selengkapnya tentang nilai cakupan sinkronisasi, lihat WDF_SYNCHRONIZATION_SCOPE.

Cakupan sinkronisasi default untuk objek driver adalah WdfSynchronizationScopeNone. Cakupan sinkronisasi default untuk objek perangkat dan antrean adalah WdfSynchronizationScopeInheritFromParent.

Untuk menggunakan kerangka kerja guna menyediakan sinkronisasi tingkat perangkat untuk semua perangkat, atur SynchronizationScope ke WdfSynchronizationScopeDevice dalam struktur WDF_OBJECT_ATTRIBUTES dari objek driver. Gunakan nilai WdfSynchronizationScopeInheritFromParent default untuk setiap objek perangkat.

Untuk menyediakan sinkronisasi tingkat perangkat untuk perangkat individual, gunakan nilai WdfSynchronizationScopeNone default untuk objek driver. Atur SynchronizationScope ke WdfSynchronizationScopeDevice dalam struktur WDF_OBJECT_ATTRIBUTES dari masing-masing objek perangkat.

Jika Anda ingin kerangka kerja menyediakan sinkronisasi tingkat antrean untuk perangkat, Anda dapat menggunakan teknik berikut:

  • Untuk versi kerangka kerja 1.9 dan yang lebih baru, aktifkan sinkronisasi pada tingkat antrean untuk masing-masing antrean dengan menyetel WdfSynchronizationScopeQueue dalam struktur WDF_OBJECT_ATTRIBUTES dari objek antrean. Teknik ini lebih disukai.

  • Atau, Anda dapat menggunakan langkah-langkah berikut di semua versi kerangka kerja:

    1. Setel SynchronizationScope ke WdfSynchronizationScopeQueue dalam struktur WDF_OBJECT_ATTRIBUTES dari objek perangkat.
    2. Gunakan nilai default WdfSynchronizationScopeInheritFromParent untuk setiap objek antrean perangkat.

Jika Anda tidak ingin framework menyinkronkan fungsi callback yang menangani permintaan I/O driver Anda, gunakan nilai default SynchronizationScope untuk objek driver, perangkat, dan antrean driver Anda. Dalam hal ini, kerangka kerja tidak secara otomatis menyinkronkan fungsi panggilan balik terkait permintaan I/O driver. Framework dapat memanggil fungsi panggilan balik pada IRQL <= DISPATCH_LEVEL.

Mengatur nilai SynchronizationScope hanya menyinkronkan fungsi panggilan balik yang terdapat dalam tabel sebelumnya. Jika Anda ingin kerangka kerja juga menyinkronkan fungsi panggilan balik penginterupsi, DPC, item kerja, dan objek timer driver, atur anggota AutomaticSerialization dari struktur konfigurasi objek-objek ini ke TRUE.

Namun, Anda dapat mengatur AutomaticSerialization ke TRUE hanya jika semua fungsi callback yang ingin Anda sinkronkan berjalan pada IRQL yang sama. Memilih tingkat eksekusi, yang akan dijelaskan berikutnya, dapat mengakibatkan tingkat IRQL yang tidak kompatibel. Dalam situasi seperti itu, driver harus menggunakan kunci kerangka kerja alih-alih mengatur AutomaticSerialization. Untuk informasi lebih lanjut tentang struktur konfigurasi untuk objek interupsi, DPC, item kerja, dan timer, serta informasi mengenai pembatasan yang berlaku untuk pengaturan AutomaticSerialization dalam struktur ini, lihat WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIG, dan WDF_TIMER_CONFIG.

Jika Anda mengatur AutomaticSerialization ke TRUE, pilih sinkronisasi tingkat antrean.

Memilih tingkat eksekusi

Saat driver membuat beberapa jenis objek kerangka kerja, ia dapat menentukan tingkat eksekusi untuk objek tersebut. Level eksekusi menentukan level IRQL di mana kerangka kerja memanggil fungsi balik kejadian objek yang menangani permintaan I/O dari driver.

Jika driver menyediakan tingkat eksekusi, tingkat yang disediakan memengaruhi fungsi panggilan balik untuk antrean dan objek file. Biasanya, jika driver menggunakan sinkronisasi otomatis, kerangka kerja memanggil fungsi panggilan balik ini di IRQL = DISPATCH_LEVEL. Dengan menentukan tingkat eksekusi, driver dapat memaksa kerangka kerja untuk memanggil fungsi panggilan balik ini di IRQL = PASSIVE_LEVEL. Kerangka kerja menggunakan aturan berikut saat mengatur IRQL di mana ia memanggil fungsi panggilan balik objek antrean dan file:

  • Jika driver menggunakan sinkronisasi otomatis, kerangka kerja memanggil fungsi panggilan balik antrean dan objek file di IRQL = DISPATCH_LEVEL kecuali driver meminta kerangka kerja untuk memanggil fungsi panggilan baliknya di IRQL = PASSIVE_LEVEL.

  • Jika driver tidak menggunakan sinkronisasi otomatis dan tidak menentukan tingkat eksekusi, kerangka kerja dapat memanggil fungsi antrean driver dan panggilan balik objek file di IRQL <= DISPATCH_LEVEL.

Jika driver Anda menyediakan fungsi panggilan balik objek file, Kemungkinan besar Anda ingin kerangka kerja memanggil fungsi panggilan balik ini di IRQL = PASSIVE_LEVEL karena beberapa data file, seperti nama file, dapat di-pageable.

Untuk menyediakan level eksekusi, driver Anda harus menentukan nilai untuk anggota ExecutionLevel dari struktur WDF_OBJECT_ATTRIBUTES objek. Nilai tingkat eksekusi yang dapat ditentukan driver Anda adalah:

WdfExecutionLevelPassive
Kerangka kerja memanggil fungsi panggilan balik objek di IRQL = PASSIVE_LEVEL.

WdfExecutionLevelDispatch
Kerangka dapat memanggil fungsi callback dari objek pada IRQL <= DISPATCH_LEVEL. Jika driver menggunakan sinkronisasi otomatis, kerangka kerja selalu memanggil fungsi panggilan balik di IRQL = DISPATCH_LEVEL.

WdfExecutionLevelInheritFromParent
Framework memperoleh nilai ExecutionLevel objek dari induknya.

Level eksekusi default untuk objek driver adalah WdfExecutionLevelDispatch. Tingkat eksekusi default untuk semua objek lainnya adalah WdfExecutionLevelInheritFromParent.

Untuk informasi lebih lanjut tentang nilai tingkat eksekusi, lihat WDF_EXECUTION_LEVEL.

Tabel berikut menunjukkan level IRQL yang digunakan oleh framework untuk memanggil fungsi callback driver pada objek antrean dan objek file.

Cakupan Sinkronisasi Tingkat Eksekusi IRQL dari Fungsi Antrean dan Panggilan Balik File

#B0 #C1 SinkronisasiRuangLingkupPerangkatWdf

#B0 WdfExecutionLevelPassive #C1

PASSIVE_LEVEL

#B0 #C1 SinkronisasiRuangLingkupPerangkatWdf

WdfExecutionLevelDispatch

Tingkat Pengiriman

WdfSynchronizationScopeQueue

#B0 WdfExecutionLevelPassive #C1

PASSIVE_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

Tingkat Pengiriman

#B0 WdfSynchronizationScopeNone #C1

#B0 WdfExecutionLevelPassive #C1

PASSIVE_LEVEL

#B0 WdfSynchronizationScopeNone #C1

WdfExecutionLevelDispatch

<= TINGKAT_PENGIRIMAN

Anda dapat mengatur tingkat eksekusi ke WdfExecutionLevelPassive atau WdfExecutionLevelDispatch untuk driver, perangkat, file, antrian, timer, dan objek umum. Untuk objek lain, Anda hanya dapat mengatur WdfExecutionLevelInheritFromParent.

Tentukan WdfExecutionLevelPassive jika:

  • Fungsi panggilan balik driver Anda harus memanggil metode kerangka kerja atau rutinitas Windows Driver Model (WDM) yang hanya dapat Anda panggil di IRQL = PASSIVE_LEVEL.

  • Fungsi panggilan balik driver Anda harus mengakses kode atau data yang dapat dihalaman. Misalnya, fungsi panggilan balik objek file biasanya mengakses data yang dapat di-pageable.

Daripada mengatur WdfExecutionLevelPassive, driver Anda dapat mengatur WdfExecutionLevelDispatch dan menyediakan fungsi panggilan balik yang membuat item kerja jika harus menangani beberapa operasi di IRQL = PASSIVE_LEVEL.

Sebelum Anda memutuskan apakah driver Anda harus mengatur tingkat eksekusi objek ke WdfExecutionLevelPassive, tentukan IRQL di mana driver Anda dan driver lain di tumpukan driver dipanggil. Pertimbangkan situasi berikut:

  • Jika driver Anda berada di puncak tumpukan driver mode kernel, sistem biasanya memanggil driver pada IRQL = PASSIVE_LEVEL. Klien driver tersebut mungkin merupakan driver berbasis UMDF atau aplikasi mode pengguna. Menentukan WdfExecutionLevelPassive tidak berdampak buruk pada performa driver, karena framework tidak perlu menjadwalkan panggilan driver Anda ke item kerja yang dipanggil di IRQL = PASSIVE_LEVEL.

  • Jika driver Anda tidak berada di bagian atas tumpukan, sistem kemungkinan tidak memanggil driver Anda di IRQL = PASSIVE_LEVEL. Oleh karena itu, kerangka kerja harus mengantrekan panggilan driver Anda ke item kerja, yang kemudian dipanggil di IRQL = PASSIVE_LEVEL. Proses ini dapat menyebabkan performa driver yang buruk, dibandingkan dengan memungkinkan fungsi callback driver Anda dipanggil pada IRQL >= DISPATCH_LEVEL.

Untuk objek DPC, dan untuk objek timer yang bukan merupakan timer tingkat pasif, Anda tidak dapat menetapkan anggota AutomaticSerialization dari struktur konfigurasi ke TRUE jika Anda menetapkan tingkat eksekusi perangkat induk ke WdfExecutionLevelPassive. Kerangka kerja memperoleh "callback synchronization locks" dari objek perangkat di IRQL = PASSIVE_LEVEL. Oleh karena itu, tidak dapat menggunakan kunci untuk menyinkronkan fungsi panggilan balik objek DPC atau timer, yang harus dijalankan di IRQL = DISPATCH_LEVEL. Dalam hal ini, driver Anda harus menggunakan kunci spin kerangka kerja dalam fungsi callback objek perangkat, DPC, atau timer apa pun yang harus disinkronkan dengan fungsi lainnya.

Perhatikan juga bahwa untuk objek timer yang mewakili timer pada level pasif, Anda dapat mengatur anggota AutomaticSerialization dalam struktur konfigurasi menjadi TRUE hanya jika level eksekusi perangkat induk diatur ke WdfExecutionLevelPassive.