Cancel-Safe Antrean IRP

Driver yang menerapkan antrean IRP mereka sendiri harus menggunakan kerangka kerja antrean IRP yang batal aman . Antrean IRP batal aman membagi penanganan IRP menjadi dua bagian:

  1. Driver menyediakan serangkaian rutinitas panggilan balik yang menerapkan operasi standar pada antrean IRP driver. Operasi yang disediakan termasuk menyisipkan dan menghapus RUNPS dari antrean, serta mengunci dan membuka kunci antrean. Lihat Menerapkan Antrean IRP Cancel-Safe.

  2. Setiap kali driver perlu benar-benar menyisipkan atau menghapus IRP dari antrean, driver menggunakan rutinitas IoCsqXxx yang disediakan sistem. Rutinitas ini menangani semua sinkronisasi dan logika pembatalan IRP untuk driver.

Driver yang menggunakan antrean IRP batal aman tidak menerapkan rutinitas Batal untuk mendukung pembatalan IRP.

Kerangka kerja memastikan bahwa driver menyisipkan dan menghapus RUNPS dari antrean mereka secara atomik. Ini juga memastikan bahwa pembatalan IRP diterapkan dengan benar. Driver yang tidak menggunakan kerangka kerja harus mengunci dan membuka kunci antrean secara manual sebelum melakukan penyisipan dan penghapusan apa pun. Mereka juga harus menghindari kondisi balapan yang dapat dihasilkan saat menerapkan rutinitas Batal . (Untuk deskripsi kondisi balapan yang dapat muncul, lihat Menyinkronkan Pembatalan IRP.)

Kerangka kerja antrean IRP batal aman disertakan dengan Windows XP dan versi Windows yang lebih baru. Driver yang juga harus bekerja dengan Windows 2000 dan Windows 98/Me dapat ditautkan ke pustaka Csq.lib yang disertakan dalam Windows Driver Kit (WDK). Pustaka Csq.lib menyediakan implementasi kerangka kerja ini.

Rutinitas IoCsqXxx dideklarasikan dalam Windows XP dan versi Wdm.h dan Ntddk.h yang lebih baru. Driver yang juga harus bekerja dengan Windows 2000 dan Windows 98/Me harus menyertakan Csq.h untuk deklarasi.

Anda dapat melihat demonstrasi lengkap tentang cara menggunakan antrean IRP batal-aman di direktori \src\general\cancel WDK. Untuk informasi selengkapnya tentang antrean ini, lihat juga laporan resmi Aliran Kontrol untuk Cancel-Safe Antrean IRP .

Menerapkan Antrean IRP Cancel-Safe

Untuk menerapkan antrean IRP yang batal aman, driver harus memberikan rutinitas berikut:

  • Salah satu rutinitas berikut untuk menyisipkan RUNP ke dalam antrean: CsqInsertIrp atau CsqInsertIrpEx. CsqInsertIrpEx adalah versi yang diperluas dari CsqInsertIrp; antrean diimplementasikan menggunakan satu atau yang lain.

  • Rutinitas CsqRemoveIrp yang menghapus IRP yang ditentukan dari antrean.

  • Rutinitas CsqPeekNextIrp yang mengembalikan pointer ke IRP berikutnya setelah IRP yang ditentukan dalam antrean. Di sinilah sistem melewati nilai PeekContext yang diterimanya dari IoCsqRemoveNextIrp. Driver dapat menafsirkan nilai tersebut dengan cara apa pun.

  • Kedua rutinitas berikut untuk memungkinkan sistem mengunci dan membuka kunci antrean IRP: CsqAcquireLock dan CsqReleaseLock.

  • Rutinitas CsqCompleteCanceledIrp yang menyelesaikan IRP yang dibatalkan.

Penunjuk ke rutinitas driver disimpan dalam struktur IO_CSQ yang menjelaskan antrean. Driver mengalokasikan penyimpanan untuk struktur IO_CSQ . Struktur IO_CSQ dijamin tetap berukuran tetap, sehingga driver dapat dengan aman menyematkan struktur di dalam ekstensi perangkatnya.

Driver menggunakan IoCsqInitialize atau IoCsqInitializeEx untuk menginisialisasi struktur. Gunakan IoCsqInitialize jika antrean mengimplementasikan CsqInsertIrp, atau IoCsqInitializeEx jika antrean mengimplementasikan CsqInsertIrpEx.

Driver hanya perlu menyediakan fungsionalitas penting dalam setiap rutinitas panggilan balik. Misalnya, hanya rutinitas CsqAcquireLock dan CsqReleaseLock yang menerapkan penanganan kunci. Sistem secara otomatis memanggil rutinitas ini untuk mengunci dan membuka kunci antrean seperlunya.

Anda dapat menerapkan semua jenis mekanisme antrean IRP di driver Anda, selama rutinitas pengiriman yang sesuai disediakan. Misalnya, driver dapat menerapkan antrean sebagai daftar tertaut, atau sebagai antrean prioritas.

CsqInsertIrpEx menyediakan antarmuka yang lebih fleksibel ke antrean daripada CsqInsertIrp. Driver dapat menggunakan nilai pengembaliannya untuk menunjukkan hasil operasi; jika mengembalikan kode kesalahan, penyisipan gagal. Rutinitas CsqInsertIrp tidak mengembalikan nilai, jadi tidak ada cara sederhana untuk menunjukkan bahwa penyisipan gagal. Selain itu, CsqInsertIrpEx mengambil parameter InsertContext tambahan yang ditentukan driver yang dapat digunakan untuk menentukan informasi khusus driver tambahan yang akan digunakan oleh implementasi antrean.

Driver dapat menggunakan CsqInsertIrpEx untuk menerapkan penanganan IRP yang lebih canggih. Misalnya, jika tidak ada RUNP yang tertunda, rutinitas CsqInsertIrpEx dapat mengembalikan kode kesalahan dan driver dapat segera memproses IRP. Demikian pula, jika IRP tidak dapat lagi diantrekan, CsqInsertIrpEx dapat mengembalikan kode kesalahan untuk menunjukkan fakta tersebut.

Driver terisolasi dari semua penanganan pembatalan IRP. Sistem menyediakan rutinitas Batal untuk IRP dalam antrean. Rutinitas ini memanggil CsqRemoveIrp untuk menghapus IRP dari antrean, dan CsqCompleteCanceledIrp untuk menyelesaikan pembatalan IRP.

Diagram berikut mengilustrasikan alur kontrol untuk pembatalan IRP.

diagram yang mengilustrasikan alur kontrol untuk pembatalan irp.

Implementasi dasar CsqCompleteCanceledIrp adalah sebagai berikut.

VOID CsqCompleteCanceledIrp(PIO_CSQ Csq, PIRP Irp) {
  Irp->IoStatus.Status = STATUS_CANCELLED;
  Irp->IoStatus.Information = 0;

  IoCompleteRequest(Irp, IO_NO_INCREMENT);
}

Driver dapat menggunakan salah satu primitif sinkronisasi sistem operasi untuk mengimplementasikan rutinitas CsqAcquireLock dan CsqReleaseLock mereka. Primitif sinkronisasi yang tersedia termasuk kunci putaran dan objek mutex.

Berikut adalah contoh bagaimana driver dapat menerapkan penguncian menggunakan kunci spin.

/* 
  The driver has previously initialized the SpinLock variable with
  KeInitializeSpinLock.
 */

VOID CsqAcquireLock(PIO_CSQ IoCsq, PKIRQL PIrql)
{
    KeAcquireSpinLock(SpinLock, PIrql);
}

VOID CsqReleaseLock(PIO_CSQ IoCsq, KIRQL Irql)
{
    KeReleaseSpinLock(SpinLock, Irql);
}

Sistem meneruskan pointer ke variabel IRQL ke CsqAcquireLock dan CsqReleaseLock. Jika driver menggunakan kunci putar untuk menerapkan penguncian untuk antrean, driver dapat menggunakan variabel ini untuk menyimpan IRQL saat ini ketika antrean dikunci.

Driver tidak diharuskan menggunakan kunci putaran. Misalnya, driver dapat menggunakan mutex untuk mengunci antrean. Untuk deskripsi teknik sinkronisasi yang tersedia untuk driver, lihat Teknik Sinkronisasi.

Menggunakan Antrean IRP Cancel-Safe

Driver menggunakan rutinitas sistem berikut saat mengantre dan menghapus antrean IRP:

Diagram berikut mengilustrasikan alur kontrol untuk IoCsqRemoveNextIrp.

diagram yang mengilustrasikan aliran kontrol untuk iocsqremovenextirp.

Diagram berikut mengilustrasikan alur kontrol untuk IoCsqRemoveIrp.

diagram yang mengilustrasikan aliran kontrol untuk iocsqremoveirp.

Rutinitas ini, pada gilirannya, dikirim ke rutinitas yang disediakan pengemudi.

Rutinitas IoCsqInsertIrpEx menyediakan akses ke fitur yang diperluas dari rutinitas CsqInsertIrpEx . Ini mengembalikan nilai status yang dikembalikan oleh CsqInsertIrpEx. Pemanggil dapat menggunakan nilai ini untuk menentukan apakah IRP berhasil diantrekan atau tidak. IoCsqInsertIrpEx juga memungkinkan pemanggil untuk menentukan nilai untuk parameter InsertContext CsqInsertIrpEx.

Perhatikan bahwa IoCsqInsertIrp dan IoCsqInsertIrpEx dapat dipanggil pada antrean yang batal-aman, apakah antrean memiliki rutinitas CsqInsertIrp atau rutinitas CsqInsertIrpEx . IoCsqInsertIrp berperilaku sama dalam kedua kasus. Jika IoCsqInsertIrpEx melewati antrean yang memiliki rutinitas CsqInsertIrp , itu berperilaku identik dengan IoCsqInsertIrp.

Diagram berikut mengilustrasikan alur kontrol untuk IoCsqInsertIrp.

diagram yang mengilustrasikan aliran kontrol untuk iocsqinsertirp.

Diagram berikut mengilustrasikan alur kontrol untuk IoCsqInsertIrpEx.

diagram yang mengilustrasikan aliran kontrol untuk iocsqinsertirpex.

Ada beberapa cara alami untuk menggunakan rutinitas IoCsqXxx untuk mengantre dan menghapus antrean IRP. Misalnya, driver hanya dapat mengantrekan IRP untuk diproses dalam urutan diterimanya. Driver dapat mengantre IRP sebagai berikut:

    status = IoCsqInsertIrpEx(IoCsq, Irp, NULL, NULL);

Jika driver tidak diperlukan untuk membedakan antara IRP tertentu, itu kemudian dapat menghapus antreannya dalam urutan di mana mereka diantrekan, sebagai berikut:

    IoCsqRemoveNextIrp(IoCsq, NULL);

Atau, driver dapat mengantre dan menghapus antrean IRP tertentu. Rutinitas menggunakan struktur IO_CSQ_IRP_CONTEXT buram untuk mengidentifikasi RUNP tertentu dalam antrean. Driver mengantre IRP sebagai berikut:

    IO_CSQ_IRP_CONTEXT ParticularIrpInQueue;
    IoCsqInsertIrp(IoCsq, Irp, &ParticularIrpInQueue);

Driver kemudian dapat menghapus antrean IRP yang sama dengan menggunakan nilai IO_CSQ_IRP_CONTEXT .

    IoCsqRemoveIrp(IoCsq, Irp, &ParticularIrpInQueue);

Driver mungkin juga diperlukan untuk menghapus RUNPS dari antrean berdasarkan kriteria tertentu. Misalnya, driver mungkin mengaitkan prioritas dengan setiap IRP, sehingga IRP prioritas yang lebih tinggi dibatalkan antreannya terlebih dahulu. Driver mungkin meneruskan nilai PeekContext ke IoCsqRemoveNextIrp, yang diteruskan sistem kembali ke driver ketika meminta IRP berikutnya dalam antrean.