Windows核心编程 第十五章 在应用程序中使用虚拟内存
第1?5章?在應用程序中使用虛擬內存
????Wi?n?d?o?w?s提供了3種進行內存管理的方法,它們是:
??????虛擬內存,最適合用來管理大型對象或結構數組。
??????內存映射文件,最適合用來管理大型數據流(通常來自文件)以及在單個計算機上運行的多個進程之間共享數據。
??????內存堆棧,最適合用來管理大量的小對象。
本章將要介紹第一種方法,即虛擬內存。內存映射文件和堆棧分別在第?1?7章和第1?8章介紹。用于管理虛擬內存的函數可以用來直接保留一個地址空間區域,將物理存儲器(來自頁文件)提交給該區域,并且可以設置你自己的保護屬性。
5.1?在地址空間中保留一個區域
??通過調用Vi?r?t?u?a?l?A?l?l?o?c函數,可以在進程的地址空間中保留一個區域:
? ? 第一個參數p?v?A?d?d?r?e?s?s包含一個內存地址,用于設定想讓系統將地址空間保留在什么地方。在大多數情況下,你為該參數傳遞NU?L?L。它告訴Vi?r?t?u?a?l?A?l?l?o?c,保存著一個空閑地址區域的記錄的系統應該將區域保留在它認為合適的任何地方。系統可以從進程的地址空間的任何位置來保留一個區域,因為不能保證系統可以從地址空間的底部向上或者從上面向底部來分配各個區域。可以使用M?E?M?_?TO?P?_?D?O?W?N標志來說明該分配方式。這個標志將在本章的后面加以介紹。
? ? 對大多數程序員來說,能夠選擇一個特定的內存地址,并在該地址保留一個區域,這是個非同尋常的想法。當你在過去分配內存時,操作系統只是尋找一個其大小足以滿足需要的內存塊,并分配該內存塊,然后返回它的地址。但是,由于每個進程有它自己的地址空間,因此可以設定一個基本內存地址,在這個地址上讓操作系統保留地址空間區域。
? ? 例如,你想將一個從50?MB開始的區域保留在進程的地址空間中。這時,可以傳遞52?428?800(5?0?×?1?0?2?4?×?1?0?2?4)作為p?v?A?d?d?r?e?s?s參數。如果該內存地址有一個足夠大的空閑區域滿足你的要求,那么系統就保留這個區域并返回。如果在特定的地址上不存在空閑區域,或者如果空閑區域不夠大,那么系統就不能滿足你的要求,Vi?r?t?u?a?l?A?l?l?o?c函數返回N?U?L?L。注意,為p?v?A?d?d?r?e?s?s參數傳遞的任何地址必須始終位于進程的用戶方式分區中,否則對?Vi?r?t?u?a?l?A?l?l?o?c函數的調用就會失敗,導致它返回N?U?L?L。
? ? 第1?3章講過,地址空間區域總是按照分配粒度的邊界來保留的(迄今為止在所有的Wi?n?d?o?w?s環境下均是6?4?K?B?)。因此,如果試圖在進程地址空間中保留一個從19?668?992(300?×?65?536?+?8192)這個地址開始的區域,系統就會將這個地址圓整為?6?4?K?B的倍數,然后保留從19?660?800(3?0?0×?65?536)這個地址開始的區域。
? ? 如果Vi?r?t?u?a?l?A?l?l?o?c函數能夠滿足你的要求,那么它就返回一個值,指明保留區域的基地址。如果傳遞一個特定的地址作為?Vi?r?t?u?a?l?A?l?l?o?c的p?v?A?d?d?r?e?s?s參數,那么該返回值與傳遞給Vi?r?t?u?a?l?A?l?l?o?c的值相同,并被圓整為(如果需要的話)6?4?K?B邊界值。
? ? Vi?r?t?u?a?l?A?l?l?o?c函數的第二個參數是d?w?S?i?z?e,用于設定想保留的區域的大小(以字節為計量單位)。由于系統保留的區域始終必須是?C?P?U頁面大小的倍數,因此,如果試圖保留一個跨越6?2?K?B的區域,結果就會在使用?4?KB、8?KB或16?KB頁面的計算機上產生一個跨越?6?4?K?B的區域。
? ? Vi?r?t?u?a?l?A?l?l?o?c函數的第三個參數是f?d?w?A?l?l?o?c?a?t?i?o?n?Ty?p?e,它能夠告訴系統你想保留一個區域還是提交物理存儲器(這樣的區分是必要的,因為Vi?r?t?u?a?l?A?l?l?o?c函數也可以用來提交物理存儲器)。若要保留一個地址空間區域,必須傳遞?M?E?M?_?R?E?S?E?RV?E標識符作為F?d?w?A?l?l?o?c?a?t?i?o?n?Ty?p?e參數的值。
? ? 如果保留的區域預計在很長時間內不會被釋放,那么可以在盡可能高的內存地址上保留該區域。這樣,該區域就不會從進程地址空間的中間位置上進行保留。因為在這個位置上它可能導致區域分成碎片。如果想讓系統在最高內存地址上保留一個區域,必須為?p?v?A?d?d?r?e?s?s參數和f?d?w?A?l?l?o?c?a?t?i?o?n?Ty?p?e參數傳遞?N?U?L?L,還必須逐位使用?O?R將M?E?M?_?TO?P?_?D?O?W?N標志和M?E?M?_?R?E?S?E?RV?E標志連接起來。
? ? 注意?在Windows?98下,M?E?M?_?TO?P?_?D?O?W?N標志將被忽略。
? ? 最后一個參數是f?d?w?P?r?o?t?e?c?t,用于指明應該賦予該地址空間區域的保護屬性。與該區域相關聯的保護屬性對映射到該區域的已提交內存沒有影響。無論賦予區域的保護屬性是什么,如果沒有提交任何物理存儲器,那么訪問該范圍中的內存地址的任何企圖都將導致該線程引發一個訪問違規。
? ? 當保留一個區域時,應該為該區域賦予一個已提交內存最常用的保護屬性。例如,如果打算提交的物理存儲器的保護屬性是?PA?G?E?_?R?E?A?D?W?R?I?T?E(這是最常用的保護屬性),那么應該用PA?G?E?_?R?E?A?D?W?R?I?T?E保護屬性來保留該區域。當區域的保護屬性與已提交內存的保護屬性相匹配時,系統保存的內部記錄的運行效率最高。
? ? 可以使用下列保護屬性中的任何一個:?PA?G?E?_?N?O?A?C?C?E?S?S、PA?G?E?_?R?E?A?D?W?R?I?T?E、PA?G?E?_?R?E?A?D?O?N?LY、PA?G?E?_?E?X?E?C?U?T?E、PA?G?E?_?E?X?E?C?U?T?E?_?R?E?A?D或PA?G?E?_?E?X?E?C?U?T?E?_R?E?A?D?W?R?I?T?E。但是,既不能設定?PA?G?E?_?W?R?I?T?E?C?O?P?Y屬性,也不能設定PA?G?E?_?E?X?E?C?U?T?E?_W?R?I?T?E?C?O?P?Y屬性。如果設定了這些屬性,Vi?r?t?u?a?l?A?l?l?o?c函數將不保留該區域,并且返回N?U?L?L。另外,當保留地址空間區域時,不能使用保護屬性標志?PA?G?E?_?G?U?A?R?D,PA?G?E?_?N?O?C?A?C?H?E或PA?G?E?_?W?R?I?T?E?C?O?M?B?I?N?E,這些標志只能用于已提交的內存。
注意?Windows?98只支持PA?G?E?_?N?O?A?C?C?E?S?S、PA?G?E?_?R?E?A?D?O?N?LY和PA?G?E?_?R?E?A?D?W?R?I?T?E保護屬性。如果試圖保留使用?PA?G?E?_?E?X?E?C?U?T?E或PA?G?E?_?E?X?E?C?U?T?E?_?R?E?A?D兩個保護屬性的區域,將會產生一個帶有?PA?G?E?_?R?E?A?D?O?N?LY保護屬性的區域。同樣,如果保留一個使用?PA?G?E?_?E?X?E?C?U?T?E?_?R?E?A?D?W?R?I?T?E保護屬性的區域,就會產生一個帶有PA?G?E?_?R?E?A?D?W?R?I?T?E保護屬性的區域。
15.2?在保留區域中的提交存儲器
? ? 當保留一個區域后,必須將物理存儲器提交給該區域,然后才能訪問該區域中包含的內存地址。系統從它的頁文件中將已提交的物理存儲器分配給一個區域。物理存儲器總是按頁面邊界和頁面大小的塊來提交的。
? ? 若要提交物理存儲器,必須再次調用Vi?r?t?u?a?l?A?l?l?o?c函數。不過這次為f?d?w?A?l?l?o?c?a?t?i?o?n?Ty?p?e參數傳遞的是M?E?M?_?C?O?M?M?I?T標志,而不是M?E?M?_?R?E?S?E?RV?E標志。傳遞的頁面保護屬性通常與調用Vi?r?t?u?a?l?A?l?l?o?c來保留區域時使用的保護屬性相同(大多數情況下是?PA?G?E?_?R?E?A?D?W?R?I?T?E),不過也可以設定一個不同的保護屬性。
? ? 在已保留的區域中,你必須告訴Vi?r?t?u?a?l?A?l?l?o?c函數,你想將物理存儲器提交到何處,以及要提交多少物理存儲器。為了做到這一點,可以在?p?v?A?d?d?r?e?s?s參數中設定你需要的內存地址,并在d?w?S?i?z?e參數中設定物理存儲器的數量(以字節為計量單位)。注意,不必立即將物理存儲器提交給整個區域。
? ? 下面讓我們來看一個如何提交物理存儲器。比如說,你的應用程序是在?x86?CPU上運行的,該應用程序保留了一個從地址?5?242?880開始的512?KB的區域。你想讓應用程序將物理存儲器提交給已保留區域的6?KB部分,從2?KB的地方開始,直到已保留區域的地址空間。為此,可以調用帶有M?E?M?_?C?O?M?M?I?T標志的Vi?r?t?u?a?l?A?l?l?o?c函數,如下所示:
?
? ? 在這個例子中,系統必須提交8?KB的物理存儲器,地址范圍從5?242?880到5?251?071?(5?242880?+?8?KB??-?1字節)。這兩個提交的頁面都擁有PA?G?E?_?R?E?A?D?W?R?I?T?E保護屬性。保護屬性只以整個頁面為單位來賦予。同一個內存頁面的不同部分不能使用不同的保護屬性。然而,區域中的一個頁面可以使用一種保護屬性(比如?PA?G?E?_?R?E?A?D?W?R?I?T?E),而同一個區域中的另一個頁面可以使用不同的保護屬性(比如PA?G?E?_?R?E?A?D?O?N?LY)。
15.3?同時進行區域的保留和內存的提交
有時你可能想要在保留區域的同時,將物理存儲器提交給它。只需要一次調用?Vi?r?t?u?a?l?A?l?l?o?c函數就能進行這樣的操作,如下所示:
?
? ? 這個函數調用請求保留一個?99?KB的區域,并且將99?KB的物理存儲器提交給它。當系統處理這個函數調用時,它首先要搜索你的進程的地址空間,找出未保留的地址空間中一個地址連續的區域,它必須足夠大,能夠存放100?KB(在4?KB頁面的計算機上)或104?KB(在8?KB頁面的計算機上)。
? ? 系統之所以要搜索地址空間,原因是已將?p?v?A?d?d?r?e?s?s參數設定為N?U?L?L。如果為p?v?A?d?d?r?e?s?s設定了內存地址,系統就要查看在該內存地址上是否存在足夠大的未保留地址空間。如果系統找不到足夠大的未保留地址空間,Vi?r?t?u?a?l?A?l?l?o?c將返回N?U?L?L,
? ? 如果能夠保留一個合適的區域,系統就將物理存儲器提交給整個區域。無論是該區域還是提交的內存,都將被賦予PA?G?E?_?R?E?A?D?W?R?I?T?E保護屬性。
? ? 最后需要說明的是,Vi?r?t?u?a?l?A?l?l?o?c將返回保留區域和提交區域的虛擬地址,然后該虛擬地址被保存在p?v?M?e?m變量中。如果系統無法找到足夠大的地址空間,或者不能提交該物理存儲器,Vi?r?t?u?a?l?A?l?l?o?c將返回N?U?L?L。
? ? 當用這種方式來保留一個區域和提交物理存儲器時,將特定的地址作為?p?v?A?d?d?r?e?s?s參數傳遞給?Vi?r?t?u?a?l?A?l?l?o?c當然是可能的。否則就必須用?O?R將M?E?M?_?TO?P?_?D?O?W?N?標志與f?d?w?A?l?l?o?c?a?t?i?o?n?Ty?p?e參數連接起來,并為p?v?A?d?d?r?e?s?s參數傳遞N?U?L?L,讓系統在進程的地址空間的頂部選定一個適當的區域。
15.4?何時提交物理存儲器
? ? 假設想實現一個電子表格應用程序,這個電子表格為?2?0?0行?x?256列。對于每一個單元格,都需要一個C?E?L?L?D?ATA結構來描述單元格的內容。若要處理這種二維單元格矩陣,最容易的方法是在應用程序中聲明下面的變量:
?
? ? 如果C?E?L?L?D?ATA結構的大小是1?2?8字節,那么這個二維矩陣將需要6?553?600(200?x?256?x1?2?8)個字節的物理存儲器。對于電子表格來說,如果直接用頁文件來分配物理存儲器,那么這是個不小的數目了,尤其是考慮到大多數用戶只是將信息放入少數的單元格中,而大部分單元格卻空閑不用,因此顯得有些浪費。內存的利用率非常低。
? ? 傳統上,電子表格一直是用其他數據結構技術來實現的,比如鏈接表等。使用鏈接表,只需要為電子表格中實際包含數據的單元格創建C?E?L?L?D?ATA結構。由于電子表格中的大多數單元格都是不用的,因此這種方法可以節省大量的內存。但是這種方法使得你很難獲得單元格的內容。如果想知道第5行第1?0列的單元格的內容,必須遍歷鏈接表,才能找到需要的單元格,因此使用鏈接表方法比明確聲明的矩陣方法速度要慢。
? ? 虛擬內存為我們提供了一種兼顧預先聲明二維矩陣和實現鏈接表的兩全其美的方法。運用虛擬內存,既可以使用已聲明的矩陣技術進行快速而方便的訪問,又可以利用鏈接表技術大大節省內存的使用量。
? ? 如果想利用虛擬內存技術的優點,你的程序必須按照下列步驟來編寫:
? ? 1)?保留一個足夠大的地址空間區域,用來存放?C?E?L?L?D?ATA結構的整個數組。保留一個根本不使用任何物理存儲器的區域。
? ? 2)?當用戶將數據輸入一個單元格時,找出?C?E?L?L?D?ATA結構應該進入的保留區域中的內存地址。當然,這時尚未有任何物理存儲器被映射到該地址,因此,訪問該地址的內存的任何企圖都會引發訪問違規。
? ? 3)?就C?E?L?L?D?ATA結構來說,只將足夠的物理存儲器提交給第二步中找到的內存地址(你可以告訴系統將物理存儲器提交給保留區域的特定部分,這個區域既可以包含映射到物理存儲器的各個部分,也可以包含沒有映射到物理存儲器的各個部分)。
? ? 4)?設置新的C?E?L?L?D?ATA結構的成員。
? ? 現在物理存儲器已經映射到相應的位置,你的程序能夠訪問內存,而不會引發訪問違規。
這個虛擬內存技術非常出色,因為只有在用戶將數據輸入電子表格的單元格時,才會提交物理存儲器。由于電子表格中的大多數單元格是空的,因此大部分保留區域沒有提交給它的物理存儲器。
? ? 虛擬內存技術存在的一個問題是,必須確定物理存儲器在何時提交。如果用戶將數據輸入一個單元格,然后只是編輯或修改該數據,那么就沒有必要提交物理存儲器,因為該單元格的C?E?L?L?D?ATA結構的內存在數據初次輸入時就已經提交了。
? ? 另外,系統總是按頁面的分配粒度來提交物理存儲器的。因此,當試圖為單個?C?E?L?L?D?ATA結構提交物理存儲器時(像上面的第二步那樣),系統實際上提交的是內存的一個完整的頁面。這并不像它聽起來那樣十分浪費:為單個C?E?L?L?D?ATA結構提交物理存儲器的結果是,也要為附近的其他C?E?L?L?D?ATA結構提交內存。如果這時用戶將數據輸入鄰近的單元格(這是經常出現的情況),就不需要提交更多的物理存儲器。
有4種方法可以用來確定是否要將物理存儲器提交給區域的一個部分:
? ? ??始終設法進行物理存儲器的提交。每次調用?Vi?r?t?u?a?l?A?l?l?o?c函數的時候,不要查看物理存儲器是否已經映射到地址空間區域的一個部分,而是讓你的程序設法進行內存的提交。系統首先查看內存是否已經被提交,如果已經提交,那么就不要提交更多的物理存儲器。
這種方法最容易操作,但是它的缺點是每次改變?C?E?L?L?D?ATA結構時要多進行一次函數的
調用,這會使程序運行得比較慢。
??????(使用Vi?r?t?u?a?l?Q?u?e?r?y函數)確定物理存儲器是否已經提交給包含C?E?L?L?D?ATA結構的地址空間。如果已經提交了,那么就不要進行任何別的操作。如果尚未提交,則可以調用Vi?r?t?u?a?l?A?l?l?o?c函數以便提交內存。這種方法實際上比第一種方法差,它既會增加代碼的長度,又會降低程序運行的速度(因為增加了對Vi?r?t?u?a?l?A?l?l?o?c函數的調用)。
??????保留一個關于哪些頁面已經提交和哪些頁面尚未提交的記錄。這樣做可以使你的應用程序運行得更快,因為不必調用?Vi?r?t?u?a?l?A?l?l?o?c函數,你的代碼能夠比系統更快地確定內存是否已經被提交。它的缺點是,必須不斷跟蹤頁面提交的信息,這可能非常簡單,也可能非常困難,要根據你的情況而定。
??????使用結構化異常處理(?S?E?H)方法,這是最好的方法。?S?E?H是一個操作系統特性,它使系統能夠在發生某種情況時將此情況通知你的應用程序。實際上可以創建一個帶有異常
處理程序的應用程序,然后,每當試圖訪問未提交的內存時,系統就將這個問題通知應用程序。然后你的應用程序便進行內存的提交,并告訴系統重新運行導致異常條件的指令。這時對內存的訪問就能成功地進行了,程序將繼續運行,仿佛從未發生過問題一樣。這種方法是優點最多的方法,因為需要做的工作最少(也就是說要你編寫的代碼比較少),同時,你的程序可以全速運行。關于?S?E?H的全面介紹,請參見第2?3、2?4和2?5章。第2?5章中的電子表格示例應用程序說明了如何按照上面介紹的方法來使用虛擬內存。
15.5?回收虛擬內存和釋放地址空間區域
? ? 若要回收映射到一個區域的物理存儲器,或者釋放這個地址空間區域,可調用?Vi?r?t?u?a?l?F?r?e?e函數:
? ? 首先讓我們觀察一下調用Vi?r?t?u?a?l?F?r?e?e函數來釋放一個已保留區域的簡單例子。當你的進程不再訪問區域中的物理存儲器時,就可以釋放整個保留的區域和所有提交給該區域的物理存儲器,方法是一次調用Vi?r?t?u?a?l?F?r?e?e函數。
? ? 就這個函數的調用來說,?p?v?A?d?d?r?e?s?s參數必須是該區域的基地址。此地址與該區域被保留時Vi?r?t?u?a?l?A?l?l?o?c函數返回的地址相同。系統知道在特定內存地址上的該區域的大小,因此可以為d?w?S?i?z?e參數傳遞0。實際上,必須為d?w?S?i?z?e參數傳遞0,否則對Vi?r?t?u?a?l?F?r?e?e的調用就會失敗。對于第三個參數f?d?w?F?r?e?e?Ty?p?e,必須傳遞M?E?M?_?R?E?L?E?A?S?E,以告訴系統將所有映射的物理存儲器提交給該區域并釋放該區域。當釋放一個區域時,必須釋放該區域保留的所有地址空間。例如不能保留一個128?KB的區域,然后決定只釋放它的64?KB。必須釋放所有的128?KB。
? ? 當想要從一個區域回收某些物理存儲器,但是卻不釋放該區域時,也可以調用?Vi?r?t?u?a?l?F?r?e?e函數,若要回收某些物理存儲器,必須在Vi?r?t?u?a?l?F?r?e?e函數的p?v?A?d?d?r?e?s?s參數中傳遞用于標識要回收的第一個頁面的內存地址,還必須在?d?w?S?i?z?e參數中設定要釋放的字節數,并在?f?d?w?F?r?e?e?Ty?p?e參數中傳遞M?E?M?_?D?E?C?O?M?M?I?T標志。
? ? 與提交物理存儲器的情況一樣,回收時也必須按照頁面的分配粒度來進行。這就是說,設
定頁面中間的一個內存地址就可以回收整個頁面。當然,如果?pvAddress?+?dwSize的值位于一個頁面的中間,那么包含該地址的整個頁面將被回收。因此位于?pvAddress?至pvAddress?+d?w?S?i?z?e范圍內的所有頁面均被回收。
? ? 如果d?w?S?i?z?e是0,p?v?S?d?d?r?e?s?s是已分配區域的基地址,那么?Vi?r?t?u?a?l?F?r?e?e將回收全部范圍內的已分配頁面。當物理存儲器的頁面已經回收之后,已釋放的物理存儲器就可以供系統中的所有其他進程使用,如果試圖訪問未回收的內存,將會造成訪問違規。
15.6?改變保護屬性
? ? 雖然實踐中很少這樣做,但是可以改變已經提交的物理存儲器的一個或多個頁面的保護屬性。例如,你編寫了一個用于管理鏈接表的代碼,將它的節點存放在一個保留區域中。可以設計一些函數,以便處理該鏈接表,這樣,它們就可以在每個函數開始運行時將已提交內存的保護屬性改為?PA?G?E?_?R?E?A?D?W?R?I?T?E,然后在每個函數終止運行時將保護屬性重新改為PA?G?E?_?N?O?A?C?C?E?S?S。
? ? 通過這樣的設置,就能夠使鏈接表數據不受隱藏在程序中的其他錯誤的影響。如果進程中的任何其他代碼存在一個迷失指針,試圖訪問你的鏈接表數據,那么就會引發訪問違規。當試圖尋找應用程序中難以發現的錯誤時,利用保護屬性是極其有用的。
? ? 若要改變內存頁面的保護屬性,可以調用Vi?r?t?u?a?l?P?r?o?t?e?c?t函數:
? ? 這里的p?v?A?d?d?r?e?s?s參數指向內存的基地址(它必須位于進程的用戶方式分區中),d?w?S?i?z?e參數用于指明你想要改變保護屬性的字節數,而?f?l?N?e?w?P?r?o?t?e?c?t參數則代表PA?G?E?_?*保護屬性標志中的任何一個標志,但PA?G?E?_?W?R?I?T?E?C?O?P?Y和PA?G?E?_?E?X?E?C?U?T?E?_?W?R?I?T?E?C?O?P?Y這兩個標志除外。
? ? 最后一個參數p?f?l?O?l?d?P?r?o?t?e?c?t是D?W?O?R?D的地址,Vi?r?t?u?a?l?P?r?o?t?e?c?t將用原先與p?v?A?d?d?r?e?s?s位置上的字節相關的保護屬性填入該D?W?O?R?D。盡管許多應用程序并不需要該信息,但是必須為該參數傳遞一個有效地址,否則該函數的運行將會失敗。
? ? 當然,保護屬性是與內存的整個頁面相關聯的,而不是賦予內存的各個字節的。因此,如
果要使用下面的代碼來調用?4?KB?頁面的計算機上的?Vi?r?t?u?a?l?P?r?o?t?e?c?t函數,其結果是把PA?G?E?_?N?O?A?C?C?E?S?S保護屬性賦予內存的兩個頁面:
? ? Windows?98?Windows?98只支持PA?G?E?_?R?E?A?D?O?N?LY和PA?G?E?_?R?E?A?D?W?R?I?T?E兩個保護屬性。如果試圖將頁面的保護屬性改為PA?G?E?_?E?X?E?C?U?T?E或PA?G?E?_?E?X?E?C?U?T?E?_?R?E?A?D,該頁面可得到PA?G?E?_?R?E?A?D?O?N?LY保護屬性。同樣,如果試圖將頁面的保護屬性改為PA?G?E?_?E?X?E?C?U?T?E?_?R?E?A?D?W?R?I?T?E,那么該頁面將得到PA?G?E?_?R?E?A?D?W?R?I?T?E保護屬性。
? ? Vi?r?t?u?a?l?P?r?o?t?e?c?t函數不能用于改變跨越不同保留區域的頁面的保護屬性。如果擁有相鄰的保留區域并想改變這些區域中的一些頁面的保護屬性,那么必須多次調用?Vi?r?t?u?a?l?P?r?o?t?e?c?t函數。
總結
以上是生活随笔為你收集整理的Windows核心编程 第十五章 在应用程序中使用虚拟内存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows核心编程 第十四章 虚拟内
- 下一篇: Windows核心编程 第十七章 -内存