Menggunakan MDL

Buffer I/O yang mencakup berbagai alamat memori virtual yang berdekatan dapat tersebar di beberapa halaman fisik, dan halaman ini dapat dihentikan. Sistem operasi menggunakan daftar deskriptor memori (MDL) untuk menjelaskan tata letak halaman fisik untuk buffer memori virtual.

MDL terdiri dari struktur MDL yang diikuti oleh array data yang menjelaskan memori fisik tempat buffer I/O berada. Ukuran MDL bervariasi sesuai dengan karakteristik buffer I/O yang dijelaskan MDL. Rutinitas sistem tersedia untuk menghitung ukuran MDL yang diperlukan dan untuk mengalokasikan dan membebaskan MDL.

Struktur MDL semi buram. Driver Anda harus langsung mengakses anggota Berikutnya dan MdlFlags dari struktur ini. Untuk contoh kode yang menggunakan kedua anggota ini, lihat bagian Contoh berikut.

Anggota MDL yang tersisa buram. Jangan mengakses anggota buram dari MDL secara langsung. Sebagai gantinya, gunakan makro berikut, yang disediakan sistem operasi untuk melakukan operasi dasar pada struktur:

MmGetMdlVirtualAddress mengembalikan alamat memori virtual buffer I/O yang dijelaskan oleh MDL.

MmGetMdlByteCount mengembalikan ukuran, dalam byte, dari buffer I/O.

MmGetMdlByteOffset mengembalikan offset dalam halaman fisik awal buffer I/O.

Anda dapat mengalokasikan MDL dengan rutinitas IoAllocateMdl . Untuk membebaskan MDL, gunakan rutinitas IoFreeMdl . Atau, Anda dapat mengalokasikan blok memori yang tidak di-patahkan dan kemudian memformat blok memori ini sebagai MDL dengan memanggil rutinitas MmInitializeMdl .

Baik IoAllocateMdl maupun MmInitializeMdl tidak menginisialisasi array data yang segera mengikuti struktur MDL. Untuk MDL yang berada di blok memori yang dialokasikan driver, gunakan MmBuildMdlForNonPagedPool untuk menginisialisasi array ini untuk menggambarkan memori fisik tempat buffer I/O berada.

Untuk memori yang dapat di-pageable, korespondensi antara memori virtual dan fisik bersifat sementara, sehingga array data yang mengikuti struktur MDL hanya valid dalam keadaan tertentu. Panggil MmProbeAndLockPages untuk mengunci memori yang dapat dipaginasi dan untuk menginisialisasi array data ini untuk tata letak saat ini. Memori tidak akan di-page out sampai pemanggil menggunakan rutinitas MmUnlockPages , di mana konten array data tidak lagi valid.

Rutinitas MmGetSystemAddressForMdlSafe memetakan halaman fisik yang dijelaskan oleh MDL yang ditentukan ke alamat virtual di ruang alamat sistem, jika belum dipetakan ke ruang alamat sistem. Alamat virtual ini berguna untuk driver yang mungkin harus melihat halaman untuk melakukan I/O, karena alamat virtual asli mungkin merupakan alamat pengguna yang hanya dapat digunakan dalam konteks aslinya dan dapat dihapus kapan saja.

Perhatikan bahwa saat Anda membangun MDL parsial dengan menggunakan rutinitas IoBuildPartialMdl , MmGetMdlVirtualAddress mengembalikan alamat awal asli untuk MDL parsial. Alamat ini adalah alamat mode pengguna jika MDL awalnya dibuat sebagai akibat dari permintaan mode pengguna. Dengan demikian, alamat tidak memiliki relevansi di luar konteks proses tempat permintaan berasal.

Biasanya, driver sebagai gantinya membuat alamat mode sistem dengan memanggil makro MmGetSystemAddressForMdlSafe untuk memetakan MDL parsial. Ini memastikan bahwa driver dapat terus mengakses halaman dengan aman terlepas dari konteks proses.

Ketika driver memanggil IoAllocateMdl, driver dapat mengaitkan IRP dengan MDL yang baru dialokasikan dengan menentukan pointer ke IRP sebagai parameter IrpIoAllocateMdl. IRP dapat memiliki satu atau beberapa MDL yang terkait dengannya. Jika IRP memiliki satu MDL yang terkait dengannya, anggota MdlAddress IRP menunjuk ke MDL tersebut. Jika IRP memiliki beberapa MDL yang terkait dengannya, MdlAddress menunjuk ke MDL pertama dalam daftar MDL tertaut yang terkait dengan IRP, yang dikenal sebagai rantai MDL. MDL ditautkan oleh anggota Berikutnya . Anggota Berikutnya dari MDL terakhir dalam rantai diatur ke NULL.

Jika, ketika driver memanggil IoAllocateMdl, driver menentukan FALSE untuk parameter SecondaryBuffer , anggota MdlAddress IRP diatur untuk menunjuk ke MDL baru. Jika SecondaryBufferTRUE, rutin menyisipkan MDL baru di akhir rantai MDL.

Ketika IRP selesai, sistem membuka kunci dan membebaskan semua MDL yang terkait dengan IRP. Sistem membuka kunci MDL sebelum mengantrekan rutinitas penyelesaian I/O dan membebaskannya setelah rutinitas penyelesaian I/O dijalankan.

Driver dapat melintasi rantai MDL dengan menggunakan anggota Berikutnya dari setiap MDL untuk mengakses MDL berikutnya dalam rantai. Driver dapat memasukkan MDL secara manual ke dalam rantai dengan memperbarui anggota Berikutnya .

Rantai MDL biasanya digunakan untuk mengelola array buffer yang terkait dengan satu permintaan I/O. (Misalnya, driver jaringan dapat menggunakan satu buffer untuk setiap paket IP dalam operasi jaringan.) Setiap buffer dalam array memiliki MDL sendiri dalam rantai. Ketika driver menyelesaikan permintaan, ia menggabungkan buffer menjadi satu buffer besar. Sistem kemudian secara otomatis membersihkan semua MDL yang dialokasikan untuk permintaan tersebut.

Manajer I/O adalah sumber permintaan I/O yang sering. Ketika manajer I/O menyelesaikan permintaan I/O, manajer I/O membebaskan IRP dan membebaskan MDL apa pun yang dilampirkan ke IRP. Beberapa MDL ini mungkin telah dilampirkan ke IRP oleh driver yang terletak di bawah manajer I/O di tumpukan perangkat. Demikian pula, jika driver Anda adalah sumber permintaan I/O, driver Anda harus membersihkan IRP dan MDL apa pun yang dilampirkan ke IRP ketika permintaan I/O selesai.

Contoh

Contoh kode berikut adalah fungsi yang diimplementasikan driver yang membebaskan rantai MDL dari IRP:

VOID MyFreeMdl(PMDL Mdl)
{
    PMDL currentMdl, nextMdl;

    for (currentMdl = Mdl; currentMdl != NULL; currentMdl = nextMdl) 
    {
        nextMdl = currentMdl->Next;
        if (currentMdl->MdlFlags & MDL_PAGES_LOCKED) 
        {
            MmUnlockPages(currentMdl);
        }
        IoFreeMdl(currentMdl);
    }
} 

Jika halaman fisik yang dijelaskan oleh MDL dalam rantai dikunci, fungsi contoh memanggil rutinitas MmUnlockPages untuk membuka kunci halaman sebelum memanggil IoFreeMdl untuk membebaskan MDL. Namun, fungsi contoh tidak perlu secara eksplisit membatalkan peta halaman sebelum memanggil IoFreeMdl. Sebaliknya, IoFreeMdl secara otomatis membuka peta halaman saat membebaskan MDL.