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 fungsi panggilan balik objek perangkat umum, objek perangkat fungsional (FDO), dan objek perangkat fisik (PDO) satu sama lain sehingga hanya satu fungsi panggilan balik (kecuali EvtDeviceSurpriseRemoval, EvtDeviceQueryRemove, dan EvtDeviceQueryStop) yang dapat dipanggil pada satu waktu untuk setiap perangkat. Fungsi panggilan balik ini mendukung Plug and Play (PnP) dan peristiwa manajemen daya dan dipanggil di 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 antrean, interupsi, panggilan prosedur yang ditangguhkan (DPC), timer, item kerja, dan objek file , bersama dengan fungsi panggilan balik EvtRequestCancel objek permintaan. Kerangka kerja memanggil sebagian besar fungsi panggilan balik ini di IRQL = DISPATCH_LEVEL, tetapi Anda dapat memaksa fungsi panggilan balik antrean dan objek file untuk berjalan di IRQL = PASSIVE_LEVEL. (Fungsi panggilan balik item kerja selalu berjalan pada PASSIVE_LEVEL.)

Kerangka kerja mengimplementasikan sinkronisasi otomatis ini dengan menggunakan sekumpulan kunci sinkronisasi internal. Kerangka kerja memastikan bahwa dua utas atau lebih tidak dapat memanggil fungsi panggilan balik yang sama secara bersamaan, karena setiap utas harus menunggu hingga dapat memperoleh kunci sinkronisasi sebelum memanggil fungsi panggilan balik. (Secara opsional, driver juga dapat memperoleh kunci sinkronisasi ini bila perlu. 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 akan dipanggil pada satu waktu dan ruang konteks objek hanya akan dapat diakses oleh satu fungsi panggilan balik pada satu waktu.

Kecuali driver Anda menerapkan penanganan gangguan tingkat pasif, kode yang mengganggu layanan dan mengakses data interupsi harus berjalan di IRQL (DIRQL) perangkat 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 mereka berjalan satu per satu. Tabel berikut mencantumkan fungsi panggilan balik yang disinkronkan kerangka kerja.

Tipe objek Fungsi Panggilan Balik yang Disinkronkan

Objek antrean

Penangan permintaan, EvtIoQueueState, EvtIoResume, EvtIoStop

Objek file

Semua fungsi panggilan balik

Meminta objek

EvtRequestCancel

Secara opsional, kerangka kerja juga dapat menyinkronkan fungsi panggilan balik ini dengan fungsi panggilan balik objek interupsi, DPC, item kerja, dan timer yang disediakan driver Anda untuk perangkat (tidak termasuk fungsi panggilan balik EvtInterruptIsr objek interupsi). Untuk mengaktifkan sinkronisasi tambahan ini, driver harus mengatur anggota AutomaticSerialization dari 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 objek interupsi, DPC, item kerja, dan timer.

  • Driver harus menyinkronkan kode yang mengganggu layanan 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 CompletionRoutine driver, atau fungsi panggilan balik yang ditentukan objek target I/O. Sebaliknya, kerangka kerja menyediakan kunci tambahan yang dapat digunakan driver untuk menyinkronkan fungsi panggilan balik 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 yang dimuat tabel sebelumnya, untuk semua antrean I/O perangkat, sehingga berjalan 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 yang dimuat tabel sebelumnya, untuk setiap antrean I/O individual, sehingga mereka menjalankan 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 yang dimuat 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, Anda dapat menentukan cakupan sinkronisasi untuk objek driver, objek perangkat, atau objek antrean Anda. Anggota SynchronizationScope dari struktur WDF_OBJECT_ATTRIBUTES objek mengidentifikasi cakupan sinkronisasi objek. Nilai cakupan sinkronisasi yang dapat ditentukan driver Anda adalah:

WdfSynchronizationScopeDevice
Kerangka kerja disinkronkan dengan mendapatkan kunci sinkronisasi 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 objek dari objek induk objek.

Secara umum, kami tidak menyarankan penggunaan 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 perangkat dan objek antrean adalah WdfSynchronizationScopeInheritFromParent.

Jika Anda ingin kerangka kerja menyediakan sinkronisasi tingkat perangkat untuk semua perangkat, Anda bisa menggunakan langkah-langkah berikut:

  1. Atur SynchronizationScope ke WdfSynchronizationScopeDevice dalam struktur WDF_OBJECT_ATTRIBUTES objek driver driver.

  2. Gunakan nilai WdfSynchronizationScopeInheritFromParent default untuk setiap objek perangkat .

Atau, untuk menyediakan sinkronisasi tingkat perangkat untuk masing-masing perangkat, Anda dapat menggunakan langkah-langkah berikut:

  1. Gunakan nilai WdfSynchronizationScopeNone default untuk objek driver .

  2. Atur SynchronizationScope ke WdfSynchronizationScopeDevice dalam struktur WDF_OBJECT_ATTRIBUTES objek perangkat individual.

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

  • Untuk kerangka kerja versi 1.9 dan yang lebih baru, Anda harus mengaktifkan sinkronisasi tingkat antrean untuk antrean individual dengan mengatur WdfSynchronizationScopeQueue dalam struktur WDF_OBJECT_ATTRIBUTES objek antrean. Ini adalah teknik yang disukai.

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

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

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

Perhatikan bahwa mengatur nilai SynchronizationScope hanya menyinkronkan fungsi panggilan balik yang dikandung tabel sebelumnya. Jika Anda ingin kerangka kerja juga menyinkronkan fungsi interupsi driver, DPC, item kerja, dan panggilan balik objek timer, driver harus mengatur anggota AutomaticSerialization dari struktur konfigurasi objek ini ke TRUE.

Namun, Anda dapat mengatur AutomaticSerialization ke TRUE hanya jika semua fungsi panggilan balik yang ingin Anda sinkronkan berjalan di IRQL yang sama. Memilih tingkat eksekusi, yang 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 selengkapnya tentang struktur konfigurasi untuk objek interupsi, DPC, item kerja, dan timer, dan untuk informasi selengkapnya tentang pembatasan yang berlaku untuk mengatur AutomaticSerialization dalam struktur ini, lihat WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIG, dan WDF_TIMER_CONFIG.

Jika Anda mengatur AutomaticSerialization ke TRUE, Anda harus memilih sinkronisasi tingkat antrean.

Memilih Tingkat Eksekusi

Ketika driver membuat beberapa jenis objek kerangka kerja, driver dapat menentukan tingkat eksekusi untuk objek . Tingkat eksekusi menentukan IRQL di mana kerangka kerja akan memanggil fungsi panggilan balik peristiwa objek yang menangani permintaan I/O 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 fungsi panggilan balik antrean dan objek file dipanggil:

  • Jika driver menggunakan sinkronisasi otomatis, fungsi panggilan balik antrean dan objek file dipanggil 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, fungsi panggilan balik antrean dan objek file driver dapat dipanggil di IRQL <= DISPATCH_LEVEL.

Perhatikan bahwa jika driver Anda menyediakan fungsi panggilan balik objek file, kemungkinan besar Anda akan ingin kerangka kerja memanggil fungsi panggilan balik ini di IRQL = PASSIVE_LEVEL karena beberapa data file, seperti nama file, dapat dipaginasi.

Untuk menyediakan tingkat 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 kerja dapat memanggil fungsi panggilan balik objek di IRQL <= DISPATCH_LEVEL. (Jika driver menggunakan sinkronisasi otomatis, kerangka kerja selalu memanggil fungsi panggilan balik di IRQL = DISPATCH_LEVEL.)

WdfExecutionLevelInheritFromParent
Kerangka kerja memperoleh nilai ExecutionLevel objek dari induk objek.

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

Untuk informasi selengkapnya tentang nilai tingkat eksekusi, lihat WDF_EXECUTION_LEVEL.

Tabel berikut menunjukkan tingkat IRQL di mana kerangka kerja dapat memanggil fungsi panggilan balik driver untuk objek antrean dan objek file.

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

WdfSynchronizationScopeDevice

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelDispatch

<= DISPATCH_LEVEL

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

Anda harus menentukan WdfExecutionLevelPassive jika:

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

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

Alih-alih 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, Anda harus menentukan IRQL tempat driver Anda dan driver lain di tumpukan driver dipanggil. Pertimbangkan situasi berikut:

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

  • Jika driver Anda tidak berada di bagian atas tumpukan, sistem kemungkinan tidak akan 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 panggilan balik driver Anda dipanggil di IRQL <= DISPATCH_LEVEL.

Untuk objek DPC, dan untuk objek timer yang tidak mewakili timer tingkat pasif, perhatikan bahwa Anda tidak dapat mengatur anggota AutomaticSerialization dari struktur konfigurasi ke TRUE jika Anda telah mengatur tingkat eksekusi perangkat induk ke WdfExecutionLevelPassive. Ini karena kerangka kerja akan memperoleh kunci sinkronisasi panggilan balik objek perangkat di IRQL = PASSIVE_LEVEL dan oleh karena itu kunci tidak dapat digunakan untuk menyinkronkan fungsi panggilan balik objek DPC atau timer, yang harus dijalankan pada IRQL = DISPATCH_LEVEL. Dalam kasus seperti itu, driver Anda harus menggunakan kunci putar kerangka kerja di perangkat, DPC, atau fungsi panggilan balik objek timer apa pun yang harus disinkronkan satu sama lain.

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