天天看點

GlobalLock的前世今生1:古老年代

在很久很久以前,那個時候的作業系統還是Windows 1.0。當時确實是一段十分古老的年代,你可以看到那個時代的各種标志:段,近指針和遠指針,沒有虛拟記憶體,協作式多任務。

因為作業系統中沒有虛拟記憶體,如果作業系統向要執行交換(Swapping),作業系統必須和應用程式一起協作來完成。如果需要配置設定記憶體但是沒有一段連續的記憶體塊可供配置設定,則記憶體管理器隻能執行一項稱之為”壓縮”(Compaction)的操作,來確定一段連續的記憶體可供配置設定。

> 代碼段可以被完全地釋放,因為它們可以從原始執行程式中重新加載(因為沒有虛拟記憶體,就沒有所謂的頁換出)。 将代碼釋放需要一些額外的工作來確定下次代碼被調用的時候,它已經從記憶體中重新加載過了。如何確定這一點和今天文章的内容不太相關,但是它确實是一項非常複雜的過程。

> 包含有代碼的記憶體塊可以來回移動,而且對于舊位址的引用可以被修正以指向新的位址。這個過程也是十分複雜,在此就不在展開了。

> 包含有資料的記憶體塊可以來回移動,但是對舊位址的引用無法進行修正。應用程式有責任來在記憶體移動的時候確定指針指向正确的位置。

> 被鎖定或者固定式記憶體塊不會被移動。

當你使用GlobalAlloc的時候,你需要偶先做出一個決定,就是:是否希望配置設定的記憶體是可移動的(此記憶體可以由記憶體管理器來回移動)還是固定式的(記憶體不受移動的影響)。

從概念上來說,一塊固定式記憶體可以看做是一段永久性鎖定的可移動記憶體塊。

在那個年代,應用程式不被鼓勵配置設定固定式記憶體,因為它會影響記憶體管理器的操作。 (試想,碎片整理程式會面對這種不可移動的記憶體塊而無法工作。)

GlobalAlloc的傳回值是全局記憶體塊的句柄或HGLOBAL類型。 這個值本身是沒有用的。 你必須調用 GlobalLock() 才能将此 HGLOBAL 轉換為您可以使用的指針。

GlobalLock會執行如下的操作:

> 它強制記憶體送出(如果它之前被丢棄)。 可能需要丢棄或移動其他記憶體塊,進而為被鎖定的記憶體塊騰出空間。

> 如果記憶體塊是”可移動的”,那麼它還會增加記憶體塊上的”鎖計數”,進而防止記憶體管理器在壓縮期間移動記憶體塊。 (”固定”記憶體上的鎖定計數不是必需的,因為它們無論如何都無法移動。)

鼓勵應用程式僅在必要時鎖定全局記憶體塊,以避免将堆碎片化。 禁止指向未鎖定的可移動記憶體的指針,因為即使是最微小的操作——比如調用一個碰巧被丢棄的函數——都會導緻壓縮并使指針無效。

那麼,這一切又和GlobalReAlloc有什麼關聯呢?

它取決于記憶體是如何配置設定的,它的鎖狀态是什麼。它取決于記憶體是如何配置設定的,它的鎖狀态是什麼。

如果記憶體被配置設定為“可移動”并且它沒有被鎖定,那麼記憶體管理器被允許為系統中其他地方的記憶體找到一個新的家并更新它的簿記,以便下次有人調用 GlobalLock() 時,他們得到 指向新位置的指針。

如果記憶體被配置設定為“可移動”但它被鎖定,或者如果記憶體被配置設定為“固定”,那麼記憶體管理器隻能就地調整它的大小。 它無法移動記憶體,因為(如果可移動和鎖定)仍然有未完成的指針指向它,如非零鎖定計數所證明的那樣,或者(如果固定)在假設它永遠不會移動的情況下配置設定了固定記憶體。

如果記憶體被配置設定為“可移動”并被鎖定,或者如果它被配置設定為“固定”,那麼你可以傳遞 GMEM_MOVEABLE标志來覆寫“可能隻在原地調整大小”行為,在這種情況下,記憶體管理器将嘗試在必要時移動記憶體。 傳遞 GMEM_MOVEABLE 标志意味着,“不,真的,我知道根據規則,你不能移動記憶體,但我希望你無論如何都要移動它。 我保證負責更新所有指向舊位置的指針以指向新位置。”

總結

請直接使用new/delete,拒絕古老玩意兒。

但,有必要了解這段曆史。

最後

繼續閱讀