25/05/2018, 09:11

Quản lý bộ nhớ (Memory Management )

Ở chương này, tôi sẽ bàn về chủ đề quản lý bộ nhớ. Windows XP chia cắt vùng địa chỉ thực tế chia cắt bên trong bằng vài cách. Một cách chia - rất bảo mật và toàn vẹn là địa chỉ người dùng và địa chỉ nhân. Một cách chia khác, mà sử dụng hầu hết nhưng không ...

Ở chương này, tôi sẽ bàn về chủ đề quản lý bộ nhớ. Windows XP chia cắt vùng địa chỉ thực tế chia cắt bên trong bằng vài cách. Một cách chia - rất bảo mật và toàn vẹn là địa chỉ người dùng và địa chỉ nhân. Một cách chia khác, mà sử dụng hầu hết nhưng không tốt lắm là giữa trang nhớ và không phaitrang nhớ. Hầu hết địa chỉ người sử dụng và vài địa chỉ kiểu nhân tham chiếu dến ô nhớ mà Quản lý bộ nhớ đổi tới và từ đĩa trong toàn bộ thời gian, trong khi một vài địa chỉ kiểu nhân luôn luôn liên kết tới ô nhớ trong bộ nhớ vật lý. Từ Windows XP cho phép nhưững phần của trình điều khiển tớ ô nhớ, tôi sẽ giải thích cách làm thế nào điều khiển ô nhớ của trình điều khiển của bạn tại một thời điểm bạn xây dựng trình điều khiển và chạy nó. Windows XP cung cấp một số phưoơg thức để quản lý bộ nhớ. Tôi sẽ mô tả hai hàm dịch vụ cơ bản —ExAllocatePoolWithTag và ExFreePool— mà bạn sử dụng để phân chia và giải phóng một cách ngẫu nhiên. Tôi sẽ giải thích nguyên thuỷ mà bạn sử dụng để tổ chức bộ nhớ và trong những danh sách liên kết của cấu trúc. Cuối cùng tôi sẽ giải thích khái niệm một danh sách mà cho phép bạn phân chia có hiệu quả.

Windows XP và Microsoft Windows 98/Me chạy trên nhữngmáy tính mà hỗ trợ một vùng địa chỉ thực tế nơi mà địa chỉ ảo được vẽ tới bộ nhớ vật lý hoặc một ô nhớ bên trong file tráo đổi trên dĩa cứng. Thủ tục đơn giản hoá những vấn đề, bạn có thể nghĩ về địa chỉ ảo như chia cắt thành hai phần: một phaanf kiêu nhân và một phần kiểu người dùng. Nhìn hình 3-6.

Hình 3-6. Thành viên-hạt nhân và chế độ-chế độ của phần không gian địa chỉ. Mỗi người sử dụng chế độ xử lý có địa chỉ bối cảnh riêng của mình, mà bản đồ của người sử dụng chế độ ảo-địa chỉ duy nhất cho một bộ sưu tập của vật chất trang khung. Nói cách khác, ý nghĩa của bất kỳ địa chỉ riêng ảo thay đổi từ một chút thời gian để tiếp theo như là Windows XP Scheduler tắc từ một sợi ở một trong quá trình vào một sợi khác trong quá trình. Một phần của công việc trong khâu chuyển đổi là để thay đổi các trang bảng được sử dụng bởi một xử lý để họ giới thiệu đến các luồng của quá trình bối cảnh. It's thường không WDM đó, một driver sẽ thực hiện trong cùng một bối cảnh như là sợi xướng của I / O yêu cầu nó xử lý.

Chúng ta nói rằng chúng tôi đang chạy trong bối cảnh arbitrary sợi, nếu chúng ta không biết chắc chắn sẽ cho quá trình mà hiện nay người sử dụng chế độ địa chỉ bối cảnh thuộc. Trong bối cảnh arbitrary sợi, chúng tôi chỉ đơn giản có thể không được sử dụng một địa chỉ mà ảo thuộc với người sử dụng chế độ bởi vì chúng tôi có thể không có bất kỳ ý tưởng với những gì vật lý bộ nhớ nó có thể điểm. Trong số này xem không chắc chắn, nhìn chung chúng tôi phải tuân theo các quy định sau đây trong một chương trình driver: Không bao giờ (tốt, mấy khi) người trực tiếp tham chiếu-chế độ bộ nhớ

Nói cách khác, không có một địa chỉ mà một người sử dụng chế độ-cung cấp các ứng dụng và chữa trị cho rằng như là một địa chỉ trỏ rằng chúng tôi có thể trực tiếp dereference. Tôi sẽ thảo luận trong chương sau một vài kỹ thuật để truy cập dữ liệu đệm mà bắt nguồn ở chế độ người dùng. Tất cả chúng tôi cần phải biết ngay bây giờ, tuy nhiên, chúng tôi đang là (gần như) luôn luôn có được bằng cách sử dụng hạt nhân-chế độ địa chỉ ảo bất cứ khi nào chúng tôi muốn truy cập vào bộ nhớ của máy tính. Bao lớn Có một trang?

Trong một hệ thống bộ nhớ ảo, hệ điều hành tổ chức vật lý bộ nhớ và các vùng trao đổi vào tập tin như-kích thước trang khung. Trong một WDM trình điều khiển, bạn có thể sử dụng để tỏ cố PAGE_SIZE cho bạn biết làm thế nào là một trang lớn. Trong một số máy tính Windows XP, một trang là 4096 bytes dài; trong những người khác, it's 8192 bytes dài. A liên quan đến việc đặt tên PAGE_SHIFT kích thước bằng các trang như là một sức mạnh của 2. Đó là:

PAGE_SIZE == 1 << PAGE_SHIFT

Cho sự tiện nghi của bạn, bạn có thể sử dụng một vài preprocessor macros mã của bạn trong khi bạn đang làm việc với các kích thước của một trang web:

• ROUND_TO_PAGES trong vòng một kích thước byte đến trang kế tiếp cao hơn-ranh giới. Ví dụ, ROUND_TO_PAGES (1) là 4.096 trên một 4-KB-trang máy tính.

• BYTES_TO_PAGES xác định bao nhiêu trang được yêu cầu tổ chức một số byte, bắt đầu từ bắt đầu của một trang. Ví dụ, BYTES_TO_PAGES (42) sẽ là 1 trên tất cả các nền tảng, và BYTES_TO_PAGES (5000) sẽ là 2 trên một số nền tảng và 1 về những người khác. • BYTE_OFFSET sẽ trả về byte bù đắp phần của một địa chỉ ảo. Đó là, tính toán của nó bắt đầu bù đắp trong vòng một số trang khung của một địa chỉ. Ngày 4-KB-trang, máy tính, BYTE_OFFSET (0x12345678) sẽ được 0x678

PAGE_ALIGN vòng một ảo địa chỉ xuống để một trang ranh giới. Ngày 4-KB-trang, máy tính, PAGE_ALIGN (0x12345678) sẽ được 0x12345000.

• ADDRESS_AND_SIZE_TO_SPAN_PAGES trả ra số trang khung chiếm được xác định bởi một số byte, bắt đầu từ một địa chỉ ảo. Ví dụ, các tuyên bố ADDRESS_AND_SIZE_TO_SPAN_PAGES (0x12345FFF, 2) là 2 trên một 4-KB-trang máy vì 2 byte chiều dài một trang ranh giới.

Toàn bộ số điểm của một hệ thống bộ nhớ ảo là bạn có thể có được một không gian địa chỉ ảo đó là nhiều lớn hơn số lượng bộ nhớ vật lý trên máy tính. Để thực hiện điều này feat, Trưởng đại diện các Bộ nhớ nhu cầu để trao đổi trang khung trong và ngoài của vật lý bộ nhớ.

Các thể loại "phải được cư dân" công cụ rộng lớn hơn nhiều hơn là chỉ các trang lỗi xử lý. Windows XP cho phép phần cứng interrupts để xảy ra ở gần bất kỳ lúc nào, bao gồm một trang lỗi trong khi đang được phục vụ. Nếu điều này không được như vậy, các trang lỗi handler sẽ không thể đọc hoặc viết trang từ một thiết bị sử dụng một gián đoạn

Vì vậy, mỗi phần cứng gián đoạn dịch vụ thường phải được nonpaged trong bộ nhớ. Các thiết kế của Windows NT, bao gồm các quyết định ngay cả thêm thói quen trong nonpaged danh mục bằng cách sử dụng một nguyên tắc đơn giản: Mã thi hành tại hoặc gián đoạn ở trên mức yêu cầu (IRQL) DISPATCH_LEVEL trang có thể không gây ra lỗi lầm.

Tôi sẽ xây dựng trên nguyên tắc này trong chương kế tiếp. Bạn có thể sử dụng PAGED_CODE preprocessor vĩ mô (trong wdm.h) để giúp bạn khám phá các hành vi vi phạm các quy định này trong kiểm tra xây dựng của driver của bạn. Ví dụ:

NTSTATUS DispatchPower(PDEVICE_OBJECT fdo, PIRP Irp)

  {

  PAGED_CODE()

}

PAGED_CODE chứa các điều kiện biên soạn. Trong khi kiểm tra-xây dựng, môi trường, nó in một tin nhắn và tạo ra một đồng thất bại nếu IRQL hiện nay là quá cao. Trong miễn phí-xây dựng, môi trường, nó không làm bất cứ điều gì. Để hiểu lý do tại sao PAGED_CODE rất hữu ích, tưởng tượng rằng DispatchPower nhu cầu cho một số lý do để được ở nonpaged nhưng bộ nhớ rằng bạn có misplaced nó trong bộ nhớ paged

Nếu hệ thống xảy ra DispatchPower để gọi tại một thời gian khi trang chứa nó không phải là hiện nay, một trang lỗi sẽ xảy ra, theo sau là một lỗi kiểm tra. Lỗi mã kiểm tra sẽ được pretty uninformative (IRQL_NOT_LESS_OR_EQUAL hay DRIVER_IRQL_NOT_LESS_OR_EQUAL), nhưng ít nhất bạn sẽ tìm ra rằng bạn có một vấn đề.

Nếu bạn kiểm tra trình điều khiển của bạn trong một tình hình trong đó các trang có chứa DispatchPower sẽ xảy ra fortuitously để được trong bộ nhớ, tuy nhiên, sẽ không có một trang lỗi. PAGED_CODE sẽ phát hiện ngay cả vấn đề như vậy

Cài đặt các Driver Verifier "IRQL lực lượng kiểm tra" lựa chọn sẽ làm tăng đáng kể cơ hội khám phá ra rằng bạn đã tan vỡ các quy định về paging và IRQL. Tùy chọn lực lượng pageable trang ra khỏi bất cứ khi nào xác nhận trình điều khiển bộ nhớ nâng cao IRQL để DISPATCH_LEVEL hay ngoài.

Cho rằng một số phần của trình điều khiển của bạn phải luôn luôn được cư dân và một số phần có thể được paged, bạn cần một cách để kiểm soát các giao của bạn và mã dữ liệu vào paged và nonpaged pool. Bạn pháp phần của công việc này bằng cách hướng dẫn làm thế nào để biên apportion mã của bạn và dữ liệu khác nhau giữa các phần

The run-tải thời gian sử dụng tên của các phần để đưa phần của trình điều khiển của bạn trong những nơi bạn có ý. Bạn cũng có thể pháp phần của công việc này tại thời gian chạy bằng cách gọi các bộ nhớ Trưởng đại diện thói quen mà tôi sẽ thảo luận trong phần kế tiếp.

Win32 executable file, bao gồm cả hạt nhân-chế độ điều khiển, đang có nội bộ thành từ một hay nhiều phần. Một phần có thể chứa dữ liệu và mã hay, nói chung, có thêm thuộc tính như là đang được đọc, ghi, shareable, thi hanh, và như vậy trên. Một phần nhỏ cũng là đơn vị mà bạn có thể chỉ khi bạn đã xác định khả năng trang ¬

Khi driver tải một hình ảnh, hệ thống đã nhận phần chữ có tên bắt đầu với Trang hay. Eda (bắt đầu. EDATA) vào paged pool, trừ khi DisablePagingExecutive giá trị trong HKLM System CurrentControlSet Control Session Manager Memory Quản lý chủ chốt sẽ xảy ra để được thiết lập (trong đó có trường hợp không có bằng driver paging xảy ra).

). Lưu ý rằng các tên này là trường hợp nhạy cảm! Trong một trong những twists ít có ảnh hưởng đến số phận của chúng tôi tất cả theo thời gian, chạy mềm-Ice / W trên Windows XP yêu cầu bạn để vô hiệu hóa hạt nhân trong paging bằng cách này. Điều này chắc chắn làm cho khó hơn để tìm thấy lỗi gây ra bởi misplacement của driver hoặc mã dữ liệu vào paged pool! Nếu bạn sử dụng debugger này, tôi khuyên bạn nên religiously sử dụng PAGED_CODE vĩ mô và Driver Verifier.

Cách truyền thống cho các biên để đưa mã vào một phần là để sử dụng những alloc_text pragma. Kể từ mỗi biên sẽ không nhất thiết phải hỗ trợ các pragma, các DDK tiêu đề hoặc xác định hay không xác định việc ALLOC_PRAGMA để cho biết nên sử dụng pragma. Sau đó bạn có thể gọi các pragma để xác định vị trí của phần subroutines cá nhân của bạn trong trình điều khiển, như sau:

#ifdef ALLOC_PRAGMA

  #pragma alloc_text(PAGE, AddDevice)

  #pragma alloc_text(PAGE, DispatchPnp)

#endif

Những tài liệu phục vụ cho các nơi AddDevice và DispatchPnp hàm vào paged pool. Của Microsoft C / C + + biên nơi hai gây phiền nhiễu hạn chế về việc sử dụng các alloc_text:

• Các pragma phải làm theo các công bố của một hàm nhưng việc định nghĩa. Một cách để phải tuân theo các quy định này là để tuyên bố tất cả các hàm trong trình điều khiển của bạn trong một tiêu chuẩn tiêu đề tập tin và gọi alloc_text tại đầu của các tập tin nguồn có chứa một hàm nhưng sau khi bạn bao gồm các tiêu đề.

• Các pragma chỉ có thể được sử dụng với hàm có liên kết C-. Nói cách khác, nó sẽ không làm việc cho các thành viên lớp học hoặc hàm cho các hàm trong một C + + mã nguồn tệp tin mà bạn đã không khai báo bằng cách sử dụng extern "C". Để kiểm soát các vị trí của các biến dữ liệu, bạn sử dụng một pragma khác nhau dưới sự kiểm soát của là một biểu tượng khác nhau preprocessor vĩ mô:

#ifdef ALLOC_DATA_PRAGMA

  #pragma data_seg("PAGEDATA")

#endif

Data_seg pragma nguyên nhân của tất cả các dữ liệu tĩnh tuyên bố các biến trong một mô-đun nguồn sau khi xuất hiện của pragma để đi vào paged pool. Bạn sẽ nhận thấy rằng này pragma khác trong một cách căn bản từ alloc_text. Một phần pageable bắt đầu, nơi # pragma data_seg ( "PAGEDATA") xuất hiện và kết thúc, nơi một countervailing # pragma data_seg () xuất hiện. Alloc_text, mặt khác, áp dụng cho một hàm cụ thể.

Bảng 3-3 liệt kê các dịch vụ, bạn có thể sử dụng hàm chạy ở thời gian để tinh chỉnh sự pageability của bạn driver trong tình huống khác nhau. Mục đích của các thói quen là để cho bạn phát hành của bộ nhớ vật lý mà có khác được gắn lên do mã của bạn và dữ liệu trong suốt thời gian khi nó sẽ không được cần thiết.

Trong Chương 8, ví dụ, tôi sẽ thảo luận làm thế nào bạn có thể đặt điện thoại của bạn vào một ít quyền lực nhà nước trong thời gian không hoạt động. Powering xuống có thể sẽ có một thời gian tốt để phát hành các trang web của bạn bị khóa

Tôi đang đi để mô tả một cách sử dụng các hàm để kiểm soát pageability của mã trong trình điều khiển của bạn. Bạn có thể muốn đọc DDK mô tả để tìm hiểu về những cách khác để sử dụng chúng. Subroutines trong phân phối đầu tiên của bạn driver vào một cách riêng biệt, tên mã phần, như thế này:

#pragma alloc_text(PAGEIDLE, DispatchRead)

#pragma alloc_text(PAGEIDLE, DispatchWrite)

Đó là, xác định một phần tên bắt đầu với Trang và kết thúc bằng bất cứ bốn ký tự suffix bạn vui lòng. Sau đó, sử dụng alloc_text pragma để đặt một số nhóm các thói quen của riêng bạn trong đó phần đặc biệt. Bạn có thể có bao nhiêu phần pageable đặc biệt như bạn muốn, nhưng vấn đề hậu của bạn sẽ phát triển như bạn driver của bạn chia theo cách này. Trong thời gian sở khởi (nói rằng, trong DriverEntry), khóa của bạn pageable phần như thế này:

PVOID hPageIdleSection;

NTSTATUS DriverEntry(...)

  {

  hPageIdleSection = MmLockPagableCodeSection((PVOID)

    DispatchRead);

  }

Khi bạn gọi MmLockPagableCodeSection, bạn chỉ định bất kỳ ở tất cả các địa chỉ trong phần bạn đang cố gắng để khóa. Thực sự nhằm mục đích thực hiện cuộc gọi trong thời gian này DriverEntry được xử lý để có được giá trị của nó trở về, tôi đã hiển thị mà bạn tiết kiệm trong toàn cầu một biến tên hPageIdleSection. Bạn sẽ sử dụng nhiều xử lý sau này, khi bạn quyết định bạn không cần một phần trong bộ nhớ cho một trong khi:

MmUnlockPagableImageSection(hPageIdleSection);

Điều này sẽ mở khóa gọi các trang web có chứa các PAGEIDLE phần và cho phép họ để di chuyển ra và vào bộ nhớ theo yêu cầu. Nếu bạn sau này khám phá rằng bạn cần những trang lại một lần nữa, bạn thực hiện cuộc gọi này:

MmLockPagableSectionByHandle(hPageIdleSection);

Sau đây gọi này, các PAGEIDLE phần sẽ lại một lần nữa được nonpaged trong bộ nhớ (nhưng không nhất thiết phải là cùng một vật lý bộ nhớ như trước đó). Lưu ý rằng hàm này gọi là có sẵn cho bạn chỉ trong Windows 2000 và Windows XP, và sau đó chỉ nếu bạn đã bao gồm ntddk.h thay vì wdm.h. Trong những tình huống khác, bạn sẽ phải gọi lại MmLockPagableCodeSection

Bạn có thể làm gì đó tương tự để đặt dữ liệu vào các đối tượng pageable phần

PVOID hPageDataSection;

#pragma data_seg("PAGE")

ULONG ulSomething;

#pragma data_seg()

hPageDataSection = MmLockPagableDataSection((PVOID) 

  &ulSomething);

MmUnlockPagableImageSection(hPageDataSection);

MmLockPagableSectionByHandle(hPageDataSection);

Tôi đã phát nhanh và loose với cú pháp của tôi đây-những tài liệu sẽ xuất hiện trong rộng rãi bị tách phần của trình điều khiển của bạn. Những ý tưởng đằng sau những dịch vụ hàm quản lý bộ nhớ tôi chỉ cần được mô tả là bạn sẽ bước đầu khóa một phần có chứa một hoặc nhiều trang web và có được một xử lý cho sử dụng trong các cuộc gọi.Bottom of Form

Sau đó bạn có thể mở khóa các trang trong một phần bằng cách gọi điện thoại qua MmUnlockPagableImageSection và xử lý tương ứng. Relocking phần sau này đòi hỏi một cuộc gọi đến MmLockPagableSectionByHandle.

A nhanh chóng tắt hiện có sẵn nếu bạn chắc chắn rằng không có phần của driver của bạn sẽ cần phải được cho cư dân trong một thời gian. MmPageEntireDriver sẽ đánh dấu tất cả các phần trong một hình ảnh như bằng driver đang được pageable. Ngược lại, MmResetDriverPaging sẽ khôi phục lại các biên-thời gian pageability thuộc tính cho toàn bộ trình điều khiển. Để gọi những thói quen, bạn chỉ cần địa chỉ của một số đoạn mã hoặc dữ liệu trong trình điều khiển. Ví dụ:

MmPageEntireDriver((PVOID) DriverEntry);

MmResetDriverPaging((PVOID) DriverEntry);

Bottom of Form

Ví dụ:

#define DRIVERTAG 'KNUJ'

PVOID p = ExAllocatePoolWithTag(PagedPool, 42, DRIVERTAG);

Nếu không có đủ bộ nhớ để đáp ứng yêu cầu của bạn, các allocator pool sẽ đem lại một NULL trỏ. Bạn nên luôn luôn kiểm tra lại giá trị và làm cái gì hợp lý. Ví dụ:

PMYSTUFF p = (PMYSTUFF) ExAllocatePool(PagedPool, sizeof(MYSTUFF));

if (!p)

  return STATUS_INSUFFICIENT_RESOURCES;

Các loại pool bao gồm khái niệm phải thành công. Nếu không có đủ bộ nhớ heap để satisify một yêu cầu từ phải-pool thành công, hệ thống kiểm tra lỗi. Trình điều khiển không nên phân bổ bộ nhớ bằng cách sử dụng một trong những thành công phải-specifiers. Điều này là do một driver có thể gần như luôn luôn thất bại nào là hoạt động theo cách

Gây ra một hệ thống sụp đổ trong một bộ nhớ thấp-tình hình không phải là cái gì làm nên một driver. Hơn nữa, chỉ có một giới hạn pool phải-thành công của bộ nhớ tồn tại trong toàn bộ hệ thống, và hệ điều hành có thể không có khả năng phân bổ bộ nhớ cần thiết để có thể giữ cho các máy tính chạy nếu trình điều khiển đó lên một số.

Nếu bạn HOẶC giá trị POOL_RAISE_IF_ALLOCATION_FAILURE (0x00000010) vào pool loại mã, các heap allocator sẽ tăng một STATUS_INSUFFICIENT_RESOURCES ngoại trừ thay vì trở về NULL nếu không có đủ bộ nhớ. Bạn nên sử dụng một cấu trúc ngoại trừ khung để nắm bắt như một ngoại lệ. Ví dụ:

#ifndef POOL_RAISE_IF_ALLOCATION_FAILURE

  #define POOL_RAISE_IF_ALLOCATION_FAILURE 16

#endif

#define PagedPoolRaiseException (POOL_TYPE) 

  (PagedPool │ POOL_RAISE_IF_ALLOCATION_FAILURE)

#define NonPagedPoolRaiseException (POOL_TYPE) 

  (NonPagedPool │ POOL_RAISE_IF_ALLOCATION_FAILURE)

NTSTATUS SomeFunction()

  {

  NTSTATUS status;

  __try

    {

    PMYSTUFF p = (PMYSTUFF)

      ExAllocatePoolWithTag(PagedPoolRaiseException, 

      sizeof(MYSTUFF), DRIVERTAG);

    <Code that uses "p" without checking it for NULL>

    status = STATUS_SUCCESS;

    }

  __except(EXCEPTION_EXECUTE_HANDLER)

    {

    status = GetExceptionCode();

    }

  return status;

  }

NOTE POOL_RAISE_IF_ALLOCATION_FAILURE được định nghĩa trong NTIFS.H, một phần đầu tập tin đó là có sẵn chỉ là một phần của thêm chi phí cài tệp Hệ thống kit. Làm bộ nhớ phân với cờ này được thiết lập để phổ biến trong hệ thống tập tin trình điều khiển, tuy nhiên, tôi nghĩ rằng bạn nên biết về điều đó.

Incidentally, tôi đề nghị bạn không đi crazy cố gắng để chẩn đoán hoặc phục hồi từ thất bại để bố trí nhỏ, khối bộ nhớ. Như là một vấn đề thực tế, một phân bổ cho các yêu cầu, nói rằng, 32 byte là không bao giờ đi đến thất bại. Nếu bộ nhớ thường là những mặt, hệ thống sẽ được chạy như vậy sluggishly chắc rằng ai đó sẽ khởi động lại máy

Bạn phải không phải là nguyên nhân của một hệ thống sụp đổ trong tình hình này, tuy nhiên, vì bạn do đó sẽ được các nguồn tiềm năng của một từ chối-của-dịch vụ khai thác.

Nhưng từ khi có lỗi sẽ không phát sinh trong thực tế đời sống, không có điểm trong xây dựng đưa vào mã của bạn để đăng nhập lỗi trình điều khiển, tín hiệu WMI các sự kiện, in gỡ lỗi tin nhắn, thực hiện các thuật toán thay thế, và như vậy trên. Thật vậy, thêm mã cần thiết để làm tất cả mà có thể là lý do hệ thống không có thêm 32 byte để cung cấp cho bạn là người đầu tiên ở nơi! Vì vậy, tôi khuyên bạn nên kiểm tra các giá trị trả ra tất cả các cuộc gọi đến từ ExAllocatePoolWithTag.

Nếu nó discloses một lỗi, làm bất cứ yêu cầu và sự trở lại một mã trạng thái. Khoảng thời gian.

Để giải phóng bộ nhớ được bạn cấp phát trước đó với ExAllocatePoolWithTag, bạn gọi ExFreePool.

ExFreePool((PVOID) p);

Bạn cần giữ những rãnh ghi chưa được xác định của bộ nhớ mà bạn có , được cấp phát trước đó từ bộ nhớ chung để giải phóng nó khi nó không còn cần. Không ai khác sẽ làm điều đó cho bạn. Thỉnh thoảng bạn phải đọc tài liệu DDK của những hàm bạn gọi với một cái nhìn về quyền sở hữu bộ nhớ. Chẳng hạn, trong hàm AddDevice tôi cho thấy nó trong chương trước , ở đó nó được gọi tới IoRegisterDeviceInterface. Hàm đó có một tác dụng phụ: nó cấp phát một khối bộ nhớ chứa chuỗi có tên là giao diện(interface). Bạn sẽ phải giải phóng bộ nhớ đó sau này. Thiết bị kiểm chứng sẽ kiểm tra thời gian DriverUnload đảm bảo đã giải phóng tất cả bộ nhớ nó đã cấp phát . Ngoài ra, việc kiểm tra diễn ra đúng đắn thiết bị kiểm chứng đều gọi tới ExFreePool để chắc chắn rằng họ tham chiếu tới tất cả bộ nhớ được cấp phát từ một vùng nhất định với IRQL hiện hành . Phần đầu DDK khai báo một hàm undocumented có tên ExFreePoolWithTag. Hàm này được dự định sự sử dụng nội bộ để làm chắc chắn những thành phần hệ thống đã không được giải phóng bộ nhớ thuộc về những thành phần khác . Hàm đã thật sự được gọi là một " tiêu phí của thời gian " bởi một trong những người phát triển Microsoft, mà khá nhiều người nói rằng chúng tôi không cần lo lắng phải làm nó hay sử dụng nó như thế nào. (Gợi ý: bạn cần làm một số thứ undocumented khác để sử dụng nó một cách thành công.)

Những đối sốlà tương tự như chúng tôi đang đựơc học trừ những cái bạn cung cấp thêm ưu tiên chỉ báo. Nhìn Bảng 3-5.

DDK cho biết rằng đa số những thiết bị điều khiển cần phải chỉ rõ NormalPoolPriority khi gọi đến hàm này. HighPoolPriorityshould được dành riêng cho những trạng thái mà sự thành công là nguy kịch quan trọng đối với sự hoạt động liên tục của hệ thống. Bạn có thể bổ xung bằng mệnh đề SpecialPoolOverrun và SpecialPoolUnderrun vào trong Bảng 3-5 ( chẳng hạn,LowPoolưPrioritySpecialPoolOverrun, vân vân). Nếu một sự cấp phát sử dụng vùng đặc biệt, cờ tràn và những cờ underrun bị ghi đè một cách mặc định . Lúc đó tôi viết điều này,ExAllocatePoolWithTagPriority biến chuyển và trở lên đơn giản được gọi là ExAllocatePoolWithTag nếu bạn yêu cầu được đánh số trang bộ nhớ ở mức ưu tiên đặc biệt hay bất kỳ quyền ưu tiên nào. Việc kiểm tra thêm tài nguyên chỉ xảy ra với những yêu cầu đánh số trang bộ nhớ có quyền ưu tiên thấp hay bình thường. Hành vi này đã có thể thay đổi trong những gói dịch vụ hay những phiên bản sau của hệ điều hành.

Pseudofunction sau đây minh họa thao tác như thế nào danh sách đơn liên kết

  typedef struct _ONEWAY {

  SINGLE_LIST_ENTRY linkfield;

  } ONEWAY, *PONEWAY;

  SINGLE_LIST_ENTRY SingleHead;

  SingleHead.Next = NULL;

  PONEWAY psElement = (PONEWAY) ExAllocatePool(PagedPool,

    sizeof(ONEWAY));

PushEntryList(&SingleHead, &psElement->linkfield);

  SINGLE_LIST_ENTRY psLink = PopEntryList(&SingleHead);

  if (psLink)

    {

    psElement = CONTAINING_RECORD(psLink, ONEWAY, linkfield);

    ExFreePool(psElement);

    }

1. Thay vì việc kéo theo một hàm dịch vụ vận hành để khởi tạo phần đầu danh sách liên kết đơn, chỉ thiết lập trường kế tiếp hướng tới NULL.. Chú ý sự thiếu vắng của một hàm dịch vụ cho việc thử nghiệm liệu danh sách này trống rỗng; chỉ kiểm tra chính phần tử kế tiếp PushEntryList đặt một phần tử tại đầu của danh sách, mà nó là phần duy nhất của danh sách có thể trực tiếp tiếp cận

2. PushEntryList đã nhận một yếu tố tại các đầu trong danh sách, đó chỉ là một phần của danh sách đó là có thể truy cập trực tiếp. Thông báo mà bạn chỉ định địa chỉ của nhúng thay vì các lĩnh vực liên kết riêng của bạn ONEWAY cơ cấu.

3. PopEntryList loại bỏ những mục đầu tiên trong danh sách và sẽ mang lại cho bạn một trỏ lại để các lĩnh vực liên kết bên trong nó. Ngược lại với danh sách liên kết-gấp đôi, một giá trị NULL cho thấy rằng trong danh sách là không có sản phẩm nào. Trong thực tế, không có nước để IsListEmpty để sử dụng với một danh sách liên kết-singly.

Thậm chí tốt nhất có thể sử dụng các thuật toán, một người quản lý heap ngẫu nhiên mà chúng tôi kinh doanh với kích cỡ khối bộ nhớ sẽ yêu cầu một số hiếm thời gian để xử lý coalesce liền kề miễn phí từ các khối thời gian. Hình 3-10 minh hoạ như thế nào, cái gì khi trở về khối B vào heap tại một thời gian khi khối A và C đã và đang miễn phí, các heap quản lý có thể kết hợp khối A, B, và C để tạo thành một khối duy lớn

Các khối lớn, sau đó sẽ có sẵn để đáp ứng một số yêu cầu sau cho một khối lớn hơn bất kỳ một trong ba thành phần gốc

Hình 3-10. Coalescing adjacent free blocks in a heap.

Nếu bạn biết bạn luôn luôn có được làm việc với kích thước cố định-khối bộ nhớ, bạn có thể thủ công một cách hiệu quả hơn nhiều chương trình để quản lý một heap. Bạn có thể, ví dụ, preallocate một khối lớn bộ nhớ rằng bạn chia thành từng phần cho các kích thước cố định. Sau đó, bạn có thể devise một số chương trình cho biết khối đó là miễn phí và được sử dụng trong, như đề nghị do Hình 3-11.

Để lại một khối như một heap chỉ bao gồm việc đánh dấu nó như miễn phí-bạn không cần phải coalesce nó liền kề với khối bởi vì bạn không bao giờ cần phải ngẫu nhiên kích thước đáp ứng yêu cầu.

Hình 3-11. A-heap chứa cố định kích cỡ khối.

Hình 3-12 minh hoạ khái niệm của một lookaside danh sách. Hãy tưởng tượng rằng bạn đã có một ly mà bạn có thể (nào đó-pháp luật của Vật lý không chính xác làm cho việc này dễ dàng!) Số dư ngay thẳng, trong một hồ pool

Bottom of Form

Hệ thống định kỳ điều chỉnh sâu của tất cả các lookaside danh sách dựa trên thực tế sử dụng. Các chi tiết của các thuật toán không thật sự quan trọng, và chúng tôi có thể thay đổi trong bất kỳ trường hợp nào. Về cơ bản (trong bản phát hành hiện tại, anyway), hệ thống sẽ giảm chiều sâu của lookaside danh sách mà không có được truy cập trong thời gian gần đây hay không được truy cập pool buộc ít nhất 5 phần trăm của thời gian. Sâu không bao giờ đi dưới đây 4, tuy nhiên, đó cũng là chiều sâu ban đầu của một danh sách mới.

Bảng 3-8 liệt kê các dịch vụ tám hàm mà bạn sử dụng khi bạn làm việc với một lookaside danh sách. Có thật sự hai bộ bốn hàm, một đặt cho một danh sách lookaside rằng paged quản lý bộ nhớ (các ExXxxPagedLookasideList đặt) và một cho một danh sách lookaside rằng nonpaged quản lý bộ nhớ (ExXxxNPagedLookasideList các thiết lập). Điều đầu tiên bạn phải làm là đặt nonpaged bộ nhớ cho một PAGED_LOOKASIDE_LIST hoặc một đối tượng NPAGED_LOOKASIDE_LIST

Ngay cả các paged nhiều đối tượng cần phải được nonpaged trong bộ nhớ vì hệ thống sẽ truy cập vào danh sách đối tượng chính nó tại một cao IRQL.

Blocksize các tham số là kích cỡ của khối bộ nhớ bạn sẽ được phân bổ từ danh sách, và từ khóa là 32-bit, giá trị từ khóa bạn muốn đặt ở phía trước của mỗi khối như vậy. Hai đối số không được placeholders cho các giá trị mà bạn cung cấp trong phiên bản trước của Windows NT, nhưng mà bây giờ xác định trên hệ thống riêng của mình; những giá trị đều có cờ để kiểm soát loại phân bổ và độ sâu của lookaside danh sách.

Để bố trí một bộ nhớ khối từ danh sách, gọi điện cho phù hợp AllocateFrom hàm:

PVOID p = ExAllocateFromPagedLookasideList(pagedlist);

PVOID q = ExAllocateFromNPagedLookasideList(nonpagedlist);

To put a block back onto the list, call the appropriate FreeTo function:

ExFreeToPagedLookasideList(pagedlist, p);

ExFreeToNPagedLookasideList(nonpagedlist, q);

Finally, to destroy a list, call the appropriate Delete function:

ExDeletePagedLookasidelist(pagedlist);

ExDeleteNPagedLookasideList(nonpagedlist);

Nếu bạn thực hiện lỗi này, trong thời gian tới các hệ thống chạy qua, một loạt các danh sách các lookaside để điều chỉnh các danh sách của họ sâu, nó sẽ đặt chân xuống tại chỗ, nơi đối tượng danh sách của bạn được sử dụng để được, có lẽ với kết quả xấu.

0