最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺聊一下C#中內(nèi)存映射文件的玩法

 更新時間:2023年06月14日 08:26:29   作者:一線碼農(nóng)  
內(nèi)存映射文件是怎么玩的,說實話這東西理論我相信很多朋友都知道,就是將文件映射到進程的虛擬地址,說起來很容易,那如何讓大家眼見為實呢,本文就來和大家簡單聊聊

一:背景

1. 講故事

前段時間訓(xùn)練營里有朋友問 內(nèi)存映射文件 是怎么玩的?說實話這東西理論我相信很多朋友都知道,就是將文件映射到進程的虛擬地址,說起來很容易,那如何讓大家眼見為實呢?可能會難倒很多人,所以這篇我以自己的認(rèn)知嘗試讓大家眼見為實。

二:如何眼見為實

1. 我想象的文件映射

在任何討論之前,內(nèi)存文件映射大概像下面這樣,多個進程可以完全View一個文件,也可以 View 文件的一部分到進程的虛擬地址中,畫個圖大概像下面這樣。

但仔細(xì)一想,這里還有很多的小細(xì)節(jié),比如:

疑問1:到底是映射文件還是映射磁盤的物理地址 ?

疑問2:既然是后備存儲,那是不是每次修改虛擬地址都要刷硬盤 ?

疑問3:內(nèi)存頁是4k為一個單位,文件大小不是 4k 整數(shù)倍怎么辦 ?

這三個疑問我相信很多朋友或多或少都會遇到,這里我簡單解答一下,后面再用 windbg 驗證。

  • 嚴(yán)格來說是 硬盤物理地址
  • 文件所處的硬盤地址為后備存儲這個不假,但這里有個小細(xì)節(jié),對虛擬地址的讀寫涉及到 內(nèi)存頁 概念,如果訪問的虛擬地址所在的物理地址不在 物理內(nèi)存 中,就會引發(fā)缺頁中斷,操作系統(tǒng)會將 磁盤上的 4k 頁粒度灌入到 物理內(nèi)存 中,同樣的道理,如果修改了虛擬地址,那么物理內(nèi)存頁就是臟數(shù)據(jù),會在后續(xù)的某個時刻刷新到 硬盤 上,產(chǎn)生磁盤 IO。

總的來說:從磁盤到物理內(nèi)存(內(nèi)存條) 之間的內(nèi)存頁的換入換出都是一種按需的 懶加載懶寫入行為,稍后我們用 windbg 驗證下。

內(nèi)存的管理采用的是內(nèi)存頁的方式,如果 View 大于 文件Size,那么文件會擴容到 4k 對齊,這樣方便對文件追加寫入。

綜合上面的三點信息,圖就可以畫的再詳細(xì)一點了,比如下面這樣:

熟悉內(nèi)存管理的朋友應(yīng)該知道,我們程序的 exe 和 dll 就是用 內(nèi)存映射文件 的方式加載到虛擬地址中的,所以就拿它開刀吧。

2. 一段測試代碼

為了方便演示,上一段簡單的的測試代碼,觀察 ConsoleApp1.exe 的映射方式。

        static void Main(string[] args)
        {
            Console.WriteLine($"當(dāng)前時間:{DateTime.Now}, 程序啟動!");
            Console.ReadLine();
        }

接下來用 windbg 啟動 ConsoleApp1.exe 兩次,結(jié)合詳細(xì)分解圖,我們觀察下這兩個進程的虛擬地址所映射的內(nèi)存條物理地址是否一致?

1.實例1

ModLoad: 00007ff6`bfe00000 00007ff6`bfe2a000   apphost.exe
ModLoad: 00007ff9`b1450000 00007ff9`b1648000   ntdll.dll
...
0:008> lmvm apphost
Browse full module list
start             end                 module name
00007ff6`bfe00000 00007ff6`bfe2a000   apphost  C (private pdb symbols)  c:\mysymbols\apphost.pdb\1643A9EB126F4FE184548E9CC1B740B71\apphost.pdb
    Loaded symbol image file: D:\net7\ConsoleApplication1\ConsoleApp1\bin\Debug\net6.0\ConsoleApp1.exe
    Image path: apphost.exe
    Image name: apphost.exe
    ...
0:008> ~
   0  Id: 232c.4abc Suspend: 1 Teb: 0000000e`7b1a5000 Unfrozen

2.實例2

ModLoad: 00007ff6`bfe00000 00007ff6`bfe2a000   apphost.exe
ModLoad: 00007ff9`b1450000 00007ff9`b1648000   ntdll.dll
...
0:008> ~
   0  Id: 60e8.3e3c Suspend: 1 Teb: 000000da`ab498000 Unfrozen
   1  Id: 60e8.53b0 Suspend: 1 Teb: 000000da`ab49a000 Unfrozen

這里要提醒一下的是在 Windows 平臺上 ConsoleApp1.exe 已經(jīng)成了一個引導(dǎo)程序,通過 lmvm 可以看到它其實是 apphost.exe。

兩個實例都開起來后,可以看到 apphost.exe 在各自進程的虛擬地址都一樣,那他們的物理地址是否也一樣呢? 要尋找答案,接下來我們到 Windows 內(nèi)核態(tài)去挖一挖。

lkd> !process 0 0 ConsoleApp1.exe
PROCESS ffff838bd84c9080
    SessionId: 8  Cid: 232c    Peb: e7b1a4000  ParentCid: 0b14
FreezeCount 2
    DirBase: 3468cf000  ObjectTable: ffff938feae02900  HandleCount: 172.
    Image: ConsoleApp1.exe
PROCESS ffff838bef157080
    SessionId: 8  Cid: 60e8    Peb: daab497000  ParentCid: 4804
FreezeCount 2
    DirBase: 3552f3000  ObjectTable: ffff938fe8f7ec40  HandleCount: 166.
    Image: ConsoleApp1.exe

從卦中看,Cid: 232c 是我們的實例1, Cid: 60e8 是我們的實例2,接下來用 windbg 提供的 !vtop 命令觀察 apphost.exe 的首地址對應(yīng)的物理地址。

// ----  實例1 -----
lkd> !vtop 3468cf000 00007ff6bfe00000
Amd64VtoP: Virt 00007ff6bfe00000, pagedir 00000003468cf000
Amd64VtoP: PML4E 00000003468cf7f8
Amd64VtoP: PDPE 00000001138dbed0
Amd64VtoP: PDE 00000002153dcff8
Amd64VtoP: PTE 000000024dadd000
Amd64VtoP: Mapped phys 00000002271c2000
Virtual address 7ff6bfe00000 translates to physical address 2271c2000.
//----  實例2 -----
lkd> !vtop 3552f3000 00007ff6bfe00000
Amd64VtoP: Virt 00007ff6bfe00000, pagedir 00000003552f3000
Amd64VtoP: PML4E 00000003552f37f8
Amd64VtoP: PDPE 00000002db7ffed0
Amd64VtoP: PDE 0000000208100ff8
Amd64VtoP: PTE 000000033de01000
Amd64VtoP: Mapped phys 00000002271c2000
Virtual address 7ff6bfe00000 translates to physical address 2271c2000.

從卦中看,物理地址上有一段 This program cannot be run in DOS mode,這不就是經(jīng)典的 PE 文件哈,如果不相信可以用 WinHex 打開 ConsoleApp1.exe 即可,截圖如下:

最后就是內(nèi)核中的 內(nèi)存管理器 會將 物理地址 與 磁盤地址 進行打通,實現(xiàn)懶加載和懶寫入。

3. 如何自定義實現(xiàn)

Image 雖然是一個快捷的觀察內(nèi)存文件映射方式,那如果自己能實現(xiàn)一個就更有意思了,比如下面對 1.txt 進行文件映射,在 C# 中有一個快捷類 MemoryMappedFile 實現(xiàn)了 win32api 的封裝,參考代碼如下:

    internal class Program
    {
        static void Main(string[] args)
        {
            int capaticy = 1024; //1k
            using (var mmf = MemoryMappedFile.CreateFromFile(@"C:\1.txt", FileMode.OpenOrCreate,
                                                            "testmapfile",
                                                             capaticy,
                                                             MemoryMappedFileAccess.ReadWrite))
            {
                var viewAccessor = mmf.CreateViewAccessor(0, capaticy);
                while (true)
                {
                    Console.WriteLine("請輸入你要寫入的內(nèi)容: ");
                    string input = Console.ReadLine();
                    viewAccessor.WriteArray(0, input.ToArray(), 0, input.Length);
                }
            }
        }
    }

接下來用 windbg 附加一下,觀察 1.txt 是不是被 MappedFile 上了,同時做的修改有沒有更新到物理磁盤上。

0:006> !address
  BaseAddr EndAddr+1 RgnSize     Type       State                 Protect             Usage
-----------------------------------------------------------------------------------------------
...
+  31a0000  31a1000     1000 MEM_MAPPED  MEM_COMMIT  PAGE_READWRITE                     MappedFile "\Device\HarddiskVolume3\1.txt"
...
0:006> du 31a0000
031a0000  "helloworld!"

從卦中可以看到,雖然 1.txt 最大的 View 區(qū)間是 1k,但提交的內(nèi)存頁還是按照最小粒度 4k 給的。

到此這篇關(guān)于淺聊一下C#中內(nèi)存映射文件的玩法的文章就介紹到這了,更多相關(guān)C#內(nèi)存映射文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Unity切割圖集轉(zhuǎn)換為多張圖片

    Unity切割圖集轉(zhuǎn)換為多張圖片

    這篇文章主要為大家詳細(xì)介紹了Unity切割圖集轉(zhuǎn)換為多張圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • C#高效實現(xiàn)復(fù)制Word文檔內(nèi)容

    C#高效實現(xiàn)復(fù)制Word文檔內(nèi)容

    在日常辦公或企業(yè)級文檔處理中,我們經(jīng)常需要復(fù)制 Word 文檔的內(nèi)容,本文將基于 Free Spire.Doc for .NET 免費庫,詳細(xì)介紹如何在 C# 中實現(xiàn) Word 內(nèi)容的復(fù)制,希望對大家有所幫助
    2026-03-03
  • C#泛型類型知識講解

    C#泛型類型知識講解

    這篇文章主要介紹了C#泛型類型知識,文中代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • C#使用CancellationTokenSource 取消 Task的方法

    C#使用CancellationTokenSource 取消 Task的方法

    因為涉及到了日常經(jīng)常會碰到的取消任務(wù)操作,本文主要介紹了C#使用CancellationTokenSource 取消 Task,文中通過代碼介紹的非常詳細(xì),感興趣的可以了解一下
    2022-02-02
  • Winform中GridView分組排序功能實現(xiàn)方法

    Winform中GridView分組排序功能實現(xiàn)方法

    這篇文章主要介紹了Winform中GridView分組排序功能實現(xiàn)方法,以實例形式詳細(xì)說明了分組排序的實現(xiàn)方法,并附帶完整的DBHelp類代碼,是非常實用的技巧,需要的朋友可以參考下
    2014-11-11
  • C#深度優(yōu)先搜索算法

    C#深度優(yōu)先搜索算法

    這篇文章主要介紹了C#深度優(yōu)先搜索算法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • C# 對MongoDB 進行增刪改查的簡單操作實例

    C# 對MongoDB 進行增刪改查的簡單操作實例

    這篇文章介紹了C# 對MongoDB 進行增刪改查的簡單操作實例,有需要的朋友可以參考一下
    2013-09-09
  • C# 排序算法之堆排序

    C# 排序算法之堆排序

    這里是指一種數(shù)據(jù)結(jié)構(gòu),而不是我們在C#中提到的用于存儲引用類型對象的地方。它可以被當(dāng)成一棵完全二叉樹。
    2010-09-09
  • C#中this的五種用法小結(jié)

    C#中this的五種用法小結(jié)

    本文主要介紹了C#中this的五種用法小結(jié),包括區(qū)分當(dāng)前類的對象、作為參數(shù)傳遞、作為索引器、調(diào)用其他的構(gòu)造函數(shù)、擴展靜態(tài)類方法等,感興趣的可以了解一下
    2025-11-11
  • C#中修改私有字段的幾種方法

    C#中修改私有字段的幾種方法

    本文主要介紹了C#中修改私有字段的方法,包括添加公共屬性、Set和Get方法、通過構(gòu)造函數(shù)設(shè)置以及完整實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-01-01

最新評論

汶上县| 汤原县| 辽阳县| 荔波县| 龙海市| 永胜县| 米脂县| 香河县| 文化| 珲春市| 洛宁县| 依兰县| 阿荣旗| 富锦市| 喀什市| 灵川县| 嘉荫县| 玛曲县| 宾阳县| 郯城县| 肥东县| 鹰潭市| 尖扎县| 拉萨市| 故城县| 峨眉山市| 阿拉善右旗| 隆安县| 杨浦区| 湄潭县| 青龙| 建阳市| 新宁县| 永康市| 韶关市| 永福县| 广元市| 永顺县| 九龙坡区| 格尔木市| 寻甸|