Bagikan melalui


Menggunakan Pengunci Berputar: Sebuah Contoh

Meminimalkan waktu pengemudi memegang kunci putaran dapat secara signifikan meningkatkan performa driver dan sistem secara keseluruhan. Misalnya, pertimbangkan gambar berikut, yang menunjukkan bagaimana pengunci berputar interupsi melindungi data khusus perangkat yang perlu dibagikan antara rutinitas ISR dan StartIo serta DpcForIsr di komputer SMP.

diagram yang mengilustrasikan penggunaan kunci putar interupsi.

  1. Sementara ISR dari driver berjalan pada DIRQL di satu prosesor, rutinitas StartIo berjalan pada DISPATCH_LEVEL di prosesor kedua. Handler interupsi kernel menggunakan InterruptSpinLock untuk ISR driver, yang mengakses data perangkat yang terproteksi, seperti status atau penunjuk ke register perangkat (SynchronizeContext), dalam ekstensi perangkat driver. Rutinitas StartIo, yang siap untuk mengakses SynchronizeContext, memanggil KeSynchronizeExecution, dengan meneruskan penunjuk ke objek interupsi yang terkait, SynchronizeContext yang bersifat bersama, serta rutin SynchCritSection driver (AccessDevice pada gambar sebelumnya).

    Hingga ISR kembali, sehingga melepaskan InterruptSpinLock driver, KeSynchronizeExecutionberputar pada prosesor kedua, mencegah AccessDevice menyentuh SynchronizeContext. Namun, KeSynchronizeExecution juga menaikkan IRQL pada prosesor kedua ke SynchronizeIrql dari objek interupsi, sehingga mencegah terjadinya gangguan perangkat lain pada prosesor tersebut sehingga AccessDevice dapat dijalankan di DIRQL segera setelah ISR kembali. Namun, gangguan DIRQL yang lebih tinggi untuk perangkat lain, gangguan interupsi jam, dan gangguan kegagalan daya masih dapat terjadi pada salah satu prosesor.

  2. Ketika ISR mengantre DpcForIsr dari driver dan kemudian kembali, AccessDevice berjalan pada prosesor kedua di SynchronizeIrql dari objek interupsi terkait dan mengakses SynchronizeContext. Sementara itu, DpcForIsr dijalankan pada prosesor lain di DISPATCH_LEVEL IRQL. DpcForIsr juga siap untuk mengakses SynchronizeContext, sehingga memanggil KeSynchronizeExecution menggunakan parameter yang sama dengan yang dilakukan StartIo rutin di Langkah 1.

    Ketika KeSynchronizeExecution memperoleh spin lock dan menjalankan AccessDevice atas nama rutinitas StartIo, rutinitas sinkronisasi yang disediakan driver AccessDevice diberikan akses eksklusif ke SynchronizeContext. Karena AccessDevice berjalan di SynchronizeIrql, ISR driver tidak dapat memperoleh kunci spin dan mengakses area yang sama sampai kunci putar dilepaskan, bahkan jika perangkat mengganggu prosesor lain saat AccessDevice dijalankan.

  3. AccessDevice kembali, melepaskan kunci putaran. Rutinitas StartIo melanjutkan berjalan pada DISPATCH_LEVEL di prosesor kedua. KeSynchronizeExecution sekarang menjalankan AccessDevice pada prosesor ketiga, sehingga dapat mengakses SynchronizeContext atas nama DpcForIsr. Namun, jika gangguan perangkat terjadi sebelum DpcForIsr memanggil KeSynchronizeExecution di Langkah 2, ISR mungkin berjalan pada prosesor lain sebelum KeSynchronizeExecution dapat memperoleh kunci spin dan kemudian menjalankan AccessDevice pada prosesor ketiga.

Seperti yang ditunjukkan oleh gambar sebelumnya, saat sebuah rutinitas yang berjalan pada satu prosesor memegang kunci putaran, setiap rutinitas lain yang mencoba memperoleh kunci putaran tersebut tidak akan dapat menyelesaikan pekerjaan. Setiap rutinitas yang mencoba memperoleh kunci putaran yang sudah dipegang akan terus berputar di prosesor saat ini hingga pemilik melepaskan kunci putaran. Ketika kunci putaran dilepaskan, satu dan hanya satu rutinitas yang dapat memperolehnya; setiap rutinitas lain yang saat ini mencoba memperoleh kunci putaran yang sama akan terus berputar.

Pemegang kunci putaran apa pun berjalan pada IRQL yang dinaikkan: baik di DISPATCH_LEVEL untuk kunci putaran eksekutif, atau di DIRQL untuk kunci putaran yang mengganggu. Pemanggil KeAcquireSpinLock dan KeAcquireInStackQueuedSpinLock berjalan di DISPATCH_LEVEL sampai mereka memanggil KeReleaseSpinLock atau KeReleaseInStackQueuedSpinLock untuk melepaskan kunci. Penelepon KeSynchronizeExecution secara otomatis menaikkan IRQL pada prosesor saat ini ke SynchronizeIrql dari objek interupsi hingga rutinitas SynchCritSection selesai dan KeSynchronizeExecution mengembalikan kontrol. Untuk informasi selengkapnya, lihat Rutinitas Dukungan Panggilan yang Menggunakan Kunci Putar.

Ingatlah fakta berikut tentang menggunakan kunci putar:

Semua kode yang berjalan pada IRQL yang lebih rendah tidak dapat menyelesaikan pekerjaan apa pun pada set prosesor yang sedang digunakan oleh pemegang kunci putar dan oleh rutinitas lain yang mencoba memperoleh kunci putar yang sama.

Akibatnya, meminimalkan waktu pengemudi memegang kunci spin menghasilkan performa driver yang jauh lebih baik dan berkontribusi secara signifikan terhadap performa sistem secara keseluruhan yang lebih baik.

Seperti yang ditunjukkan oleh gambar sebelumnya, handler interupsi kernel menjalankan rutinitas yang berjalan pada IRQL yang sama dalam mesin multiprosesor berdasarkan urutan kedatangan. Kernel juga melakukan hal berikut:

  • Ketika rutin pengemudi memanggil KeSynchronizeExecution, kernel menyebabkan rutin pengemudi SynchCritSection dijalankan pada prosesor yang sama dari mana panggilan ke KeSynchronizeExecution terjadi (lihat Langkah 1 dan 3).

  • Ketika ISR driver mengantrekan DpcForIsr, kernel membuat DPC berjalan pada prosesor pertama yang dapat digunakan di mana IRQL berada di bawah DISPATCH_LEVEL. Ini belum tentu prosesor yang sama dari mana panggilanIoRequestDpcterjadi (lihat Langkah 2).

Operasi I/O yang digerakkan oleh interupsi pada driver mungkin cenderung disusun secara berurutan dalam mesin uniprocessor, tetapi operasi yang sama dapat benar-benar asinkron dalam mesin SMP. Seperti yang ditunjukkan oleh gambar sebelumnya, ISR driver dapat berjalan pada CPU4 di komputer SMP sebelum DpcForIsr mulai memproses IRP di mana ISR telah menangani gangguan perangkat pada CPU1.

Dengan kata lain, Anda tidak boleh berasumsi bahwa spin lock interupsi dapat melindungi data spesifik operasi yang disimpan ISR saat berjalan pada satu prosesor dari penulisan ulang oleh ISR ketika gangguan perangkat terjadi pada prosesor lain sebelum rutinitas DpcForIsr atau CustomDpc berjalan.

Meskipun driver dapat mencoba menserialisasikan semua operasi I/O berbasis interupsi untuk mempertahankan data yang dikumpulkan oleh ISR, driver tersebut tidak akan berjalan jauh lebih cepat di mesin SMP daripada di mesin uniprocessor. Untuk mendapatkan performa driver terbaik sambil tetap portabel di seluruh platform uniprocessor dan multiprosesor, driver harus menggunakan beberapa teknik lain untuk menyimpan data khusus operasi yang diperoleh oleh ISR untuk pemrosesan berikutnya oleh DpcForIsr.

Misalnya, ISR dapat menyimpan data khusus operasi dalam IRP yang diteruskannya ke DpcForIsr. Penyempurnaan teknik ini adalah mengimplementasikan DpcForIsr yang mengacu pada hitungan yang telah ditingkatkan oleh ISR, memproses jumlah IRP menggunakan data yang disediakan ISR, dan mengatur ulang hitungan menjadi nol sebelum mengembalikan kontrol. Tentu saja, penghitung harus dilindungi oleh kunci putaran interupsi driver karena ISR-nya dan SynchCritSection akan mempertahankan nilainya secara dinamis.