玩玩Win2k内存篇 分页机制(三四)

基于图像分割的LIDC-IDRI 数据集预处理之数据下载篇 近期接触到了一个关于图像分割实验项目,关于数据预处理部分在此予以记录,和大家分享。 阅读详情

8种转换

由于页表被映射到了0xc0000000 开始的4MB地址空间。
所以我们也可以象CPU那样完成虚拟地址到物理地址的转换。

系统按照对应虚拟空间的先后顺序,把一个进程的页表映射在0xc0000000 开始的4MB地址空间中,把页目录映射在0xc0300000 开始的4KB地址空间中。于是我们可以做如下几种地址的相互转换。

1 虚拟地址->虚拟地址对应的PDE地址

PDE_Address=(VirtualAddress>>22)*4+0xC0300000 

2 虚拟地址->虚拟地址对应的PTE地址

PTE_Address=(VirtualAddress>>12)*4+0xC0000000 

3 虚拟地址->物理地址

如果 虚拟地址大于等于0x80000000 并且小于0xa0000000(在 Large Page 部分),
直接用虚拟地址减去0x80000000就得到了物理地址。
其他情况
取得该虚拟地址的PDE,判断是否有效。
有效的话,取得该虚拟地址的PTE,判断是否有效。
有效的话,将PTE的低12位清0加上虚拟地址的低12位就得到了物理地址。
由于页表和页目录在系统地址空间中,访问需要程序运行在ring0,所以要测试的话,需要写驱动程序。

unsigned int PDE;
unsigned int PTE;

if(VirtualAddress>=0x80000000 && VirtualAddress<0xa0000000)
{
PhysicalAddress=VirtualAddress-0x80000000;
}
else
{
PDE=*(unsigned int*)((VirtualAddress>>22)*4+0xC0300000);
if(PDE&0x00000001)
{
PTE=*(unsigned int*)((VirtualAddress>>12)*4+0xC0000000);
if(PTE&0x00000001)
{
PhysicalAddress=((PTE&0xFFFFF000)+(VirtualAddress&0x00000FFF));
}
}
}

4 一个PDE的地址->相应的虚拟地址范围

VirtualAddressStart=((PDE_Address-0xC0300000)/4)<<22
VirtualAddressEnd=VirtualAddressStart+0x003FFFFF

5 一个PTE的地址->相应的虚拟地址范围

VirtualAddressStart=((PTE_Address-0xC0000000)/4)<<12
VirtualAddressEnd=VirtualAddressStart+0x00000FFF

6 物理地址->虚拟地址

一个物理地址常常会对应多个虚拟地址。转换方法就是遍历所有页表和页目录,如果有效就比较该项的高20bit是否等于我们提供的物理地址的高20bit,如果相等,就找到了一个。比如该项是第i个PDE的,第j个PTE,那么虚拟地址等于,i*4M+j*4K+物理地址的低12bit。遍历所有页表和页目录找到每一个。

7 一个PTE的地址->相应PDE的地址

一个PTE的地址可以找到相应的虚拟地址范围,就可以找到虚拟地址对应的PDE地址
PDE_Address=(VirtualAddress>>22)*4+0xC0300000 
PDE_Address=((((PTE_Address-0xC0000000)/4)<<12)>>22)*4+0xC0300000 

8 一个PDE的地址->相应PTE的地址范围

一个PDE的地址可以找到相应的虚拟地址范围的开始地址,就可以找到该虚拟地址对应的PTE地址,
一个PDE对应1024个PTE,4K大小。

PTE_AddressStart=(VirtualAddress>>12)*4+0xC0000000
PTE_AddressStart=((((PDE_Address-0xC0300000)/4)<<22)>>12)*4+0xC0000000 
PTE_AddressEnd=PTE_AddressStart+0x00000FFF

无效页与 Page Fault

访问的虚拟地址所在页在物理内存中时,该虚拟地址所在页相应的 PDE,PTE 都有效,CPU 自动根据相应的PDE,PTE把虚拟地址转换成物理地址,完成访问。一个虚拟地址所在页不在物理内存中时,比如在硬盘上的交换文件中,该虚拟地址所在页相应的 PDE,PTE 都无效,访问该虚拟地址将引起 Page-Fault 异常(Exception)。从而使 CPU 转去执行异常处理程序,异常处理程序会做相应的处理。对于发现访问的虚拟地址所在的页在硬盘上的交换文件中,就从交换文件中读入该页到物理内存,重新使PTE有效,并指向正确的物理页。最后 CPU 重新执行引起异常的指令,这时该指令所访问的虚拟地址已经在物理内存中了,并且该虚拟地址的PTE也有效了,于是就可以顺利执行。

下面我们针对 x86 CPU 做更详细的说明。

当某条指令访问无效页时,比如指令 MOV EAX,InValidAddress ,执行这条指令时,CPU 会自动通过页目录和页表把虚拟地址 InValidAddress 转换成物理地址,在地址转换过程中,CPU 在从页表项得到物理页地址的同时,会进行页保护检查,比如看该页表项是否有效,是否是只读等等。当CPU发现指令中地址的页表项无效,就会引发异常(Exception)。异常也是由 CPU 实现的。这里引起的是一个 Page Fault 异常,它的中断号是 0xe (十进制14),需要注意的是 Page Fault 的中断号是 0xe 这是由 CPU 定义的( x86 CPU 的 从 0 - 31 这32个中断是由 CPU 定义的,CPU 将根据这个定义做相应工作)。在发生异常时,CPU 自动把一些寄存器压入堆栈,然后根据中断号,(通过IDTR寄存器找到中断描述符表)在中断描述符表中找到相应的中断描述符,根据中断描述符中的地址,转到异常处理程序。中断描述符是由Win2k设置,异常处理程序也是由Win2k决定。对于 Win2k Build 2195 来说,中断 0xe 的处理程序是 ntoskrnl!KiTrap0E 地址在 804648a4 。当转到KiTrap0E 时,CPU 已经在堆栈中压入了下面的内容

|-------------| 
|    EFLAGS   |
|-------------|
|      CS     |
|-------------|
|     EIP     |
|-------------|
|  Error Code |
|-------------|<---- [ ESP ]


page-fault 异常 (#PF) 的 Error Code 定义如下( CPU 定义 )
                                   |  3 | 2 | 1 | 0 |
+---------------------------------------------------+ 
|          Reserved                |RSVD|U/S|R/W| P |
+---------------------------------------------------+ 

P   0 错误由无效页引起
    1 错误由违反页保护引起
W/R 0 引起错误的内存访问是读
    1 引起错误的内存访问是写
U/S 0 访问错误时处理器处在管理模式
    1 访问错误时处理器处在用户模式

需要说明的是堆栈中压入的 EIP 就是引发异常的指令地址,将来将根据这个地址重新执行该指令。而寄存器 cr2 中是引发异常时访问的地址。

#PF异常处理程序 KiTrap0E(由Win2k提供)将会调用 ntoskrnl!MmAccessFault ,MmAccessFault 通过 CR2 中的访问地址,计算出相应的 PDE,PTE地址,通过分析PTE中的内容,可以知道是哪种情况引起的异常,并根据情况作出相应的处理。

当发生异常时,CPU会把一些寄存器压入堆栈,转到相应的异常处理程序(由Win2k提供),Win2k 在异常处理程序中又会把一些寄存器压入堆栈,最后会在堆栈中形成一个 KTRAP_FRAME 结构。这个 KTRAP_FRAME 是 ntoskrnl!MmAccessFault 的参数之一。

!strct KTRAP_FRAME
struct _KTRAP_FRAME (sizeof=140)
+00 uint32 DbgEbp
+04 uint32 DbgEip
+08 uint32 DbgArgMark
+0c uint32 DbgArgPointer
+10 uint32 TempSegCs
+14 uint32 TempEsp
+18 uint32 Dr0
+1c uint32 Dr1
+20 uint32 Dr2
+24 uint32 Dr3
+28 uint32 Dr6
+2c uint32 Dr7
+30 uint32 SegGs
+34 uint32 SegEs
+38 uint32 SegDs
+3c uint32 Edx
+40 uint32 Ecx
+44 uint32 Eax
+48 uint32 PreviousPreviousMode
+4c struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList
+50 uint32 SegFs
+54 uint32 Edi
+58 uint32 Esi
+5c uint32 Ebx
+60 uint32 Ebp
+64 uint32 ErrCode
+68 uint32 Eip
+6c uint32 SegCs
+70 uint32 EFlags
+74 uint32 HardwareEsp
+78 uint32 HardwareSegSs
+7c uint32 V86Es
+80 uint32 V86Ds
+84 uint32 V86Fs
+88 uint32 V86Gs

系统,CPU 与 页目录项,页表项的关系

一个进程的PDE(页目录项),PTE(页表项)是由系统维护的。地址转换由CPU自动完成。访问无效地址CPU 将产生异常,执行异常处理程序。异常处理程序是系统提供的。

对于有效的 PDE,PTE,他们的格式大部分由CPU定义,CPU 将按照这个格式,根据每一位的值,决定相应的处理方式。CPU 利用这个格式中自己定义的物理页物理地址的部分来进行地址转换,自己定义的一些标志位来实现页的保护。系统必须按照这个格式定义,来维护 PDE 和 PTE。

x86 CPU 的有效页表项,CPU 定义如下

struct _HARDWARE_PTE_X86 (sizeof=4)
bits0-0 Valid
bits1-1 Write
bits2-2 Owner
bits3-3 WriteThrough
bits4-4 CacheDisable
bits5-5 Accessed
bits6-6 Dirty
bits7-7 LargePage
bits8-8 Global
bits9-11 reserved
bits12-31 PageFrameNumber

其中要注意的是 bits9-11 reserved 这3位,CPU 没有定义,留给操作系统使用。

对于无效的 PDE,PTE,他们的格式大部分由系统定义。当 CPU 发现访问的 PDE,PTE 无效时,就会转去执行异常处理程序。异常处理程序是由系统提供的。系统将按照自己定义的格式,根据每一位的值,决定相应的处理方式。

x86 CPU 的无效页表项,CPU 定义如下

struct _HARDWARE_PTE_X86 (sizeof=4)
bits0-0 Valid
bits1-31 reserved

对于无效页来说,CPU只定义了bits0-0,用来判断是否有效。无效页的 bits0-0 值为0。其他位都留给操作系统使用。

为什么地址空间的大小是4G

    使用32bit的cpu话,处理32bit的数据比较好。这就决定了在代码中将被非常频繁使用的地址是32bit的。而32bit的地址可以寻址的范围是从0x00000000 到 0xFFFFFFFF ,也就是4GB(对于以字节为单位的编址)大小的范围。也就决定了地址空间的大小是 4G。


为什么一页的大小是4K(只是我个人的看法)

    确定了虚拟地址空间的大小之后,又决定使用分页来进行内存管理。权衡把虚拟地址转换成物理地址的效率和保存分页信息所用的代价之后,决定了使用页目录和页表2层转换。而使用2层转换最公平的方式就是页目录中的项数和页表中的项数都一样。由于所要保存足够的信息,又由于是32bit cpu 所以,一个PTE或者一个PDE的大小是4个字节也是有理由的。当这些确定了之后,也就确定了一页的大小。

    设一页大小是 x ,单位是字节。那么这样一页中有 (x/4) 个PTE或者PDE。一个PTE可以对应一页的地址空间,也就是大小为x个字节的地址空间,一个 Page Table 页中有(x/4) 个PTE,所以一个 Page Table 页可以对应(x/4)*x个字节的地址空间。一个PDE可以找到一个 Page Table 页,也就是大小为(x/4)*x个字节的地址空间,一个 Page Directory 页中有 (x/4) 个PDE,所以一个 Page Directory 页可以对应 (x/4)(x/4)*x 个字节的地址空间。现在需要用一个PDE页对应整个地址空间,整个地址空间是4G字节,也就是4*1024*1024*1024 个字节。所以 (x/4)(x/4)*x=4*1024*1024*1024 可以算出 x = 4*1024 字节,也就是 4k 字节。

地址转换的性能

    一旦使用了分页(CPU的相关寄存器的分页标志位被设置),所有的内存操作都使用的是虚拟地址,CPU会自动转换成物理地址,可以想象将会有非常大量的地址转换,一次转换可能需要读页目录表,读页表,最后才在页中读写数据。所以有理由把一些页目录项和页表项放入高速缓存中来提高地址转换的速度。实际中也是这样做的,对于 x86 cpu ,如果启用了分页,cpu 会使用一个叫做 TLB(Translation Look-Aside Buffer) 的高速缓存来存储经常被用到的页目录和页表项。

页目录的地址为什么是C0300000,1个页目录加上1024个页表为什么只使用了1024*4K的地址空间

    对于要映射整个4G地址空间,是需要1024个页表和1个页目录的,每个都是4KB大小,也就是 1024*4KB+1*4KB=4MB+4KB。而实际中Win2k把每个进程的页目录和页表映射到了从 0xC0000000到0xC03FFFFF 这4MB的地址空间中(页目录在0xC0300000开始的4K)。注意是4MB地址空间而不是4MB+4KB。1024个页表和1个页目录,应该是需要(1024+1)*4KB的地址空间的。而现在Win2k只使用了1024*4KB的地址空间这是为什么?

    原因就是页表被映射到了进程的地址空间。
    如果页表和页目录没有被映射到进程的地址空间中,而一个进程的4GB地址空间又都映射了物理内存的话,那么就确实需要1024个物理页来存放页表,和另外1个物理页来存放页目录,也就是需要(1024+1)*4KB的物理内存。
但是页表被映射到了进程的地址空间中,这导致了一个页表的内容和页目录的内容是完全一样的,正是这种完全相同,使得将1024个页表加1个页目录映射到地址空间只需要1024*4KB的地址空间,其中的一个页表和页目录完全重合了。
    一个页表1024项,每项对应4KB地址空间,一个页表对应4MB的地址空间。1024个对应了整个4GB地址空间。1024个页表也被映射到了从 0xC0000000到0xC03FFFFF 的4MB地址空间中。这4MB地址空间也是由一个页表来对应的。我们来看对应于从 0xC0000000到0xC03FFFFF 这4MB的地址空间的页表。该页表有1024项,每项对应一页的地址空间,表明是否在物理内存中,如果在,物理地址是多少。而这个页表是对应页表所在的4MB地址空间的,所以它的每一项对应的一页,正是每个页表所在的页。也就是说这个页表的每一项指出了一个页表是否有物理内存映射,如果有的话,物理地址是多少。这正是页目录所做的工作。把1024个页表映射到了地址空间,导致了1024个页表中的一个的内容和页目录完全重合,它既是页目录又是页表。所以1个页目录加上1024个页表只使用了1024*4K的地址空间。

    页表被映射到地址空间的什么地方,是由操作系统的设计者决定的,他会综合考虑各种问题,作出最后的决定。不过一旦页表所在的地址空间的地址决定了,那么页目录的地址也就决定了,除非他打算多使用一页的地址空间保存和现在一个页表中完全相同的内容。

    Win2k中把页表映射到了从 0xC0000000到0xC03FFFFF 的4MB地址空间中,我们来计算一下负责这4M地址空间的那个页表的地址,那个页表就是和页目录重合的页表。4MB地址空间的首地址0xC0000000显然是由该页表的第一项负责的,我们用这个地址来计算。PTE_Address=(VirtualAddress>>12)*4+0xC0000000,
(0xC0000000>>12)*4+0xC0000000=0xC0000*4+0xC0000000=0x300000+0xC0000000=0xC0300000
正是页目录的虚拟地址。

    0xC0300000到0xC0300FFF这4KB的地址空间,作为普通一页的话由一个PTE指明所在物理页的物理地址,作为一个页表所在页的话由一个PDE指明所在物理页的物理地址,作为页目录的话由CR3指明所在物理页的物理地址。
    0xC0300000到0xC0300FFF这4KB作为普通的一页是由哪个PTE对应呢,PTE_Address=(VirtualAddress>>12)*4+0xC0000000,计算得到0xC0300C00。也就是说0xC0300C00处的4个字节作为PTE,指明了0xC0300000到0xC0300FFF这4KB所在的物理页的物理地址。
    0xC0300000到0xC0300FFF这4KB作为一个页表所在页是由哪个PDE对应呢,PDE_Address=(VirtualAddress>>22)*4+0xC0300000,计算得到0xC0300C00。也就是说0xC0300C00处的4个字节作为PDE,指明了0xC0300000到0xC0300FFF这4KB所在的物理页的物理地址。
    0xC0300000到0xC0300FFF这4KB作为页目录的话,由 CR3 指明所在物理页的物理地址。所以 0xC0300C00 处的4个字节的值,把标志位清0的话,就等于 CR3 中的值。

下面这个特殊地址的转换过程,你就会发现页目录和一个页表确实是重合了,而且它既是页目录又是页表。
虚拟地址 C0300C00 转换成物理地址,以下输出来自 kd。

kd> r cr3
r cr3
cr3=069ca000
// CPU第一步根据cr3里的物理地址找到页目录所在物理页
// 取C0300000的最高10bit, 1100 0000 00 = 11 0000 0000 = 300 ,作为页目录项索引,由于每项长4字节
// 所以页目录项的物理地址为 300*4+069ca000=069cac00
kd> !dd 069cac00
!dd 069cac00
# 69cac00 069ca063 01e2b063 00000000 01670163
# 69cac10 01671163 01672163 01673163 01674163
# 69cac20 01675163 01676163 01657163 01658163
# 69cac30 01659163 0165a163 0165b163 0165c163
# 69cac40 0165d163 0165e163 0165f163 016c0163
# 69cac50 01681163 01682163 01683163 01684163
# 69cac60 01685163 01686163 01687163 01688163
# 69cac70 01689163 0168a163 0168b163 0168c163
// 结果该页目录项中的物理地址(页目录项的高20bit是物理页帧号,低12bit是标志)是069ca000
// 注意页表的物理地址等于页目录的物理地址,说明了页目录和页表是重合的。

// CPU第二步根据该目录项中的物理地址找到页表所在的物理页
// 取C0300000的最高10bit之后的10bit, 11 0000 0000 = 300 ,作为页表项索引,由于每项长4字节
// 所以页表项的物理地址为 300*4+069ca000=069cac00
kd> !dd 069cac00
!dd 069cac00
# 69cac00 069ca063 01e2b063 00000000 01670163
# 69cac10 01671163 01672163 01673163 01674163
# 69cac20 01675163 01676163 01657163 01658163
# 69cac30 01659163 0165a163 0165b163 0165c163
# 69cac40 0165d163 0165e163 0165f163 016c0163
# 69cac50 01681163 01682163 01683163 01684163
# 69cac60 01685163 01686163 01687163 01688163
# 69cac70 01689163 0168a163 0168b163 0168c163
// 结果该页表项中的物理地址(页目录项的高20bit是物理页帧号,低12bit是标志)是069ca000

// CPU第三步根据页表项中的物理地址找到物理页,用C0300000低12bit作为页内偏移,
// 相加得到物理地址 ,低12bit, 1100 0000 0000 =c00,c00+069ca000=069cac00
// 这样就得到了虚拟地址 C0300C00 对应的物理地址 069cac00
kd> !dd 069cac00
!dd 069cac00
# 69cac00 069ca063 01e2b063 00000000 01670163
# 69cac10 01671163 01672163 01673163 01674163
# 69cac20 01675163 01676163 01657163 01658163
# 69cac30 01659163 0165a163 0165b163 0165c163
# 69cac40 0165d163 0165e163 0165f163 016c0163
# 69cac50 01681163 01682163 01683163 01684163
# 69cac60 01685163 01686163 01687163 01688163
# 69cac70 01689163 0168a163 0168b163 0168c163
// 该处的DWORD值为 069ca063
// 前面看到 CR3 的值为 069ca000

一个进程的页目录和页表本身所需要的物理内存

    一个进程需要物理页来放页表和页目录,如果有1024个页表,那么不是需要1024*4K=4M 的物理内存吗?一个进程只是页表信息就需要4M 的物理内存吗?不过实际上,当页目录中的一项有效标志位(第0位,最低位)为0,那么该项对应的页表就不存在,就不需要占用一个物理页。在前面的页目录的例子中我们已经看到了用户地址空间中有多少个无效项(系统地址空间中的东西,基本上大家都一样)。也就是说实际上为了保存一个进程的页表用不了多少物理内存。
    这也就是为什么要使用2级的地址转换。如果只使用一级,只用页表,那么每个进程1024个页表就必须都放在物理内存中,即每个进程需要4M物理内存来放页表,这个代价对目前来说是太大了。使用2级地址转换,多一个步骤,虽然转换速度会变慢一些,但是节省了物理内存,在速度与内存之间寻求了平衡。

页目录项的 Write 标志位
    从前面页目录的例子中,可以看到页目录的标志 bits1-1 Write ,总是为1。
表明页表所在的物理页总是可写的。这是因为页表和页目录是由系统维护的,系统需要向其中写入东西。

保护模式,Flat Model,线性地址,逻辑地址,ring0,ring3

以下内容只针对x86

    Win2k 运行在保护模式(Protected Mode )下,是否运行在保护模式下,取决于PE(Protection Enable)标志位 (CR0 的 bit 0 )。为0表示实模式(Real-Address Mode)(当机器加电启动和重启的时候,x86 CPU 处于实模式下),为1表示保护模式。观察Win2k的CR0的bit0,值为1,表明运行在保护模式下。

    对于x86体系结构来说,分段机制是必须被使用的(分页机制是可选的,分段和分页可以同时使用,Win2k使用了分页机制)。比如CPU执行代码总是参考代码段。在保护模式下,段寄存器(CS,DS,SS,ES,FS,GS)中放着的是段选择符,用于在某个描述符表中寻找段描述符。

    保护模式下的分段机制,有多种使用模型,Win2k使用的是一种叫做 Flat Model (平坦模型)。就是通过把段基址设为0,段界限设为4G,这样每个段都是整个地址空间,这样就隐藏了分段机制。

全局描述符表
全局描述符表(GDT,global descriptor table)用来存放描述符,整个系统只有一个。由GDTR(全局描述符表寄存器)指出所在位置和大小。由于GDTR中的界限部分为16位,所以全局描述符表最大可以为64KB,每个描述符占8个字节,所以全局描述符表中最多可以放8192个描述符。

段选择符(CS,DS,SS,ES,FS,GS中放的是段选择符)的格式定义如下(来自Intel手册):
段选择符,长2个字节,16bit。

Index: bit3-bit15 13位 在全局描述符表或者局部描述符表中的索引,可以索引8192个描述符
TI flag: bit2 1位 TI,table indicator,指定描述符表。为0在全局描述符表(GDT)中选择,为1在 当前的局部描述符表(LDT)中选择
RPL: bit0-bit1 2位 选择符的特权级

全局描述符表寄存器(GDTR,48bit)格式定义如下(来自Intel手册):

Table Limit: bit0-bit15 16位 指定描述符表的字节数。
Base Address: bit16-bit47 32位 指定描述符表的基地址。是线性地址。

段描述符格式定义如下:

typedef struct _SEG_DESCRIPTOR //size 8 bytes
{
unsigned short limit_0_15;
unsigned short base_0_15;
unsigned char base_16_23;

unsigned char accessed : 1;
unsigned char r_w_e : 1;
unsigned char e_c : 1;
unsigned char code_data : 1;
unsigned char app_system : 1;
unsigned char dpl : 2;
unsigned char present : 1;

unsigned char limit_16_19 : 4;
unsigned char unused : 1;
unsigned char always_0 : 1;
unsigned char seg_16_32 : 1;
unsigned char granularity : 1;

unsigned char base_24_31;
} SEG_DESCRIPTOR ,*PSEG_DESCRIPTOR;

limit_0_15,limit_16_19,granularity
段界限,两部分,共20bit。
如果granularity位为0,段界限的值以字节为单位。段最大为1MB。
如果granularity位为1,段界限的值以4KB为单位。段最大为4GB。

base_0_15,base_16_23,base_24_31
段基址,三部分,共32bit。

accessed,readable,conforming,code_data
accessed表示是否已经被访问过了。
code_data为0表示代码段,为1表示数据段。
对于数据段,r_w_e位表示是否可写,为0只读,为1可写。e_c位表示增长方向,为1向下增长。
对于代码段,r_w_e位表示是否可读,为0只执行,为1可读。e_c位表示conforming或者nonconforming。

app_system
0 = system, 1 = code or data。为1时,表明是代码段或者数据段。为0时表明是系统段,系统段一些位的含义和数据段或者代码段不同。有以下几种系统段:16-Bit TSS (Available),LDT,16-Bit TSS (Busy),16-Bit Call Gate,Task Gate,16-Bit Interrupt Gate,16-Bit Trap Gate,32-Bit TSS (Available),32-Bit TSS (Busy),32-Bit Call Gate,32-Bit Interrupt Gate,32-Bit Trap Gate。更多内容可以参考 Intel 手册。

dpl
指定段的特权级。

present
段是否在内存中

seg_16_32
段是16位还是32位。


下面看Win2k中实际的用法

一个ring3程序的段寄存器
(使用VC调试器获得,也可以自己写程序获得,也可以用SoftICE断到一个ring3程序中获得...)
CS = 001B DS = 0023 ES = 0023 SS = 0023 FS = 0038 GS = 0000

一个ring0程序的段寄存器
(使用KD获得,也可以写驱动程序获得,也可以用SoftICE断到一个ring0程序中获得...)
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000

GDTR的内容可以由指令sgdt得到(sgdt不是特权指令,ring3也可以执行)。
下面的内容来自SoftICE:
:gdt
Sel. Type Base Limit DPL Attributes
GDTbase=80036000 Limit=03FF
// GDTbase=80036000 Limit=03FF ,可以使用 sgdt指令做验证
// 16bit的界限值为03FF,表明Win2k中GDT只有1K,可以存放128个描述符

0008 Code32 00000000 FFFFFFFF 0 P RE
0010 Data32 00000000 FFFFFFFF 0 P RW
001B Code32 00000000 FFFFFFFF 3 P RE
0023 Data32 00000000 FFFFFFFF 3 P RW
0028 TSS32 801F4000 000020AB 0 P B
0030 Data32 FFDFF000 00001FFF 0 P RW
003B Data32 00000000 00000FFF 3 P RW
0043 Data16 00000400 0000FFFF 3 P RW
0048 Reserved 00000000 00000000 0 NP
0050 TSS32 80470040 00000068 0 P
0058 TSS32 804700A8 00000068 0 P
0060 Data16 00022AB0 0000FFFF 0 P RW
0068 Data16 000B8000 00003FFF 0 P RW
0070 Data16 FFFF7000 000003FF 0 P RW
0078 Code16 80400000 0000FFFF 0 P RE
0080 Data16 80400000 0000FFFF 0 P RW
0088 Data16 00000000 00000000 0 P RW
0090 Reserved 00000000 00000000 0 NP
0098 Reserved 00000000 00000000 0 NP
00A0 TSS32 8141A348 00000068 0 P
00A8 Reserved 00000000 00000000 0 NP
00B0 Reserved 00000000 00000000 0 NP
00B8 Reserved 00000000 00000000 0 NP
00C0 Reserved 00000000 00000000 0 NP
00C8 Reserved 00000000 00000000 0 NP
00D0 Reserved 00000000 00000000 0 NP
00D8 Reserved 00000000 00000000 0 NP
00E0 Code16 F0450000 0000FFFF 0 P RE ED
00E8 Data16 00000000 0000FFFF 0 P RW
00F0 Code16 8042DCE8 000003B7 0 P EO
00F8 Data16 00000000 0000FFFF 0 P RW
0100 Data32 F0460000 0000FFFF 0 P RW
0108 Data32 F0460000 0000FFFF 0 P RW
0110 Data32 F0460000 0000FFFF 0 P RW
0118 Reserved 00008003 00006120 0 NP
0120 Reserved 00008003 00006128 0 NP
0128 Reserved 00008003 00006130 0 NP
0130 Reserved 00008003 00006138 0 NP
0138 Reserved 00008003 00006140 0 NP
0140 Reserved 00008003 00006148 0 NP
0148 Reserved 00008003 00006150 0 NP
0150 Reserved 00008003 00006158 0 NP
0158 Reserved 00008003 00006160 0 NP
0160 Reserved 00008003 00006168 0 NP
0168 Reserved 00008003 00006170 0 NP
0170 Reserved 00008003 00006178 0 NP
0178 Reserved 00008003 00006180 0 NP
0180 Reserved 00008003 00006188 0 NP
0188 Reserved 00008003 00006190 0 NP
0190 Reserved 00008003 00006198 0 NP
0198 Reserved 00008003 000061A0 0 NP
01A0 Reserved 00008003 000061A8 0 NP
01A8 Reserved 00008003 000061B0 0 NP
01B0 Reserved 00008003 000061B8 0 NP
01B8 Reserved 00008003 000061C0 0 NP
01C0 Reserved 00008003 000061C8 0 NP
01C8 Reserved 00008003 000061D0 0 NP
01D0 Reserved 00008003 000061D8 0 NP
01D8 Reserved 00008003 000061E0 0 NP
01E0 Reserved 00008003 000061E8 0 NP
01E8 Reserved 00008003 000061F0 0 NP
01F0 Reserved 00008003 000061F8 0 NP
01F8 Reserved 00008003 00006200 0 NP
0200 Reserved 00008003 00006208 0 NP
0208 Reserved 00008003 00006210 0 NP
0210 Reserved 00008003 00006218 0 NP
0218 Reserved 00008003 00006220 0 NP
0220 Reserved 00008003 00006228 0 NP
0228 Reserved 00008003 00006230 0 NP
0230 Reserved 00008003 00006238 0 NP
0238 Reserved 00008003 00006240 0 NP
0240 Reserved 00008003 00006248 0 NP
0248 Reserved 00008003 00006250 0 NP
0250 Reserved 00008003 00006258 0 NP
0258 Reserved 00008003 00006260 0 NP
0260 Reserved 00008003 00006268 0 NP
0268 Reserved 00008003 00006270 0 NP
0270 Reserved 00008003 00006278 0 NP
0278 Reserved 00008003 00006280 0 NP
0280 Reserved 00008003 00006288 0 NP
0288 Reserved 00008003 00006290 0 NP
0290 Reserved 00008003 00006298 0 NP
0298 Reserved 00008003 000062A0 0 NP
02A0 Reserved 00008003 000062A8 0 NP
02A8 Reserved 00008003 000062B0 0 NP
02B0 Reserved 00008003 000062B8 0 NP
02B8 Reserved 00008003 000062C0 0 NP
02C0 Reserved 00008003 000062C8 0 NP
02C8 Reserved 00008003 000062D0 0 NP
02D0 Reserved 00008003 000062D8 0 NP
02D8 Reserved 00008003 000062E0 0 NP
02E0 Reserved 00008003 000062E8 0 NP
02E8 Reserved 00008003 000062F0 0 NP
02F0 Reserved 00008003 000062F8 0 NP
02F8 Reserved 00008003 00006300 0 NP
0300 Reserved 00008003 00006308 0 NP
0308 Reserved 00008003 00006310 0 NP
0310 Reserved 00008003 00006318 0 NP
0318 Reserved 00008003 00006320 0 NP
0320 Reserved 00008003 00006328 0 NP
0328 Reserved 00008003 00006330 0 NP
0330 Reserved 00008003 00006338 0 NP
0338 Reserved 00008003 00006340 0 NP
0340 Reserved 00008003 00006348 0 NP
0348 Reserved 00008003 00006350 0 NP
0350 Reserved 00008003 00006358 0 NP
0358 Reserved 00008003 00006360 0 NP
0360 Reserved 00008003 00006368 0 NP
0368 Reserved 00008003 00006370 0 NP
0370 Reserved 00008003 00006378 0 NP
0378 Reserved 00008003 00006380 0 NP
0380 Reserved 00008003 00006388 0 NP
0388 Reserved 00008003 00006390 0 NP
0390 Reserved 00008003 00006398 0 NP
0398 Reserved 00008003 000063A0 0 NP
03A0 Reserved 00008003 000063A8 0 NP
03A8 Reserved 00008003 000063B0 0 NP
03B0 Reserved 00008003 000063B8 0 NP
03B8 Reserved 00008003 000063C0 0 NP
03C0 Reserved 00008003 000063C8 0 NP
03C8 Reserved 00008003 000063D0 0 NP
03D0 Reserved 00008003 000063D8 0 NP
03D8 Reserved 00008003 000063E0 0 NP
03E0 Reserved 00008003 000063E8 0 NP
03E8 Reserved 00008003 000063F0 0 NP
03F0 Reserved 00008003 000063F8 0 NP
03F8 Reserved 00000000 00000000 0 NP

:dd 80036000 l 400
0010:80036000 00000000 00000000 0000FFFF 00CF9B00 ................
0010:80036010 0000FFFF 00CF9300 0000FFFF 00CFFB00 ................
0010:80036020 0000FFFF 00CFF300 400020AB 80008B1F ......... .@....
0010:80036030 F0000001 FFC093DF 00000FFF 0040F300 ..............@.
0010:80036040 0400FFFF 0000F200 00000000 00000000 ................
0010:80036050 00400068 80008947 00A80068 80008947 h.@.G...h...G...
0010:80036060 2AB0FFFF 00009302 80003FFF 0000920B ...*.....?......
0010:80036070 700003FF FF0092FF 0000FFFF 80009A40 ...p........@...
0010:80036080 0000FFFF 80009240 00000000 00009200 ....@...........
0010:80036090 00000000 00000000 00000000 00000000 ................
0010:800360A0 A3480068 81008941 00000000 00000000 h.H.A...........
0010:800360B0 00000000 00000000 00000000 00000000 ................
0010:800360C0 00000000 00000000 00000000 00000000 ................
0010:800360D0 00000000 00000000 00000000 00000000 ................
0010:800360E0 0000FFFF F0009F45 0000FFFF 00009200 ....E...........
0010:800360F0 DCE803B7 80009842 0000FFFF 00009200 ....B...........
0010:80036100 0000FFFF F0409346 0000FFFF F0409346 ....F.@.....F.@.
0010:80036110 0000FFFF F0409346 80036120 00000000 ....F.@. a......
0010:80036120 80036128 00000000 80036130 00000000 (a......0a......
0010:80036130 80036138 00000000 80036140 00000000 8a......@a......
0010:80036140 80036148 00000000 80036150 00000000 Ha......Pa......
0010:80036150 80036158 00000000 80036160 00000000 Xa......`a......
0010:80036160 80036168 00000000 80036170 00000000 ha......pa......
0010:80036170 80036178 00000000 80036180 00000000 xa.......a......
0010:80036180 80036188 00000000 80036190 00000000 .a.......a......
0010:80036190 80036198 00000000 800361A0 00000000 .a.......a......
0010:800361A0 800361A8 00000000 800361B0 00000000 .a.......a......
0010:800361B0 800361B8 00000000 800361C0 00000000 .a.......a......
0010:800361C0 800361C8 00000000 800361D0 00000000 .a.......a......
0010:800361D0 800361D8 00000000 800361E0 00000000 .a.......a......
0010:800361E0 800361E8 00000000 800361F0 00000000 .a.......a......
0010:800361F0 800361F8 00000000 80036200 00000000 .a.......b......
0010:80036200 80036208 00000000 80036210 00000000 .b.......b......
0010:80036210 80036218 00000000 80036220 00000000 .b...... b......
0010:80036220 80036228 00000000 80036230 00000000 (b......0b......
0010:80036230 80036238 00000000 80036240 00000000 8b......@b......
0010:80036240 80036248 00000000 80036250 00000000 Hb......Pb......
0010:80036250 80036258 00000000 80036260 00000000 Xb......`b......
0010:80036260 80036268 00000000 80036270 00000000 hb......pb......
0010:80036270 80036278 00000000 80036280 00000000 xb.......b......
0010:80036280 80036288 00000000 80036290 00000000 .b.......b......
0010:80036290 80036298 00000000 800362A0 00000000 .b.......b......
0010:800362A0 800362A8 00000000 800362B0 00000000 .b.......b......
0010:800362B0 800362B8 00000000 800362C0 00000000 .b.......b......
0010:800362C0 800362C8 00000000 800362D0 00000000 .b.......b......
0010:800362D0 800362D8 00000000 800362E0 00000000 .b.......b......
0010:800362E0 800362E8 00000000 800362F0 00000000 .b.......b......
0010:800362F0 800362F8 00000000 80036300 00000000 .b.......c......
0010:80036300 80036308 00000000 80036310 00000000 .c.......c......
0010:80036310 80036318 00000000 80036320 00000000 .c...... c......
0010:80036320 80036328 00000000 80036330 00000000 (c......0c......
0010:80036330 80036338 00000000 80036340 00000000 8c......@c......
0010:80036340 80036348 00000000 80036350 00000000 Hc......Pc......
0010:80036350 80036358 00000000 80036360 00000000 Xc......`c......
0010:80036360 80036368 00000000 80036370 00000000 hc......pc......
0010:80036370 80036378 00000000 80036380 00000000 xc.......c......
0010:80036380 80036388 00000000 80036390 00000000 .c.......c......
0010:80036390 80036398 00000000 800363A0 00000000 .c.......c......
0010:800363A0 800363A8 00000000 800363B0 00000000 .c.......c......
0010:800363B0 800363B8 00000000 800363C0 00000000 .c.......c......
0010:800363C0 800363C8 00000000 800363D0 00000000 .c.......c......
0010:800363D0 800363D8 00000000 800363E0 00000000 .c.......c......
0010:800363E0 800363E8 00000000 800363F0 00000000 .c.......c......
0010:800363F0 800363F8 00000000 00000000 00000000 .c..............

我们来分析一下:

对于ring3,CS=001B DS=0023 ES=0023 SS=0023 FS=0038 GS=0000,
DS,ES,SS,指向同一个段。
001B=0000000000011 0 11
0023=0000000000100 0 11
所以根据前面段选择符的定义,表示 CS,DS,ES,SS 都是在全局描述符表中进行选择。
选择符的特权级都是3(ring3)。对于CS,索引为3,由于每个描述符为8个字节,描述符相对于GDT开始处的地址为0x3*0x8=0x18。对于,DS,ES,SS,索引为4,描述符相对于GDT开始处的地址为0x4*0x8=0x20。

根据前面段描述符的定义,分析 80036018 开始的8个字节,和 80036020 开始的8个字节。
0010:80036010 0000FFFF 00CF9300 0000FFFF 00CFFB00 ................
0010:80036020 0000FFFF 00CFF300 400020AB 80008B1F ......... .@....
可以得出
Sel. Type Base Limit DPL Attributes
001B Code32 00000000 FFFFFFFF 3 P RE
0023 Data32 00000000 FFFFFFFF 3 P RW
可以看到,CS 对应的全局描述符表中的段描述符,是一个代码段,段基址为0,段界限为4G,DPL为3(ring3)。DS,ES,SS 对应的全局描述符表中的段描述符,是一个数据段,段基址为0,段界限为4G,DPL为3(ring3)。

对于ring0, cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000,
0008=0000000000001 0 00
0010=0000000000010 0 00
0023=0000000000100 0 11
Sel. Type Base Limit DPL Attributes
0008 Code32 00000000 FFFFFFFF 0 P RE
0010 Data32 00000000 FFFFFFFF 0 P RW
0023 Data32 00000000 FFFFFFFF 3 P RW
可以看到,代码段和堆栈段的DPL为0(ring0)。三个描述符的段基址都为0,段界限都为4G。

CS,DS,ES,SS,这些段都完全重叠了,而且都是当前的4G地址空间。和段的另外一种使用方法(不同的段在不同的一块地址空间)相比,现在的使用方法,整个地址空间是连续的,平坦的,所以叫做平坦模式(Flat Model)。

关于段寄存器(CS,DS,SS,ES,FS,GS),不可能每次参考一个段的时候CPU都去从内存中取出段描述符然后再做相应的处理,每个段寄存器还有看不到的部分,他们中存储着相应的段描述符中的信息。

全局描述表中的第一项不被使用,设为了"null descriptor" 。
0010:80036000 00000000 00000000
用来初始化没有被使用的段寄存器,以防止如果被不小心使用一个没有被使用的段寄存器,对系统造成不良影响。Win2k中,段寄存器GS就没有被使用,值都设为了0。

前面分析了 CS,DS,ES,SS 。对于 GS 来说,我们可以看到它的值总是为0,Win2k 并没有使用它。而对于 FS 来说,对于使用平坦模式的 Win2k 来说,它是一个例外,在其他地方会对 FS 做更多的介绍。

在 ntddk.h 中的以下内容,也说明了上面的一些分析。
#define KGDT_NULL 0
#define KGDT_R0_CODE 8
#define KGDT_R0_DATA 16
#define KGDT_R3_CODE 24
#define KGDT_R3_DATA 32
#define KGDT_TSS 40
#define KGDT_R0_PCR 48
#define KGDT_R3_TEB 56
#define KGDT_VDM_TILE 64
#define KGDT_LDT 72
#define KGDT_DF_TSS 80
#define KGDT_NMI_TSS 88

线性地址(linear address),把32bit可寻址的4G地址空间看做连续的线性地址空间,线性地址空间中的地址叫线性地址。逻辑地址(logical address),逻辑地址由一个段选择符(16位)和一个段内偏移(32位)组成。也经常被叫做远程指针(far pointer)。x86 CPU 总是使用分段机制的(即使是使用了最少的分段机制的平坦模型),因此 x86 CPU 指令中的地址也总是逻辑地址,是相对于某一个段而言的地址。在保护模式下,x86 CPU 首先把逻辑地址转换成线性地址,然后再把线性地址转换成物理地址。

对于Win2k来说,由于使用了平坦模型,虚拟地址XXXXXXXX 和线性地址XXXXXXXX 指的是同一个地方。而平坦模型下,CS,DS,SS 这几个段的段基址为0,段界限为4G,就是整个地址空间,所以逻辑地址中的段内偏移的值就等于该逻辑地址转换成线性地址以后的值。

保护模式下的 x86 CPU 有4个特权级,从0到3。被叫做ring0,ring1,ring2,ring3。ring0权限最大,ring3权限最小。对于Win2k来说,只使用了ring0,ring3两个特权级。用户应用程序运行在ring3,系统内核运行在ring0。
    Win2k中进程的地址空间,被分为用户地址空间和系统地址空间。用户模式下的程序将不能访问系统地址空间,这就是通过保护机制实现的。页目录项和页表项的 bit2 ,Owner标志位表明了访问所需的特权级,为0表示访问需要ring0的特权级,为1表示ring3的特权级就可以访问。在发生内存访问时,CPU会把虚拟地址转换成物理地址。转换过程中,在从页目录项和页表项中获得物理地址的同时,会检查页目录项和页表项中的Owner标志位和当前的特权级,如果没有足够的特权级将会产生一个 page-fault 异常。注意权限的检测和地址的转换是同时由硬件进行的,不会为此损失额外的性能。当前特权级(CPL,Current privilege level),当前执行程序的特权级,由CS,SS段寄存器中的最低2位指明。

保护模式下的 x86 CPU 提供了很多的保护机制,这也就是为什么叫保护模式,更多内容可以参考 Intel手册。

AI 辅助代码审查:开启代码质量管控的智能时代 AI辅助代码审查正在改变传统的代码质量管控方式。通过自动化分析、智能反馈和上下文感知,AI不仅提高了代码审查的效率,还提升了代码质量的一致性和可维护性。随着机器学习和深度学习技术的不断发展,未来AI将成为开发者日常工作中强大的助手,进一步推动软件开发向智能化、自动化方向发展。 阅读详情

相关推荐

PT32F030与MT6826S SPI通讯实战:从硬件连接到数据交互全流程解析

本文详细解析了PT32F030与MT6826S通过SPI通讯的全流程,包括硬件连接、电路设计、SPI外设初始化配置以及数据交互协议实现。通过实战案例和代码示例,帮助开发者快速掌握PT32F030与MT6826S的SPI通讯技术,实现精准的角度和速度数据采集。

weixin_29189373的博客 420

(转载)Win2k内存篇 分页机制 (二)

JIURL玩玩Win2k内存篇 分页机制 (二)作者: JIURL                 主页: http://jiurl.yeah.net     日期: 2003-7-30 8种转换由于页表被映射到了0xc0000000 开始的4MB地址空间。所以我们也可以象CPU那样完成虚拟地址到物理地址的转换。系统按照对应虚拟空间的先后顺序,把一个进程的页表映射在0xc0000000 开始的4M

feijj2002_的专栏 1965

人工智能在高中教育中的应用现状剖析与挑战应对

人工智能在高中教育中的应用现状剖析与挑战应对

燕鹏 1338

x64异常处理

;-------------------------------------------------------------------------------- ;请访问64位汇编语言官方站 [url]Http://Www.X64Asm.Com[/url] ;电子邮件 [email]Jxc25@X64Asm.Com[/email] Tel:130******** ;------------

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 1898

Mac上VMware Fusion手动安装VMware Tools实现跨系统复制粘贴

本文详细指导了在Mac的VMware Fusion中手动安装VMware Tools的完整流程,以解决虚拟机与宿主机之间无法复制粘贴的常见问题。通过定位安装镜像、挂载、解压并运行安装脚本,最终实现跨系统无缝数据交换,提升工作效率。

orange的博客 698

如何利用三阈值游程理论识别干旱事件_python

如果当前时间点与上一个干旱事件的结束位置相隔不超过一个月(`index - current_event['end_index']

weixin_44259522的博客 1945

C语言之总线错误、段错误、页错误、malloc、static

当硬件告诉操作系统一个有问题的内存引用时,就会出现这两种错误。操作系统通过向出错的进程发送一个信号与之交流。进程收到 “bus error” 或 "segmentation fault"信号后将进行信息转储并终止。不过可以认为这些信号设置一个信号处理程序(signal handler),用于修改进程的缺省反应。 bus error (core dumped) 总线错误 CPU对进程引用内存的一些做法不满 事实上,总线错误几乎都是由于未对齐的读或写引起的。之所以称为总线错误,是因为出现未对齐的..

weixin_38360181的博客 2115

大疆航线文件解析指南:5分钟搞懂KMZ里的template.kml和waylines.wpml区别

本文深入解析大疆无人机航线文件KMZ中template.kml与waylines.wpml的核心区别。template.kml是定义任务框架和全局参数的可编辑模板,而waylines.wpml是包含精确执行指令的最终文件。理解两者差异对于高效规划航线、批量生成任务及进行二次开发至关重要。

sun99的博客 866

地址转译的相关问题(四)

各种页面错误及其处理 我们都知道,地址的转译需要如下的PTE 结构 第一篇文章我们看到了正常的地址转译过程v 位总是为1,即PTE 有效,但是,虚拟地址到物理地址的转译并不总是这样的正常流程。当产生页面错误异常(14号),异常的陷阱处理器(_KiTrap0E函数)首先构造一个陷阱帧,然后把控制权交给内存管理器的MmAccessFault 函数。如果MmAccessFault 函数能能够解决

商少 1103

TikTok滑块验证码verifyV2逆向实战:从fp生成到captchabody加密的完整流程解析

本文详细解析了TikTok滑块验证码verifyV2的逆向工程全流程,从fp参数生成到captchabody加密机制,涵盖了验证码触发、WASM解密、轨迹模拟等关键技术点。通过实战案例和代码示例,帮助开发者深入理解反爬机制,提升逆向工程能力,应对复杂的验证挑战。

weixin_28568819的博客 477

SST专题(2-4)MMC-SST 输入级的环流分析

本文分析了模块化多电平换流器(iMMC-SST)的输入级等效电路、子模块电容电压波动特性及环流抑制机理。研究表明:1)子模块电容电压波动包含基频和二倍频分量,与传统换流器相比波动幅值更大;2)环流主要由子模块电容电压波动在桥臂电感上激发的偶次谐波电流构成,呈频率越高幅值越小的特性;3)可通过增大桥臂电感或采用调制策略叠加抑制电压分量来有效控制环流。研究结果为MMC系统设计提供了理论依据。

嵌入式老牛的博客 585

windows内核需要注意的

修改windows内核函数 先屏蔽KdPrint 测试。 Hook函数一律使用全局变量 妹的。。KiTrap0E 修改。触发了已经断点。但是硬件断点Hook函数里只要使用KdPrint 就蓝屏 转载于:https://www.cnblogs.com/kuangke/p/5703011.html...

115

强化学习(Q-learning、DQN) —— 理论、案例与交互式 GUI 实现

本文详细介绍了强化学习中基于值函数的 Q-learning 算法及深度强化学习的 DQN 方法,从基本理论、数学模型到算法流程均进行了全面阐述。通过迷宫导航、股票交易策略和机器人控制等典型案例,展示了强化学习在处理高维、非线性和时变问题中的强大适应性。为了帮助工程师和研究者更直观地理解强化学习的训练过程,本文还提供了一份基于 Python 与 PyQt6 实现的交互式 GUI 演示系统代码示例,用户可以在线调整参数,实时观察累计奖励和 Q 值的变化,从而更深入地理解算法收敛和策略演化的过程。

闲人编程的博客 1475

JIURL玩玩Win2k内存篇 分页机制 (四)

JIURL玩玩Win2k内存篇 分页机制 (四)作者: JIURL                 主页: http://jiurl.yeah.net     日期: 2003-7-30 为什么地址空间的大小是4G     使用32bit的cpu话,处理32bit的数据比较好。这就决定了在代

jiurl的专栏 2850

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

JIURL玩玩Win2k内存篇 分页机制 (一)

JIURL玩玩Win2k内存篇 分页机制 (一)作者: JIURL                 主页: http://jiurl.yeah.net     日期: 2003-7-30 基本概念    Windows 2000 使用基于分页机制的虚拟内存。每个进程有4GB的虚拟地址空间

jiurl的专栏 3337

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

JIURL玩玩Win2k内存篇 分页机制 (二)

JIURL玩玩Win2k内存篇 分页机制 (二)作者: JIURL                 主页: http://jiurl.yeah.net     日期: 2003-7-30 进程的页目录和页表在虚拟地址空间中的位置     当执行一条访问内存的指令时,如果寄存器中的分页标志

jiurl的专栏 2825

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

(转载)Win2k内存篇 分页机制 (一)

JIURL玩玩Win2k内存篇 分页机制 (一)作者: JIURL                 主页: http://jiurl.yeah.net     日期: 2003-7-30 基本概念    Windows 2000 使用基于分页机制的虚拟内存。每个进程有4GB的虚拟地址空间。基于分页机制,这4GB地址空间的一些部分被映射了物理内存,一些部分映射硬盘上的交换文件,一些部分什么也没有映射

feijj2002_的专栏 1536

JIURL玩玩Win2k内存篇 分页机制

<br />基本概念<br />    Windows 2000 使用基于分页机制的虚拟内存。每个进程有4GB的虚拟地址空间。基于分页机制,这4GB地址空间的一些部分被映射了物理内存,一些部分映射硬盘上的交换文件,一些部分什么也没有映射。程序中使用的都是4GB地址空间中的虚拟地址。而访问物理内存,需要使用物理地址。<br /><br />下面我们看看什么是物理地址,什么是虚拟地址。<br /><br />物理地址 (physical address): 放在寻址总线上的地址。放在寻址总线上,如果是读,电路根

绝迹·危言的专栏 2188

JIURL玩玩Win2k内存篇 分页机制 (三)

JIURL玩玩Win2k内存篇 分页机制 (三)作者: JIURL                 主页: http://jiurl.yeah.net     日期: 2003-7-30 8种转换由于页表被映射到了0xc0000000 开始的4MB地址空间。所以我们也可以象CPU那样完成虚拟地址

jiurl的专栏 1999

JIURL玩玩Win2k内存篇 内存共享(二) CopyOnWrite

JIURL玩玩Win2k内存篇 内存共享(二) CopyOnWrite作者: JIURL                 主页: http://jiurl.yeah.net     日期: 2003-7-30 CopyOnWrite     对于同一个可执行文件运行的两个进程,或

jiurl的专栏 2109
上一篇: Windows 2000内存篇 分页机制介绍1-2
下一篇: JIURL玩玩Win2k进程线程篇 EPROCESS
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值