当前位置: 首页 > news >正文

wordpress条件筛选/seo北京优化

wordpress条件筛选,seo北京优化,利用公共dns做网站解析,报价网站制作3.1.3 虚存页面的映射 文章目录 3.1.3 虚存页面的映射3.1.3 虚存页面的映射MmCreateVirtualMapping()MmCreateVirtualMappingUnsafe()MiFlushTlb()MmDeleteVirtualMapping()MmPageOu…

3.1.3 虚存页面的映射

文章目录

  • 3.1.3 虚存页面的映射
  • 3.1.3 虚存页面的映射
  • MmCreateVirtualMapping()
  • MmCreateVirtualMappingUnsafe()
  • MiFlushTlb()
  • MmDeleteVirtualMapping()
  • MmPageOutVirtualMemory()
  • MmDeleteVirtualMapping()


3.1.3 虚存页面的映射

有了虚存页面和物理内存页面,下一步就是建立二者之间的映射了。
所谓页面映射,是指从虚存页面到物理页面的映射。多个虚存页面可以映射到同一个物理页面,但是在同一时间内不可能会有多个物理页面对应于同一个空间的同一个虚存页面。
虚存页面是“虚”的,必须通过映射落实到某种形式的物理存储介质上。最理想的物理存储介质当然是物理内存,但是物理内存一方面价格较高,另一方面技术上也不能做得很大。所以,用外存特别是磁盘或磁盘上的文件作为后盾,与物理内存相结合构成一个二级的物理存储系统,是一种合适的解决方案。这样,只需要把当前正在受到访问以及很可能会受到访问的页面保存在物理内存中(这些页面的集合称为一个进程的“工作集(WorkingSer)”),而大量暂时不会受到访问的页面,例如不属于当前进程的页面,以及虽然属于当前进程但是很少受到访问的页面,则可以保存在后备的“倒换文件”中。即使判断有误,本以为不会受到访问的页面偏偏受到了访问,那也不要紧,可以临时将其倒换进来,由此而引起的效率下降一般不会很严重,因为这样的页面毕竟是极少数。
如前所述,每个进程都有个“页面映射表”,从原理上讲,这可以是个“页面映射表项”PTE的一维数组,PTE指明了一个虚存页面(在物理内存中)的起点。但是那样一来数组的大小就是1M,因为整个地址空间的大小是4GB,而每个页面的大小是4KB。然而,在4GB 的地址空间中,实际分配使用的页面却通常只是很小一部分,所以数组里面很大一部分都是空洞,如果真的使用大小为1M 的数组就造成了很大的浪费。所以,页面映射表所采用的是“稀疏数组”的结构,即把整个页面映射表分成两层。其顶层为“页面目录",这是一个“页面目录项”PDE的数组,实质上是个指针数组,如果指针非空就指向一个二级页面表,二级页面表是较小的PTE数组。页面目录和二级页面表的大小都是1024,正好都占一个页面。在实际的映射过程中,MMU以拟地址的最高10位为下标在页面目录中找到指向目标页面所在二级页面表的指针,如果这个指针非空就进而以次10位作为下标在二级页面表中找到页面表项PTE,这就找到了目标页面的起始地址,而虚拟地址的低12位则是在目标页面内部的位移。这样,在实际使用中,页面目录中的大量指针其实都是空指针,这就省却了许多二级页面表,从而节省了很多物理存储页面。当然,凡是已经分配使用的虚存页面

/* GLOBALS *****************************************************************/#define PA_BIT_PRESENT   (0)
#define PA_BIT_READWRITE (1)
#define PA_BIT_USER      (2)
#define PA_BIT_WT        (3)
#define PA_BIT_CD        (4)
#define PA_BIT_ACCESSED  (5)
#define PA_BIT_DIRTY     (6)
#define PA_BIT_GLOBAL	 (8)#define PA_PRESENT   (1 << PA_BIT_PRESENT)
#define PA_READWRITE (1 << PA_BIT_READWRITE)
#define PA_USER      (1 << PA_BIT_USER)
#define PA_DIRTY     (1 << PA_BIT_DIRTY)
#define PA_WT        (1 << PA_BIT_WT)
#define PA_CD        (1 << PA_BIT_CD)
#define PA_ACCESSED  (1 << PA_BIT_ACCESSED)
#define PA_GLOBAL    (1 << PA_BIT_GLOBAL)

中间还留下4位,Intel的手册中称为Avail,这是让程序员自由处置使用的。
这样,例如“PFN_TO_PTE(Page)IPA_PRESENTIPA_READWRITE”就是一个表项的内容,表示把虚存页面映射到物理页面号Page,页面的映像在内存中,并且可读可写。
最低位PAPRESENT是关键,如果这一位是0,就表示所映射的页面不在内存中,CPU的内存管理单元 MMU 立即就会产生一次缺页异常,此时别的标志位以及页面号对于MMU 就都失去了意义。这样,操作系统就可以用该表项指示页面在倒换设备上或文件中的位置,具体的方法则因操作系统而异。就 Windows而言,表项的最高8位用于倒换文件号,低24位则用于倒换文件内部的页面号加1。
就映射的建立而言,最为一般的操作是 MmCreateVirtualMappingO,其作用为:给定一组物理页面,将某个给定进程从虚拟地址 Address 开始的一个区块映射到这组物理页面上。

MmCreateVirtualMapping()


NTSTATUS
NTAPI
MmCreateVirtualMapping(PEPROCESS Process,PVOID Address,ULONG flProtect,PPFN_TYPE Pages,ULONG PageCount)
{ULONG i;for (i = 0; i < PageCount; i++){if (!MmIsUsablePage(Pages[i])){DPRINT1("Page at address %x not usable\n", PFN_TO_PTE(Pages[i]));KEBUGCHECK(0);//物理页面不可用,系统因无计可施而崩溃}}return(MmCreateVirtualMappingUnsafe(Process,Address,flProtect,Pages,PageCount));
}

参数 Process给定了一个具体的进程,Address 是需要建立映射的虚存区块的起始地址,属于给定的进程。如果指针Process为NULL则表示该虚存区块属于系统空间,所以并不属于任何特定的进程。参数 Pages指向一个页面号Pfn的数组,数组的大小即页面的数量为PageCount,这些物理页面不必是连续的。另一个参数 fProtect 则为对这些页面的保护模式。

显然,实际的操作是由 MmCreateVirtualMappingUnsafe()完成的,这里只是加上了对给定物理页面的有效性检查。如果给定的某个物理页面无效,则系统崩溃,CPU通过KEBUGCHECKO进入Debug 模式。

MmCreateVirtualMappingUnsafe()

NTSTATUS
NTAPI
MmCreateVirtualMappingUnsafe(PEPROCESS Process,PVOID Address,ULONG flProtect,PPFN_TYPE Pages,ULONG PageCount)
{ULONG Attributes;PVOID Addr;ULONG i;ULONG oldPdeOffset, PdeOffset;BOOLEAN NoExecute = FALSE;DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",Process, Address, flProtect, Pages, *Pages, PageCount);if (Process == NULL){if (Address < MmSystemRangeStart)//地址落在用户空间,但没有给定进程{DPRINT1("No process\n");KEBUGCHECK(0);}if (PageCount > 0x10000 ||(ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000){//页面数量太大,或地址越界DPRINT1("Page count to large\n");KEBUGCHECK(0);}}else{//给定了目标进程if (Address >= MmSystemRangeStart)//地址落在系统空间{DPRINT1("Setting kernel address with process context\n");KEBUGCHECK(0);}if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||(ULONG_PTR) Address / PAGE_SIZE + PageCount >(ULONG_PTR)MmSystemRangeStart / PAGE_SIZE){//页面数量太大,或地址越界DPRINT1("Page Count to large\n");KEBUGCHECK(0);}}Attributes = ProtectToPTE(flProtect);//把保护模式转换成 PTE中的相应控制位if (Attributes & 0x80000000)//0Protect中的PAGE_IS_EXECUTABLE位为0{NoExecute = TRUE;}Attributes &= 0xfff;//取其低12位if (Address >= MmSystemRangeStart){//虚拟地址高于系统空间与用户空间的分界Attributes &= ~PA_USER;//属于系统空间if (Ke386GlobalPagesEnabled){Attributes |= PA_GLOBAL;//页面表项不受冲刷}}else{Attributes |= PA_USER;//属于用户空间}Addr = Address;if (Ke386Pae){...}else{PULONG Pt = NULL;ULONG Pte;oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE)){if (!(Attributes & PA_PRESENT) && Pages[i] != 0){DPRINT1("Setting physical address but not allowing access at address ""0x%.8X with attributes %x/%x.\n",Addr, Attributes, flProtect);KEBUGCHECK(0);}PdeOffset = ADDR_TO_PDE_OFFSET(Addr);if (oldPdeOffset != PdeOffset){MmUnmapPageTable(Pt);Pt = MmGetPageTableForProcess(Process, Addr, TRUE);if (Pt == NULL){KEBUGCHECK(0);}}else{Pt++;}oldPdeOffset = PdeOffset;Pte = *Pt;MmMarkPageMapped(Pages[i]);if (PAGE_MASK((Pte)) != 0 && !((Pte) & PA_PRESENT)){KEBUGCHECK(0);}if (PAGE_MASK((Pte)) != 0){MmMarkPageUnmapped(PTE_TO_PFN((Pte)));}(void)InterlockedExchangeUL(Pt, PFN_TO_PTE(Pages[i]) | Attributes);if (Address < MmSystemRangeStart &&((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable != NULL &&Attributes & PA_PRESENT){PUSHORT Ptrc;Ptrc = ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable;Ptrc[ADDR_TO_PAGE_TABLE(Addr)]++;}if (Pte != 0){if (Address > MmSystemRangeStart ||(Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024)){MiFlushTlb(Pt, Address);}}}if (Addr > Address){MmUnmapPageTable(Pt);}}return(STATUS_SUCCESS);
}

进程的页面映射表代表着它的整个地址空间。页面映射表是个二层的结构,其高层为页面(映射)目录,低层是一组(最多1023个)具体的二级页面表。所以,凡是说到“页面(映射)表”的时候,读者应根据上下文判定说的是包括页面目录在内的整个页面表,还是具体的二级页面表。总之,给定一个页面表,根据虚拟地址的最高10位就可以算出这个地址所属的目录项,这个目录项指向该地址所在页面所属的二级页面(映射)表。计算的方法很简单,只要将虚拟地址整除二级页面表所代表的区间大小(1024*PAGE_SIZE)即可,因为一个二级页面表有1024个表项,每个表项代表着一个 4K字节的页面:

#define ADDR_TO_PDE_OFFSET(v) ((((ULONG)(v)) / (1024 * PAGE_SIZE)))

另一个宏操作 ADDR_TO_PAGE_TABLE的定义与此相同,所得的是二级页面表的序号,实际上就是相应目录项PDE在页面目录中的下标:

#define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (4 * 1024 * 1024))

每个地址空间还有个“页面表引用计数表”,就是代码中的Ptrc。这是个数组,里面记录着各个页面映射表的引用计数。如果某个(二级)页面表的引用计数是0,就可以释放这个页面表,并把相应目录项中的“在位(PRESENT)”标志位清0,表示这个目录项是个空项。当然,只要需要,仍可以为其分配一个(空白的)二级页面表,并将该标志位设成1。
-个页面表项决定了一个虚存页面的映射,如果这个虚存页面被映射到一个物理内存页面,那么这个 32位表项的高 20位就是目标物理页面的Pfn,而一个物理页面的P 其实就是其起始物理地址的高20位,所以这里的PFNTO_PTEO)就是将Pfn左移12位,作为PTE的高20位:

#define PFN_TO_PTE(X)  ((X) << PAGE_SHIFT)

PTE的最低8位如前所述,所以要把Pfn和Attributes 结合起来才得到PTE的值。
有了 PTE的值之后,就通过InterlockedExchangeUL()将其写入目标页面表项,并递增“页面表引用计数表”中相应页面表的引用计数。宏操作 InterlockedExchangeUL()定义为InterlockedExchange():

#define InterlockedExchangeUL(Target, Value) \(ULONG)InterlockedExchange((PLONG)(Target), (LONG)(Value))

这个函数将 Value 设置到 Target,并返回Target 原来的值,但是整个过程是个不可中断从而不可分割的“原子”操作。

不过光是改变了页面表中的表项还不解决问题,因为那只是存在于内存之中,而没有进入 MMU的页面映射高速缓存TLB,所以还要通过 MiFlushTbO“冲刷”高速缓存中的相应表项。MMU 在实现地址映射的过程中所直接依据的并不是内存中的页面表项,而是这个表项在MMU 的高速缓存TLB 内的映像。只有在目标表项不在TLB 中时才会从内存中装入该表项,这与数据和指令的高速缓存是一样的。然而,内存中页面映射表项的改变不会自动引起其在高速缓存 TLB 内的映像的改变,所以需要通过 MiFlushTIbO“冲刷”TLB 中该表项的映像,目的是从 TLB 中删除这个表项的映像。
这样,以后当 MMU 需要用到这个表项时就会自动从内存中的页面映射表装载新的表项。

MiFlushTlb()


VOID
MiFlushTlb(PULONG Pt, PVOID Address)
{
#ifdef CONFIG_SMP...
#elseif ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart){__invlpg(Address);}
#endif
}

对于单处理器结构,只要指针P落在页面映射表的范围之内,即指向某个页面映射表项,或者所映射的地址 Address 落在内核空间,就由_invlpgO)实际上是汇编指令invlpg 冲刷目标表项。这条指令名曰invlpg,似乎意为“使页面无效”,实际上是使得一个页面的映射(在TLB中)无效,所以是“Invalidate TLB Entry”,即冲刷一个页面映射表项。这里的 MmUnmapPageTable()用来判定指针 P是否落在页面映射表中,并处理一些特殊的情况,一般都会返回 TRUE,这里就不予深究了。

理解了 MmCreateVirtualMapping()以后,我们再来看其逆操作 MmDeleteVirtualMapping(),即删除某个虚拟地址所在页面的映射。

MmDeleteVirtualMapping()


VOID
NTAPI
MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,BOOLEAN* WasDirty, PPFN_TYPE Page)
/** FUNCTION: Delete a virtual mapping*/
{BOOLEAN WasValid = FALSE;PFN_TYPE Pfn;DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",Process, Address, FreePage, WasDirty, Page);if (Ke386Pae){...}else{ULONG Pte;PULONG Pt;Pt = MmGetPageTableForProcess(Process, Address, FALSE);if (Pt == NULL){if (WasDirty != NULL){*WasDirty = FALSE;}if (Page != NULL){*Page = 0;}return;}/** Atomically set the entry to zero and get the old value.*/Pte = InterlockedExchangeUL(Pt, 0);MiFlushTlb(Pt, Address);WasValid = (PAGE_MASK(Pte) != 0);if (WasValid){Pfn = PTE_TO_PFN(Pte);MmMarkPageUnmapped(Pfn);}else{Pfn = 0;}if (FreePage && WasValid){MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);}/** Return some information to the caller*/if (WasDirty != NULL){*WasDirty = Pte & PA_DIRTY ? TRUE : FALSE;}if (Page != NULL){*Page = Pfn;}}/** Decrement the reference count for this page table.*/if (Process != NULL && WasValid &&((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable != NULL &&Address < MmSystemRangeStart){PUSHORT Ptrc;ULONG Idx;Ptrc = ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable;Idx = Ke386Pae ? PAE_ADDR_TO_PAGE_TABLE(Address) : ADDR_TO_PAGE_TABLE(Address);Ptrc[Idx]--;if (Ptrc[Idx] == 0){MmFreePageTable(Process, Address);}}
}

笔者在代码中加了注释,读者对这段代码应该不会有困难。同样,这里也要调用MiFlushTIbO,因为某个页面映射表项已经发生了变化。
一个虚存页面被映射到某个物理内存页面以后,如果较长时间没有受到访问,内核就可能决定将其内容倒出到倒换文件中,腾出其所占用的物理页面,以便周转使用。内核函数MmPageOutVirtualMemoryO)就用来将一个虚存页面的内容倒出到倒换文件中。这个函数比较长,我们分段阅读。

MmPageOutVirtualMemory()


NTSTATUS
NTAPI
MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace,PMEMORY_AREA MemoryArea,PVOID Address,PMM_PAGEOP PageOp)
{PFN_TYPE Page;BOOLEAN WasDirty;SWAPENTRY SwapEntry;NTSTATUS Status;DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",Address, AddressSpace->Process->UniqueProcessId);/** Check for paging out from a deleted virtual memory area.*/if (MemoryArea->DeleteInProgress){PageOp->Status = STATUS_UNSUCCESSFUL;KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);MmReleasePageOp(PageOp);return(STATUS_UNSUCCESSFUL);}/** Disable the virtual mapping.*/MmDisableVirtualMapping(AddressSpace->Process, Address,&WasDirty, &Page);if (Page == 0){KEBUGCHECK(0);}/** Paging out non-dirty data is easy.*/if (!WasDirty){MmLockAddressSpace(AddressSpace);MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);MmDeleteAllRmaps(Page, NULL, NULL);if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0){MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry);MmSetSavedSwapEntryPage(Page, 0);}MmUnlockAddressSpace(AddressSpace);MmReleasePageMemoryConsumer(MC_USER, Page);PageOp->Status = STATUS_SUCCESS;KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);MmReleasePageOp(PageOp);return(STATUS_SUCCESS);}/** If necessary, allocate an entry in the paging file for this page*/SwapEntry = MmGetSavedSwapEntryPage(Page);if (SwapEntry == 0){SwapEntry = MmAllocSwapPage();if (SwapEntry == 0){MmShowOutOfSpaceMessagePagingFile();MmEnableVirtualMapping(AddressSpace->Process, Address);PageOp->Status = STATUS_UNSUCCESSFUL;KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);MmReleasePageOp(PageOp);return(STATUS_PAGEFILE_QUOTA);}}/** Write the page to the pagefile*/Status = MmWriteToSwapPage(SwapEntry, Page);if (!NT_SUCCESS(Status)){DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",Status);MmEnableVirtualMapping(AddressSpace->Process, Address);PageOp->Status = STATUS_UNSUCCESSFUL;KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);MmReleasePageOp(PageOp);return(STATUS_UNSUCCESSFUL);}/** Otherwise we have succeeded, free the page*/DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);MmLockAddressSpace(AddressSpace);MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry);MmUnlockAddressSpace(AddressSpace);MmDeleteAllRmaps(Page, NULL, NULL);MmSetSavedSwapEntryPage(Page, 0);MmReleasePageMemoryConsumer(MC_USER, Page);PageOp->Status = STATUS_SUCCESS;KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);MmReleasePageOp(PageOp);return(STATUS_SUCCESS);
}

在倒出一个页面之前,首先要叫停CPU对该页面的访问,方法是将相应页面映射表项中的PAPRESENT标志位清0。这样,如果CPU访问这个页面,就会发生页面异常。将PAPRESENT标志位清0的操作是由MmDisableVirtualMappingO)完成的:

MmDeleteVirtualMapping()


VOID
NTAPI
MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,BOOLEAN* WasDirty, PPFN_TYPE Page)
/** FUNCTION: Delete a virtual mapping*/
{BOOLEAN WasValid = FALSE;PFN_TYPE Pfn;DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",Process, Address, FreePage, WasDirty, Page);if (Ke386Pae){....}else{ULONG Pte;PULONG Pt;Pt = MmGetPageTableForProcess(Process, Address, FALSE);if (Pt == NULL){if (WasDirty != NULL){*WasDirty = FALSE;}if (Page != NULL){*Page = 0;}return;}/** Atomically set the entry to zero and get the old value.*/Pte = InterlockedExchangeUL(Pt, 0);MiFlushTlb(Pt, Address);WasValid = (PAGE_MASK(Pte) != 0);if (WasValid){Pfn = PTE_TO_PFN(Pte);MmMarkPageUnmapped(Pfn);}else{Pfn = 0;}if (FreePage && WasValid){MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);}/** Return some information to the caller*/if (WasDirty != NULL){*WasDirty = Pte & PA_DIRTY ? TRUE : FALSE;}if (Page != NULL){*Page = Pfn;}}/** Decrement the reference count for this page table.*/if (Process != NULL && WasValid &&((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable != NULL &&Address < MmSystemRangeStart){PUSHORT Ptrc;ULONG Idx;Ptrc = ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable;Idx = Ke386Pae ? PAE_ADDR_TO_PAGE_TABLE(Address) : ADDR_TO_PAGE_TABLE(Address);Ptrc[Idx]--;if (Ptrc[Idx] == 0){MmFreePageTable(Process, Address);}}
}

在前面 MmCreateVirtualMapping()和 MmDeleteVirtualMappingO)的基础上,读者对于这一段代码应该不会有困难。
凡是有物理页面映射的虚存页面,有可能是“干净”的,也可能是“肮脏”的。所谓“干净”的页面,是指自从创建映射或者倒入内存(物理页面)之后从未经历过写操作的页面,前者是空白页面,后者是与页面倒换文件中的页面映像完全一致的页面。空白页面无所谓倒换,只要直接将其映射删除即可,因为以后需要时可以重新创建。对于后者,由于物理页面的内容与页面倒换文件中的页面映像完全一致,故可以直接释放物理页面,但是要将其页面映射表项修改成指向页面倒换文件中相应的页面映像。至于“肮脏”的页面,则需要将当前的物理页面内容写回页面倒换文件中,如果倒换文件中尚无该页面的映像就得先分配一个,然后写入该页面映像,再修改页面映射表项并释放物理页面。
回到前面 MmPageOutVirtualMemoryO的代码,先看对于干净页面的处理。代码中先通过MmDeleteVirtualMapping()删除该页面对于物理页面的映射,然后通过 MmGetSavedSwapEntryPage()获取本页面映像在倒换文件中的页面号(更确切地说,是倒换文件号和具体倒换文件中页面号的组合,Windows采用多个倒换文件),这个倒换页面号保存在物理页面的PHYSICAL_PAGE 数据结构中。只要这个页面号非0,就说明本页面已经在倒换文件中有了映像(因而不是空白页面),所以由MmCreatePageFileMapping0)在页面映射表中创建起指向倒换文件的页面表项。可想而知,此种表项中的PAPRESENT标志位为0。在页面映射表项中,PAPRESENT标志位是最低位。如果这一位为1,其余各位的意义和作用就都有定义,CPU中的MMU根据这些位段的值实行映射:而若这一位为0,则其余各位的意义和作用(就 MMU而言)没有定义,具体的软件可以自行定义使用,Windows内核用这31位表示倒换文件号和(文件内部)页面号的组合。同时,由于原来的物理页面已经与倒换文件中的这个页面映像脱离了关系,所以就通过 MmSetSavedSwapEntryPage()将其数据结构中的字段 SavedSwapEntry 清0。然后,就可以通过 MmReleasePageMemoryConsumer()释放这个物理页面了。
再看对于“肮脏”页面的处理。

下一篇对于“肮脏”页面的处理。

相关文章:

3.1.3 虚存页面的映射

3.1.3 虚存页面的映射 文章目录 3.1.3 虚存页面的映射3.1.3 虚存页面的映射MmCreateVirtualMapping&#xff08;&#xff09;MmCreateVirtualMappingUnsafe&#xff08;&#xff09;MiFlushTlb&#xff08;&#xff09;MmDeleteVirtualMapping&#xff08;&#xff09;MmPageOu…...

【SSM详细教程】-14-SpringAop超详细讲解

精品专题&#xff1a; 01.《C语言从不挂科到高绩点》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482 02. 《SpringBoot详细教程》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12789841.html?spm1001.20…...

虚拟机桥接模式连不上,无法进行SSH等远程操作

说明&#xff1a;以下情况在window10上遇到&#xff0c;解决后顺便做了个笔记&#xff0c;以防后续再次用到&#xff0c;也给同道中人提供一个解决方案 一、首先按照以下步骤进行检查 1、是否连接了对应的wifi 2、是否设置了桥接模式 3、上述1、2确认无误的情况下请查看右上…...

jmeter基础01-1_环境准备-windows系统安装jdk

课程大纲 一、步骤解说 step1. jdk官网下载 Java Downloads | Oracle step2. 安装/解压&#xff08;二选一&#xff09; 1. 安装包格式&#xff08;后缀.exe/.msi/.dmg&#xff09;&#xff1a;双击跟随界面向导安装&#xff0c;可以指定安装位置等。 2. 压缩包格式(后缀.z…...

第六天: C语言核心概念与实战技巧全解析

1 主函数&#xff08;main&#xff09; 大家好&#xff0c;今天我们来深入探讨一下C语言中非常特殊的一个函数——main函数。虽然大家对它并不陌生&#xff0c;但是它的重要性和特殊性值得我们再次回顾。 main函数的定义 main函数是我们整个C源程序的入口点。计算机在运行程…...

初始JavaEE篇——多线程(5):生产者-消费者模型、阻塞队列

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 文章目录 阻塞队列生产者—消费者模型生产者—消费者模型的优势&#xff1a;生产者—消费者模型的劣势&#xff1a; Java标准库中的阻…...

2024年下教师资格证面试报名详细流程❗

⏰ 重要时间节点&#xff1a; &#xff08;一&#xff09;下半年笔试成绩查询&#xff1a;11月8日10:00 &#xff08;二&#xff09;注册报名&#xff1a;11月8日10:00-11日18:00 &#xff08;三&#xff09;网上审核&#xff1a;11月8日10:00-11日18:00 &#xff08;四&#x…...

软考:常用协议和端口号

常用协议及其对应的端口号如下&#xff1a; TCP/IP协议&#xff1a; TCP&#xff08;传输控制协议&#xff09;&#xff1a;端口号为6UDP&#xff08;用户数据报协议&#xff09;&#xff1a;端口号为17 网络应用协议&#xff1a; HTTP&#xff08;超文本传输协议&#xff09;…...

Linux更改符号链接

目录 1. 删除旧链接 2. 创建新的符号链接 例如我的电脑上有两个版本的cuda&#xff0c;11.8和12.4 1. 删除旧链接 rm cuda 2. 创建新的符号链接 ln -s /usr/local/cuda-11.8/ /usr/local/cuda...

int main(int argc,char* argv[])详解

#include <stdio.h> //argc 是指命令行输入参数的个数; //argv[]存储了所有的命令行参数, //arg[0]通常指向程序中的可执行文件的文件名。在有些版本的编译器中还包括程序文件所在的路径。 //如:"d:\Production\Software\VC_2005_Test\Win32控制台应用程序\Vc_T…...

单片机原理及应用笔记:C51流程控制语句与项目实践

作者介绍 周瑞康&#xff0c;男&#xff0c;银川科技学院&#xff0c;计算机人工智能学院&#xff0c;2022级计算机科学与技术8班本科生&#xff0c;单片机原理及应用课程第八组。 指导老师&#xff1a;王兴泽 电子邮箱2082545622qq.com 前言&#xff1a; 本篇文章是参考《…...

大数据日志处理框架ELK方案

介绍应用场景大数据ELK日志框架安装部署 一&#xff0c;介绍 大数据日志处理框架ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;是一套完整的日志集中处理方案&#xff0c;以下是对其的详细介绍&#xff1a; 一、Elasticsearch&#xff08;ES&#xff09; 基本…...

VQGAN(2021-06:Taming Transformers for High-Resolution Image Synthesis)

论文&#xff1a;Taming Transformers for High-Resolution Image Synthesis 1. 背景介绍 2022年中旬&#xff0c;以扩散模型为核心的图像生成模型将AI绘画带入了大众的视野。实际上&#xff0c;在更早的一年之前&#xff0c;就有了一个能根据文字生成高清图片的模型——VQGAN…...

docker中使用ros2humble的rviz2不显示问题

这里写目录标题 docker中使用ros2humble的rviz2不显示问题删除 Docker 镜像和容器删除 Docker 容器Linux服务器下查看系统CPU个数、核心数、(make编译最大的)线程数总结&#xff1a; RVIZ2 不能显示数据集 docker中使用ros2humble的rviz2不显示问题 问题描述&#xff1a; roo…...

【AIGC】2024-arXiv-Lumiere:视频生成的时空扩散模型

2024-arXiv-Lumiere: A Space-Time Diffusion Model for Video Generation Lumiere&#xff1a;视频生成的时空扩散模型摘要1. 引言2. 相关工作3. Lumiere3.1 时空 U-Net (STUnet)3.2 空间超分辨率的多重扩散 4. 应用4.1 风格化生成4.2 条件生成 5. 评估和比较5.1 定性评估5.2 …...

正则表达式:文本处理的强大工具

正则表达式是一种强大的文本处理工具&#xff0c;它允许我们通过定义一系列的规则来匹配、搜索、替换或分割文本。在编程、文本编辑、数据分析和许多其他领域中&#xff0c;正则表达式都扮演着重要的角色。本文将介绍正则表达式的基本概念、语法和一些实际应用。 正则表达式的…...

Doris单机安装

1、安装包下载 官网地址&#xff1a;https://doris.apache.org/zh-CN/docs/gettingStarted/quick-start/ 下载地址&#xff1a;https://apache-doris-releases.oss-accelerate.aliyuncs.com/apache-doris-3.0.2-bin-x64.tar.gz 2、操作系统环境准备 #环境准备 cat /proc/cp…...

ubuntu内核更新导致显卡驱动掉的解决办法

方法1&#xff0c;DKMS指定内核版本 用第一个就行 1&#xff0c;借鉴别人博客解决方法 2&#xff0c;借鉴别人博客解决方法 方法2&#xff0c;删除多于内核的方法 系统版本&#xff1a;ubuntu20.24 这个方法是下下策&#xff0c;如果重装驱动还是不行&#xff0c;就删内核在…...

【Java数据结构】树】

【Java数据结构】树 一、树型结构1.1 概念1.2 特点1.3 树的类型1.4 树的遍历方式1.5 树的表示形式1.5.1 双亲表示法1.5.2 孩子表示法1.5.3 孩子双亲表示法1.5.4 孩子兄弟表示法 二、树型概念&#xff08;重点&#xff09; 此篇博客希望对你有所帮助&#xff08;帮助你了解树&am…...

Java面试题——微服务篇

1.微服务的拆分原则/怎么样才算一个有效拆分 单一职责原则&#xff1a;每个微服务应该具有单一的责任。这意味着每个服务只关注于完成一项功能&#xff0c;并且该功能应该是独立且完整的。最小化通信&#xff1a;尽量减少服务之间的通信&#xff0c;服务间通信越少&#xff0c…...

Python 中 print 函数输出多行并且选择对齐方式

代码 # 定义各类别的标签和对应数量 categories ["class0", "class1", "class2", "class3", "class4", "class5"] counts [4953, 547, 5121, 8989, 6077, 4002]# 设置统一的列宽 column_width 10# 生成对齐后…...

书生营L0G3000 Git 基础知识

任务1: 破冰活动&#xff1a;自我介绍 用vi就行了 按照教程来就好了 git会报错密码&#xff0c;输入的时候换成token就好了 https://stackoverflow.com/questions/68775869/message-support-for-password-authentication-was-removed 提交。&#xff08;github上预览自己的…...

【C++初阶】模版入门看这一篇就够了

文章目录 1. 泛型编程2. 函数模板2. 1 函数模板概念2. 2 函数模板格式2. 3 函数模板的原理2. 4 函数模板的实例化2. 5 模板参数的匹配原则2. 6 补充&#xff1a;使用调试功能观察函数调用 3. 类模板3 .1 类模板的定义格式3. 2 类模板的实例化 1. 泛型编程 在C语言中&#xff0…...

Spring Bean创建流程

Spring Bean 创建流程图 大家总是会错误的理解Bean的“实例化”和“初始化”过程&#xff0c;总会以为初始化就是对象执行构造函数生成对象实例的过程&#xff0c;其实不然&#xff0c;在初始化阶段实际对象已经实例化出来了&#xff0c;初始化阶段进行的是依赖的注入和执行一…...

重学SpringBoot3-怎样优雅停机

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-怎样优雅停机 1. 什么是优雅停机&#xff1f;2. Spring Boot 3 优雅停机的配置3. Tomcat 和 Reactor Netty 的优雅停机机制3.1 Tomcat 优雅停机3.2 Reac…...

【数据结构】顺序表和链表

1.线性表 我们在C语言当中学过数组&#xff0c;其实呢&#xff0c;数组可以实现线性表&#xff0c;线性表理解上类似于数组&#xff0c;那么什么是线性表呢&#xff1f;线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见…...

Training language models to follow instructions with human feedback解读

前置知识方法数据集结论 前置知识 GPT的全称是Generative Pre-Trained Transformer&#xff0c;预训练模型自诞生之始&#xff0c;一个备受诟病的问题就是预训练模型的偏见性。因为预训练模型都是通过海量数据在超大参数量级的模型上训练出来的&#xff0c;对比完全由人工规则…...

线性回归矩阵求解和梯度求解

正规方程求解线性回归 首先正规方程如下&#xff1a; Θ ( X T X ) − 1 X T y \begin{equation} \Theta (X^T X)^{-1} X^T y \end{equation} Θ(XTX)−1XTy​​ 接下来通过线性代数的角度理解这个问题。 二维空间 在二维空间上&#xff0c;有两个向量 a a a和 b b b&…...

M3U8不知道如何转MP4?包能学会的4种格式转换教学!

在流媒体视频大量生产的今天&#xff0c;M3U8作为一种基于HTTP Live Streaming&#xff08;HLS&#xff09;协议的播放列表格式&#xff0c;广泛应用于网络视频直播和点播中。它包含了媒体播放列表的信息&#xff0c;指向了视频文件被分割成的多个TS&#xff08;Transport Stre…...

C++第4课——swap、switch-case-for循环(含视频讲解)

文章目录 1、课程代码2、课程视频 1、课程代码 #include<iostream> using namespace std; int main(){/* //第一个任务&#xff1a;学会swap int a,b,c;//从小到大排序输出 升序 cin>>a>>b>>c;//5 4 3if(a>b)swap(a,b);//4 5 3 swap()函数是用于交…...