記憶體管理篇——線性地址的管理

寂靜的羽夏發表於2022-02-17

寫在前面

  此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。

  看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。


? 華麗的分割線 ?


核心線性地址管理概述

  在開始線性地址管理之前,我們先從巨集觀上了解核心線性地址的劃分割槽域:

記憶體管理篇——線性地址的管理

  如下是系統的全域性變數:

記憶體管理篇——線性地址的管理

  可以看出微軟把作業系統的記憶體區域的用途劃分的十分清晰,當然你處於0環時,可以不用遵守這種約定,但最好不要與其衝突。如果自己寫個處於保護模式下的作業系統玩玩自己隨便定。

線性地址管理

  我們在3環的程式,如果你對虛擬地址瞭解比較多的話一定會知道下面的表格:

分割槽 x86 32位Windows
空指標賦值區 0x00000000 - 0x0000FFFF
使用者模式區 0x00010000 - 0x7FFEFFFF
64KB禁入區 0x7FFF0000 - 0x7FFFFFFF
核心 0x80000000 - 0xFFFFFFFF

  一個程式不可能每一個線性地址都對應一個物理頁,如果要清楚地瞭解線性地址的使用,就必須有一個記錄,在EPROCESS結構體中,有一個成員VadRoot,如下所示:

kd> dt _EPROCESS
ntdll!_EPROCESS
   ……
   +0x118 HardwareTrigger  : Uint4B
   +0x11c VadRoot          : Ptr32 Void
   +0x120 VadHint          : Ptr32 Void
   +0x124 CloneRoot        : Ptr32 Void
   +0x128 NumberOfPrivatePages : Uint4B
   +0x12c NumberOfLockedPages : Uint4B
   ……

  而這個VadRoot的記錄是由二叉樹結構組織的,我們來看看它的結構:

kd> dt _MMVAD
nt!_MMVAD
   +0x000 StartingVpn      : Uint4B
   +0x004 EndingVpn        : Uint4B
   +0x008 Parent           : Ptr32 _MMVAD
   +0x00c LeftChild        : Ptr32 _MMVAD
   +0x010 RightChild       : Ptr32 _MMVAD
   +0x014 u                : __unnamed
   +0x018 ControlArea      : Ptr32 _CONTROL_AREA
   +0x01c FirstPrototypePte : Ptr32 _MMPTE
   +0x020 LastContiguousPte : Ptr32 _MMPTE
   +0x024 u2               : __unnamed

  裡面有兩個共用體型別,我翻了翻Win2000洩露程式碼如下所示:

typedef struct _MMVAD {
    ULONG_PTR StartingVpn;
    ULONG_PTR EndingVpn;
    struct _MMVAD *Parent;
    struct _MMVAD *LeftChild;
    struct _MMVAD *RightChild;
    union {
        ULONG_PTR LongFlags;
        MMVAD_FLAGS VadFlags;
    } u;
    PCONTROL_AREA ControlArea;
    PMMPTE FirstPrototypePte;
    PMMPTE LastContiguousPte;
    union {
        ULONG LongFlags2;
        MMVAD_FLAGS2 VadFlags2;
    } u2;
    union {
        LIST_ENTRY List;
        MMADDRESS_LIST Secured;
    } u3;
    union {
        PMMBANKED_SECTION Banked;
        PMMEXTEND_INFO ExtendedInfo;
    } u4;
} MMVAD, *PMMVAD;

  可以看出,上面的結構體比XP多了兩個共用體,共用體的成員僅供參考。其中第一個共用體成員十分重要,它描述了大量主要的屬性,其結構體如下:

kd> dt _MMVAD_FLAGS
nt!_MMVAD_FLAGS
   +0x000 CommitCharge     : Pos 0, 19 Bits
   +0x000 PhysicalMapping  : Pos 19, 1 Bit
   +0x000 ImageMap         : Pos 20, 1 Bit
   +0x000 UserPhysicalPages : Pos 21, 1 Bit
   +0x000 NoChange         : Pos 22, 1 Bit
   +0x000 WriteWatch       : Pos 23, 1 Bit
   +0x000 Protection       : Pos 24, 5 Bits
   +0x000 LargePages       : Pos 29, 1 Bit
   +0x000 MemCommit        : Pos 30, 1 Bit
   +0x000 PrivateMemory    : Pos 31, 1 Bit

  下面我將會用實驗進行介紹。

實驗講解

  我在虛擬機器開了一個記事本,輸入如下指令:

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
……
Failed to get VadRoot
PROCESS 89d37da0  SessionId: 0  Cid: 018c    Peb: 7ffd3000  ParentCid: 05e8
    DirBase: 13a40260  ObjectTable: e1f4db50  HandleCount:  44.
    Image: notepad.exe

kd> dt _EPROCESS 89d37da0
ntdll!_EPROCESS
   ……
   +0x118 HardwareTrigger  : 0
   +0x11c VadRoot          : 0x89d080a8 Void
   +0x120 VadHint          : 0x89e22630 Void
   +0x124 CloneRoot        : (null) 
   ……

  對於Windbg來說,它提供了一個十分簡單檢視線性地址的記錄情況的指令,如下所示:

kd> !vad 0x89d080a8
VAD   Level     Start       End Commit
89eb60e8  3        10        10      1 Private      READWRITE          
89cf10e8  2        20        20      1 Private      READWRITE          
89d310c8  5        30        3f      6 Private      READWRITE          
89f833e0  4        40        7f     18 Private      READWRITE          
89d372d0  3        80        82      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89d350a8  4        90        91      0 Mapped       READONLY           Pagefile section, shared commit 0x2
89e22630  1        a0       19f     21 Private      READWRITE          
89cff0b8  4       1a0       1af      6 Private      READWRITE          
89e31380  3       1b0       1bf      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89d350d8  4       1c0       1d5      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89cf50d8  2       1e0       220      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89cf70a8  4       230       270      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89d200a8  3       280       285      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89d080a8  0       290       2d0      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89d170d8  5       2e0       3a7      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x3
89f68f20  6       3b0       3bf      8 Private      READWRITE          
89aeaf98  7       3c0       3c0      1 Private      READWRITE          
89abfd58  8       3d0       3d0      1 Private      READWRITE          
89d790d8 10       3e0       3e1      0 Mapped       READONLY           Pagefile section, shared commit 0x2
89d140d8  9       3f0       3f1      0 Mapped       READONLY           Pagefile section, shared commit 0x2
89d96330 10       400       40f      3 Private      READWRITE          
89d250e8  4       410       41f      8 Private      READWRITE          
89d100b8  3       420       42f      4 Private      READWRITE          
89d250b8  4       430       432      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89dd71f8  2       440       47f      3 Private      READWRITE          
89d56260  5       480       582      0 Mapped       READONLY           Pagefile section, shared commit 0x103
89d280a8  4       590       88f      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x1a
89de0f60  3       890       90f      1 Private      READWRITE          
89d060a8  5       910       95f      0 Mapped       READONLY           Pagefile section, shared commit 0x50
89d8ae80  4       960       960      0 Mapped       READWRITE          Pagefile section, shared commit 0x1
89d42ec0  6       970       9af      0 Mapped       READWRITE          Pagefile section, shared commit 0x10
89d42e90  5       9b0       9bd      0 Mapped       READWRITE          Pagefile section, shared commit 0xe
89d310e8  7       9c0       abf    123 Private      READWRITE          
89dd9e80  6       ad0       b4f      0 Mapped       READWRITE          Pagefile section, shared commit 0x7
89abfcd8  1      1000      1012      3 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\notepad.exe
89d280d8  7     58fb0     59179      9 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\AppPatch\AcGenral.dll
89d0e0d8  8     5adc0     5adf6      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\uxtheme.dll
89d100d8  6     5cc30     5cc55     20 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\shimeng.dll
89d2e248  7     62c20     62c28      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\lpk.dll
89d230d8  5     72f70     72f95      3 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\winspool.drv
89cf70d8  8     73640     7366d      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\MSCTFIME.IME
89d790a8  7     73fa0     7400a     16 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\usp10.dll
89dd93e0  8     74680     746cb      3 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\MSCTF.dll
89d0e0a8  6     759d0     75a7e      3 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\userenv.dll
89d200d8  7     76300     7631c      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\imm32.dll
89d080d8  4     76320     76366      4 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\comdlg32.dll
89d342d0  8     76990     76acc      8 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ole32.dll
89cc50a8  7     76b10     76b39      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\winmm.dll
89d140a8  8     770f0     7717a      4 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\oleaut32.dll
89d170a8  6     77180     77282      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\comctl32.dll
89a865b8  8     77bb0     77bc4      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\msacm32.dll
89d0f0d8  9     77bd0     77bd7      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\version.dll
89d230a8  7     77be0     77c37      7 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\msvcrt.dll
89abfd18  8     77d10     77d9f      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\user32.dll
89cf50a8  5     77da0     77e48      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\advapi32.dll
89d590a8  6     77e50     77ee1      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\rpcrt4.dll
89d590d8  8     77ef0     77f38      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\gdi32.dll
89d010a8  9     77f40     77fb5      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\shlwapi.dll
89d270d8  7     77fc0     77fd0      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\secur32.dll
89d372a0  3     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89eb60b8  2     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89cf60d8  5     7d590     7dd83     30 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\shell32.dll
89d9e908  4     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89d010d8  3     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89abfc58  4     7ffd3     7ffd3      1 Private      READWRITE          
89ac1ac8  5     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 66, average level: 6, maximum depth: 10
Total private commit: 0x161 pages (1412 KB)
Total shared commit:  0x21d pages (2164 KB)

  其中VAD就是MMVAD結構體的地址,Level就是二叉樹的層級,根節點是0,如果不懂請具體請自行學習二叉樹的相關知識。
  我們們的物理頁大小都是4KB,用十六進位制來表示的話就是0x1000,所以上面的StartEnd就是除掉0x1000的結果。如果這兩個值相等,就只是使用一個物理頁。
  上面還有一個Commit,這個就是你使用VirtualAlloc函式傳參時MEM_COMMIT的物理頁個數。下面我們開始手動遍歷VAD看看情況:

kd> dt _MMVAD 0x89d080a8
nt!_MMVAD
   +0x000 StartingVpn      : 0x290
   +0x004 EndingVpn        : 0x2d0
   +0x008 Parent           : (null) 
   +0x00c LeftChild        : 0x89e22630 _MMVAD
   +0x010 RightChild       : 0x89abfcd8 _MMVAD
   +0x014 u                : __unnamed
   +0x018 ControlArea      : 0x89e7e540 _CONTROL_AREA
   +0x01c FirstPrototypePte : 0xe15a69f0 _MMPTE
   +0x020 LastContiguousPte : 0xe15a6bf0 _MMPTE
   +0x024 u2               : __unnamed

  上面的內容對應用!vad的記錄:

VAD   Level     Start       End Commit
89d080a8  0       290       2d0      0 Mapped       READONLY           Pagefile section, shared commit 0x41

  StartingVpnEndingVpn分別對應的是記錄中StartEndParent就是二叉樹的父節點,LeftChildRightChild分別對應二叉樹的左子節點和右子節點,我們來看看它的VadFlags屬性:

kd> dx -id 0,0,805539a0 -r1 (*((ntkrnlpa!__unnamed *)0x89d080bc))
(*((ntkrnlpa!__unnamed *)0x89d080bc))                 [Type: __unnamed]
    [+0x000] LongFlags        : 0x1000000 [Type: unsigned long]
    [+0x000] VadFlags         [Type: _MMVAD_FLAGS]
kd> dx -id 0,0,805539a0 -r1 (*((ntkrnlpa!_MMVAD_FLAGS *)0x89d080bc))
(*((ntkrnlpa!_MMVAD_FLAGS *)0x89d080bc))                 [Type: _MMVAD_FLAGS]
    [+0x000 (18: 0)] CommitCharge     : 0x0 [Type: unsigned long]
    [+0x000 (19:19)] PhysicalMapping  : 0x0 [Type: unsigned long]
    [+0x000 (20:20)] ImageMap         : 0x0 [Type: unsigned long]
    [+0x000 (21:21)] UserPhysicalPages : 0x0 [Type: unsigned long]
    [+0x000 (22:22)] NoChange         : 0x0 [Type: unsigned long]
    [+0x000 (23:23)] WriteWatch       : 0x0 [Type: unsigned long]
    [+0x000 (28:24)] Protection       : 0x1 [Type: unsigned long]
    [+0x000 (29:29)] LargePages       : 0x0 [Type: unsigned long]
    [+0x000 (30:30)] MemCommit        : 0x0 [Type: unsigned long]
    [+0x000 (31:31)] PrivateMemory    : 0x0 [Type: unsigned long]

  CommitCharge是幾,那個Commit記錄的就是幾。有關線性地址的屬性有兩個,一個是Private(私有),另一個就是Mapped(對映)。由於我們示例的頁是對映的只讀記憶體,所以PrivateMemory值為0,有關線性記憶體的屬性,也就是Protection的值,我們先看看如下列舉:

#define MM_READONLY            1
#define MM_EXECUTE             2
#define MM_EXECUTE_READ        3
#define MM_READWRITE           4  // bit 2 is set if this is writable.
#define MM_WRITECOPY           5
#define MM_EXECUTE_READWRITE   6
#define MM_EXECUTE_WRITECOPY   7

  這個屬性就介紹完了,我們再看看ImageMap屬性,這個就是使用!vad的資訊中有的記錄有Exe這個字樣,如果ImageMap為1,那麼就是映象檔案。
  重要的成員介紹完後,我們繼續接著一個支線繼續遍歷:

kd> dt _MMVAD 0x89abfcd8
nt!_MMVAD
   +0x000 StartingVpn      : 0x1000
   +0x004 EndingVpn        : 0x1012
   +0x008 Parent           : 0x89d080a8 _MMVAD
   +0x00c LeftChild        : 0x89dd71f8 _MMVAD
   +0x010 RightChild       : 0x89eb60b8 _MMVAD
   +0x014 u                : __unnamed
   +0x018 ControlArea      : 0x89b1af50 _CONTROL_AREA
   +0x01c FirstPrototypePte : 0xe1faebd8 _MMPTE
   +0x020 LastContiguousPte : 0xfffffffc _MMPTE
   +0x024 u2               : __unnamed
kd> dt _MMVAD 0x89eb60b8
nt!_MMVAD
   +0x000 StartingVpn      : 0x7c920
   +0x004 EndingVpn        : 0x7c9b2
   +0x008 Parent           : 0x89abfcd8 _MMVAD
   +0x00c LeftChild        : 0x89d372a0 _MMVAD
   +0x010 RightChild       : 0x89d010d8 _MMVAD
   +0x014 u                : __unnamed
   +0x018 ControlArea      : 0x89fad2d8 _CONTROL_AREA
   +0x01c FirstPrototypePte : 0xe13e1b38 _MMPTE
   +0x020 LastContiguousPte : 0xfffffffc _MMPTE
   +0x024 u2               : __unnamed
kd> dt _MMVAD 0x89d010d8
nt!_MMVAD
   +0x000 StartingVpn      : 0x7ffa0
   +0x004 EndingVpn        : 0x7ffd2
   +0x008 Parent           : 0x89eb60b8 _MMVAD
   +0x00c LeftChild        : 0x89d9e908 _MMVAD
   +0x010 RightChild       : 0x89abfc58 _MMVAD
   +0x014 u                : __unnamed
   +0x018 ControlArea      : 0x89faa1f8 _CONTROL_AREA
   +0x01c FirstPrototypePte : 0xe100f658 _MMPTE
   +0x020 LastContiguousPte : 0xe100f7e8 _MMPTE
   +0x024 u2               : __unnamed
kd> dt _MMVAD 0x89abfc58
nt!_MMVAD
   +0x000 StartingVpn      : 0x7ffd3
   +0x004 EndingVpn        : 0x7ffd3
   +0x008 Parent           : 0x89d010d8 _MMVAD
   +0x00c LeftChild        : (null) 
   +0x010 RightChild       : 0x89ac1ac8 _MMVAD
   +0x014 u                : __unnamed
   +0x018 ControlArea      : (null) 
   +0x01c FirstPrototypePte : (null) 
   +0x020 LastContiguousPte : (null) 
   +0x024 u2               : __unnamed

  可以看到最後一個成員沒有了左子樹,說明到頭了。為了更好的講解上面的成員,我用這個記錄:

VAD   Level     Start       End Commit
89eb60b8  2     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll

  然後在Windbg如下所示:

kd> dt _MMVAD 89eb60b8
nt!_MMVAD
   +0x000 StartingVpn      : 0x7c920
   +0x004 EndingVpn        : 0x7c9b2
   +0x008 Parent           : 0x89abfcd8 _MMVAD
   +0x00c LeftChild        : 0x89d372a0 _MMVAD
   +0x010 RightChild       : 0x89d010d8 _MMVAD
   +0x014 u                : __unnamed
   +0x018 ControlArea      : 0x89fad2d8 _CONTROL_AREA
   +0x01c FirstPrototypePte : 0xe13e1b38 _MMPTE
   +0x020 LastContiguousPte : 0xfffffffc _MMPTE
   +0x024 u2               : __unnamed
kd> dx -id 0,0,805539a0 -r1 (*((ntkrnlpa!__unnamed *)0x89eb60cc))
(*((ntkrnlpa!__unnamed *)0x89eb60cc))                 [Type: __unnamed]
    [+0x000] LongFlags        : 0x7100005 [Type: unsigned long]
    [+0x000] VadFlags         [Type: _MMVAD_FLAGS]
kd> dx -id 0,0,805539a0 -r1 (*((ntkrnlpa!_MMVAD_FLAGS *)0x89eb60cc))
(*((ntkrnlpa!_MMVAD_FLAGS *)0x89eb60cc))                 [Type: _MMVAD_FLAGS]
    [+0x000 (18: 0)] CommitCharge     : 0x5 [Type: unsigned long]
    [+0x000 (19:19)] PhysicalMapping  : 0x0 [Type: unsigned long]
    [+0x000 (20:20)] ImageMap         : 0x1 [Type: unsigned long]
    [+0x000 (21:21)] UserPhysicalPages : 0x0 [Type: unsigned long]
    [+0x000 (22:22)] NoChange         : 0x0 [Type: unsigned long]
    [+0x000 (23:23)] WriteWatch       : 0x0 [Type: unsigned long]
    [+0x000 (28:24)] Protection       : 0x7 [Type: unsigned long]
    [+0x000 (29:29)] LargePages       : 0x0 [Type: unsigned long]
    [+0x000 (30:30)] MemCommit        : 0x0 [Type: unsigned long]
    [+0x000 (31:31)] PrivateMemory    : 0x0 [Type: unsigned long]

  如果仔細的話有的後面會跟著檔案路徑,而這個名稱有是在哪裡記錄著呢?它在ControlArea這個屬性中,我們就拿著上面剛舉例的繼續:

kd> dx -id 0,0,805539a0 -r1 ((ntkrnlpa!_CONTROL_AREA *)0x89fad2d8)
((ntkrnlpa!_CONTROL_AREA *)0x89fad2d8)                 : 0x89fad2d8 [Type: _CONTROL_AREA *]
    [+0x000] Segment          : 0xe13e1af8 [Type: _SEGMENT *]
    [+0x004] DereferenceList  [Type: _LIST_ENTRY]
    [+0x00c] NumberOfSectionReferences : 0x1 [Type: unsigned long]
    [+0x010] NumberOfPfnReferences : 0x53 [Type: unsigned long]
    [+0x014] NumberOfMappedViews : 0x15 [Type: unsigned long]
    [+0x018] NumberOfSubsections : 0x5 [Type: unsigned short]
    [+0x01a] FlushInProgressCount : 0x0 [Type: unsigned short]
    [+0x01c] NumberOfUserReferences : 0x16 [Type: unsigned long]
    [+0x020] u                [Type: __unnamed]
    [+0x024] FilePointer      : 0x89fbe9a0 : "\WINDOWS\system32\ntdll.dll" - Device for "\FileSystem\Ntfs" [Type: _FILE_OBJECT *]
    [+0x028] WaitingForDeletion : 0x0 [Type: _EVENT_COUNTER *]
    [+0x02c] ModifiedWriteCount : 0x0 [Type: unsigned short]
    [+0x02e] NumberOfSystemCacheViews : 0x0 [Type: unsigned short]

  看到FilePointer這個成員了嗎?而這個又是_FILE_OBJECT結構體指標,具體的可以在Windbg檢視檔名到底儲存在哪裡。

本節練習

本節的答案將會在下一節進行講解,務必把本節練習做完後看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。

  俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到後面,不做練習的話容易夾生了,開始還明白,後來就真的一點都不明白了。本節練習不多,請保質保量的完成。

1️⃣ 寫程式碼實現需輸入程式的PID來獲取列印其VAD樹。

下一篇

  記憶體管理篇——私有記憶體和對映記憶體

相關文章