小议Windows NT/2000分页机制(http://webcrazy.yeah.net)

探究Windows 2000/XP原型PTE(http://webcrazy.yeah.net) 探究Windows 2000/XP原型PTE              WebCrazy(http://webcrazy.yeah.net)    内存管理可以说是操作系统实现中最重要的环节,也是最为复杂的一环节。对于相对贫乏的内存资源,内存共享也成了一个很重要的有效手段。Windows 2000/XP在此方面的实现借助于一个称为原型PTE(Prototype PTE,PPTE)的软件机制。在《小 阅读详情
 小议Windows NT/2000分页机制
                     WebCrazy( tsu00@263.net)

    内存管理是操作系统最重要的一部分,其决定了操作系统的性能。Intel X86采用分段、分页的内存机制,Windows NT/2000充分利用了此项先进的机制。段内IA-32体系使用页目录(Page Directory)与页表(Page Table,SoftICE中Page命令可以显示出页目录与页表等)形成对4G地址的寻址能力。文中未特别说明,均讨论运行平台为Intel 32位处理器的Windows NT/2000,所提及的Windows也仅指Windows NT/2000。Windows中不管用户态与核心态的代码段、数据段与堆栈段基址均为0,文中提到的逻辑地址(由段基址与偏移量两部分组成)与线性地址值是相等的。由于对于用户来说线性地址是其可见的,若我未特别指出物理地址,所说的地址也仅指线性地址。

    页目录(PDE)由1024项组成,每项均指向一页表(PTE),每一页表也由1024个页组成,IA-32体系每页大小为4K,所以可寻址范围为4G(1024*1024*4K)。Windows中每个进程都拥有其各自的进程地址空间,即拥有其各自的页目录与页表。每个进程均使用线性地址C0300000H指向其特定的页目录所在的地址,而页目录中每项(即页表)均依次排列在线性地址C0000000H处,每个页表均占用4K(1024*4)字节,如第一个页表位于C0000000H处,而第二个页表位于C0000000+1000H(4K),即C0001000中,依次类推,计算公式即为C0000000H+页目录偏移值(线性地址的高10位)*1000H,在下面我将利用此公式。当然以上描述的前提是每个页表均位于物理内存中(由页目录中每项中的P位指定),这也是为什么IA-32使用两级页表的原因,否则每个进程除其代码与数据等外还额外需要4M(4*1024*1024)的存储器。

    上面的机制即实现了物理地址寻址,也就实现了在Windows NT/2000中物理地址与线性地址的相互转换(虽然CPU在对内存操作时只需要线性地址转换成物理地址,但我们在分析程序代码等仍需要物理地址转换成线性地址)。照例先看看SoftICE的分析吧:

    // addr explorer命令后以下操作将只在进程explorer的私有进程空间进行
    :addr explorer

    // 显示explorer进程页目录所在物理地址,即进程切换至explorer后PDBR(CR3)中的值
    :addr
    CR3     LDT Base:Limit KPEB Addr PID  Name
            .
            .
            .
    00C10000               FF9FC920  036C explorer
            .
            .
            .

    /*

        线性地址的格式:0-11位对应1页(4096字节)的偏移量
        12-21位对当前页表中1024项中寻址
        22-31位对页目录进行寻址
        高20位(12-31位)又称为桢
        根据上面提及的公式,即可以得到物理地址高20位的值,再加上线性地址页表偏移(作为物理
        地址的低12位),即实现了线性地址转化为物理地址,用公式表示为:

         @(C0000000h+PDE*1000h+PTE*4)&0fffff000h+PO
        =@(C0000000h+4*(PDE*400h+PTE))&0fffff000h+PO
        =@(C0000000h+(PDE>>10d+PTE)<<2d)&0fffff000h+PO
        =@(C0000000h+(LA>>12d)<<2d)&0fffff000h+PO
        =@(C0000000h+(LA&0xFFFFF000)>>10d)&0fffff000h+PO

            上式中用PDE与PTE分别代表Page Directory与Page Table的偏移值,用LA代表给定的线性
        地址,用PO代表LA的低12位,用h与d分别代表16/10进制,@后表示取后地址指针中的内容。
        如此分析后,线性地址C0300000所对应的页目录为300h,页表为300h,偏移量为0
        则 C0000000h+PD*1000h+PT*4+PO=C0000000+300h*1000h+300h*4+0
    */

    :dd c0000000+300*1000+300*4 l 4
    0010:C0300C00 00C10063 01A31063 00000000 0141F163 c...c.......c.A.
                     |
                     |_低12位(0-11)063为属性位、Intel保留位与系统(OS)使用位

    // 显示C0300000的物理地址(00C10000)
    :? dword(@(c0000000+300*1000+300*4))&fffff000+C0300000&00000fff
    00C10000

    //用SoftICE验证
    :phys dword(@(c0000000+300*1000+300*4))&fffff000+C0300000&00000fff
    C0300000

    :page C0300000
    Linear Physical Attributes
    C0300000 00C10000 P D A S RW

    其实上面最后一条命令就可以实现所有其它指令的功能,下面我列出实现的代码段:

    // 线性地址->物理地址
    // SoftICE中Page命令可以实现此功能
    // 一个线性地址对应唯一的物理地址
    // 此函数若返回0说明此线性地址未对应物理地址

    ULONG LinearAddressToPhysicalAddress(ULONG lAddress)
    {
        unsigned int *pAddr;
        unsigned int *PageDirectoryEntry=(unsigned int *)0xC0300000;
        unsigned int *PageTableEntry=(unsigned int *)0xC0000000;

        //判断页目录是否有效,第0位(P)为存在位,请参阅相关书籍
        if((!(PageDirectoryEntry[lAddress>>22]&0xFFFFF000))
            &&(!(PageDirectoryEntry[lAddress>>22]&0x00000001)))
            return 0;

        //@(C0000000h+(LA&0xFFFFF000)>>10d)&0fffff000h+PO 见上叙述
        pAddr=(int *)((int)PageTableEntry+((lAddress&0xFFFFF000)>>10));
        if((*pAddr)&1)
            return ((*pAddr) &0xFFFFF000) |(lAddress&0x00000FFF);
        return 0;
    }

    那么又如何逆向实现物理地址转换成线性地址呢?虽然其间没有任何关系,但因为知道页目录与页表的具体位置,可使用直接在这个范围中搜索的方法。理论上这个范围最大为1024*1024*4(4M),但由于很多页目录项当前均不存在于物理内存中,所以实际上搜索范围小得多。这也导致一个问题,有可能导致蓝屏(内核态访问不存在的地址)。所以以下给出的代码我检查了页目录中每一项的P位。

    // 物理地址->线性地址
    // 相当于SoftICE中Phys命令
    // 搜索所有有效的页表寻找指定物理地址
    // 有可能多个线性地址同时指向同一个物理地址
    // 此函数若未输出任何结果表明当前还没有线性地址映射至此物理地址中

    void PhysicalAddressToLinearAddress(ULONG pAddress)
    {
        unsigned int *pAddr;
        unsigned int *PageDirectoryEntry=(unsigned int *)0xC0300000;
        unsigned int *PageTableEntry=(unsigned int *)0xC0000000;
        int i,j;
        DbgPrint("/n");
        for(i=0;i<1024;i++)
            if((PageDirectoryEntry[i]&0xFFFFF000)&&(PageDirectoryEntry[i]&0x00000001))
                for(j=0;j<1024;j++){
                    pAddr=(int *)((int)PageTableEntry+i*4096+j*4);
                    if((*pAddr)&0x00000001)
                        if(((*pAddr)&0xFFFFF000)==(pAddress&0xFFFFF000))
                            DbgPrint("%08X/n",
                                ((i*4*1024*1024+j*4*1024)&0xFFFFF000)|(pAddress&0x00000FFF));
                }
    }

    上面两个程序段涉及到2G-4G范围(线性地址C0000000以上)的内存访问,普通用户态的程序无法实现。在Windows中请使用设备驱动程序以使其在核心态正确执行。

    那么Windows又是如何利用分页机制以高效、合理的利用好有限的物理内存呢?Jeffrey Richter的经典著作<<Programming Applications for Microsoft Windows,Fourth Edition>>全面阐述了Windows的内存管理机制,相关原理请具体参阅此书!下面我列出同一程序(mspaint.exe)的两个运行实例的线性地址与物理地址的对应关系,以说明Windows的内存分页机制。

    // 下面是mspaint.exe装入内存后各段的地址(两个同时运行的mspaint.exe进程的映射地址一致)
    :map32 mspaint
    Owner Obj Name Obj# Address Size Type
    mspaint .text 0001 001B:01001000 0003A500 CODE RO
    mspaint .data 0002 0023:0103C000 00002670 IDATA RW
    mspaint .rsrc 0003 0023:0103F000 000116C8 IDATA RO
                       -------------
                            |
                            |_逻辑地址

    // mspaint.exe第一个运行实例的线性地址与物理地址的对应关系

    Linear Address Range Physical Address Range Attributes
    -------------------- ---------------------- ----------
    00010000 - 00010FFF  03A8B000 - 03A8BFFF    047
    00020000 - 00020FFF  03BCC000 - 03BCCFFF    047
    0006D000 - 0006DFFF  018BC000 - 018BCFFF    047
             .                   .              .
             .                   .              .
             .                   .              .
    //mspaint.exe第一个实例的.text段
    01001000 - 01001FFF  00596000 - 00596FFF    005
    01002000 - 01002FFF  03F97000 - 03F97FFF    005
    01003000 - 01003FFF  03D58000 - 03D58FFF    005
             .                   .              .
             .                   .              .
             .                   .              .
    //mspaint.exe第一个实例的.data段
    0103C000 - 0103CFFF  0225F000 - 0225FFFF    047
    0103D000 - 0103DFFF  03620000 - 03620FFF    047
    0103E000 - 0103EFFF  03C1E000 - 03C1EFFF    047
             .                   .              .
             .                   .              .
             .                   .              .
    //mspaint.exe第一个实例的.rsrc段
    0103F000 - 0103FFFF  01652000 - 01652FFF    025
    01040000 - 01040FFF  02653000 - 02653FFF    005
    01041000 - 01041FFF  003D4000 - 003D4FFF    005
             .                   .              .
             .                   .              .
             .                   .              .
    //mspaint.exe第一个实例的页目录的表
    C0300000 - C0300FFF  030FD000 - 030FDFFF    063
    C0301000 - C0301FFF  017FE000 - 017FEFFF    063
    C0303000 - C0303FFF  0141F000 - 0141FFFF    163
             .                   .              .
             .                   .              .
             .                   .              .
    FFD0F000 - FFD0FFFF  000FF000 - 000FFFFF    023
    FFDF0000 - FFDF0FFF  0026A000 - 0026AFFF    163
    FFDFF000 - FFDFFFFF  00269000 - 00269FFF    163

    // mspaint.exe第二个运行实例的线性地址与物理地址的对应关系

    Linear Address Range Physical Address Range Attributes
    -------------------- ---------------------- ----------
    00010000 - 00010FFF  03A6A000 - 03A6AFFF    047
    00020000 - 00020FFF  0352B000 - 0352BFFF    067
    0006D000 - 0006DFFF  03413000 - 03413FFF    047
             .                   .              .
             .                   .              .
             .                   .              .
    //mspaint.exe第二个实例的.text段
    01001000 - 01001FFF  00596000 - 00596FFF    005
    01002000 - 01002FFF  03F97000 - 03F97FFF    005
    01003000 - 01003FFF  03D58000 - 03D58FFF    005
             .                   .              .
             .                   .              .
             .                   .              .
    //mspaint.exe第二个实例的.data段
    0103C000 - 0103CFFF  030DF000 - 030DFFFF    047
    0103D000 - 0103DFFF  009A0000 - 009A0FFF    047
    0103E000 - 0103EFFF  02089000 - 02089FFF    047
             .                   .              .
             .                   .              .
             .                   .              .
    //mspaint.exe第二个实例的.rsrc段
    0103F000 - 0103FFFF  01652000 - 01652FFF    005
    01040000 - 01040FFF  02653000 - 02653FFF    005
    01041000 - 01041FFF  003D4000 - 003D4FFF    005
             .                   .               .
             .                   .               .
             .                   .               .
    //mspaint.exe第二个实例的页目录的表
    C0300000 - C0300FFF  037C9000 - 037C9FFF    063
    C0301000 - C0301FFF  02F8A000 - 02F8AFFF    063
    C0303000 - C0303FFF  0141F000 - 0141FFFF    163
             .                   .              .
             .                   .              .
             .                   .              .
    FFD0F000 - FFD0FFFF  000FF000 - 000FFFFF    023
    FFDF0000 - FFDF0FFF  0026A000 - 0026AFFF    163
    FFDFF000 - FFDFFFFF  00269000 - 00269FFF    163

    上面列表直接从mspaint.exe的两个同时运行的实例的页目录与页表中取得。实际上你只要理解物理地址与线性地址的相互转换,稍微修改一下上面所给的两个代码段便能得到上面的信息(32位x86平台Windows 2000 Server Build 2195的某一时刻所取,我的机器物理内存为64M,即040000000H字节)。

    Windows中每个进程均有其私有的线性地址,在线性地址的前2G(用户空间,Windows 2000 Server的ntoskrnl.exe的MmHighestUserAddress指出用户空间最大值7FFEFFFFH),上例中列出的两个实例同时运行的mspaint.exe的.text段与.rsrc段均指向同一物理地址空间,而.data段指向不同的物理空间。这是由于不同段的作用与性质决定,不难理解。在后2G(内核空间,Windows 2000 Server的ntoskrnl.exe的MmSystemRangeStart指出其线性地址的开始位置),大部分物理空间均由两个实例共享,实际上不同程序所有运行的例程均共享这2G,当然页目录(C0300000h)与页表(C0000000h)等其它比较特殊的操作除外,上例中基本可以看出这个规则。当然页目录与页表也有项目是指向同一物理区域,这样才能实现进程间共享物理内存,如上面mspaint.exe的两个实例的页目录中C0303000-C0303FFF所映射的线性地址00800000-00BFFFFF(4M)指向同一物理区域。

    这只是讨论通常情况下Windows如何在程序间高效的使用内存,实际上Windows赋予页很多机制,如Copy On Write等,使.text段等需要时也可以指向不同的物理地址,典型情况是使用用户态的调试器(Microsoft Visual C++所附的IDE调试环境等)对应用程序进行调试。当然Windows也提供让数据共享同一物理内存区域的方法,即使用Microsoft连接器(link)的section开关,赋予特定的段共享(S)属性。

    在<< 浅析Windows NT/2000环境切换>>( Nsfocus Magazine 12)中我曾详细的介绍了Windows NT/2000环境切换后页目录基址(CR3)切换代码,那么Windows NT/2000如何为应用程序从头分配页目录与页表呢?因为只有在建立新进程时才牵涉到页目录与页表的分配,所以还是让我们看看Kernel32.dll中的CreateProcessW代码吧(CreateProcessA间接调用CreateProcessW).

    可以简单的将流程用代码如下显示:

    KERNEL32!CreateProcessW
    .
    .(一些错误例程如进程文件是否存在,内核对象安全性检查等过程)
    .
    ;打开文件
    001B:77E7DDD2 CALL [ntdll!NtOpenFile]
            .
            .(主要是一些参数的压栈代码)
            .

    ;为可执行文件分配虚拟地址
    001B:77E7DE0A CALL [ntdll!NtCreateSection]
            .
            .
            .
    ;关闭文件
    001B:77E7DE1E CALL [ntdll!NtClose]
            .
            .
            .
    ;调用NtCreateProcess创建进程
    001B:77E7DF83 CALL [ntdll!NtCreateProcess]
            .
            .
            .

    其实上面ntdll.dll的四个过程在Windows 2000 Server Build 2195中分别是Service ID为64h、2bh、18h与29h的System Service。关于System Service可参阅<< 再谈Windows NT/2000内部数据结构>>( Nsfocus Magazine 11)。

    我们继续看看NtCreateProcess处理流程:

    :u ntdll!NtCreateProcess //用户态,就是常说的Native API
    ntdll!NtCreateProcess
    001B:77F92D2C MOV EAX,00000029
    001B:77F92D31 LEA EDX,[ESP+04]
    001B:77F92D35 INT 2E //使用中断门进入核心态
    001B:77F92D37 RET 0020

    :ntcall
    Service table address: 804704D8 Number of services:000000F8
            .
            .
            .
    0029 0008:804AD948 params=08 ntoskrnl!SeUnlockSubjectContext+0514
                |
                |_ID为29h的System Service(NtCreateProcess)的入口地址
            .
            .
            .
    :u 8:804ad948
    0008:804AD948 55             PUSH EBP
    0008:804AD949 8BEC           MOV EBP,ESP
    0008:804AD94B 6AFF           PUSH FF
    0008:804AD94D 6890354080     PUSH 80403590
    0008:804AD952 682CCC4580     PUSH ntoskrnl!_except_handler3
    0008:804AD957 64A100000000   MOV EAX,FS:[00000000]
    0008:804AD95D 50             PUSH EAX
    0008:804AD95E 64892500000000 MOV FS:[00000000],ESP
                    .
                    .
                    .
    ;EBP-30中此时存放新建进程的KPEB,以下几句实现对KPEB后的0A2H*4(648)字节清零
    0008:804ADAF5 B9A2000000     MOV ECX,000000A2
    0008:804ADAFA 33C0           XOR EAX,EAX
    0008:804ADAFC 8B7DD0         MOV EDI,[EBP-30]
    0008:804ADAFF F3AB           REPZ STOSD
                    .
                    .
                    .
    0008:804AD5E7 55             PUSH EBP
    0008:804AD5E8 8BEC           MOV EBP,ESP
    ;KPEB与进程Context分别作为这个过程的第一与第四个参数传入(ebp+8与ebp+14h)
    0008:804AD5EA 8B4508         MOV EAX,[EBP+08]
    0008:804AD5ED 8D4808         LEA ECX,[EAX+08]
    0008:804AD5F0 C60003         MOV BYTE PTR [EAX],03
    0008:804AD5F3 89480C         MOV [EAX+0C],ECX
    0008:804AD5F6 C640021B       MOV BYTE PTR [EAX+02],1B
    0008:804AD5FA 8909           MOV [ECX],ECX
    0008:804AD5FC 8A4D0C         MOV CL,[EBP+0C]
    0008:804AD5FF 884862         MOV [EAX+62],CL
    0008:804AD602 8B4D10         MOV ECX,[EBP+10]
    0008:804AD605 89485C         MOV [EAX+5C],ECX
    0008:804AD608 8A4D18         MOV CL,[EBP+18]
    0008:804AD60B 884864         MOV [EAX+64],CL
    0008:804AD60E 8B4D14         MOV ECX,[EBP+14]
    0008:804AD611 8B11           MOV EDX,[ECX]
    ;EDX存放进程Context(即页目录的物理地址)
    ;至于进程Context的算法,由于不仅与ntoskrnl.exe中的几个变量有关,还与执行环境息息相关,感兴趣的自己用SoftICE跟跟
    ;将进程Context放入新建的KPEB中
    0008:804AD613 895018         MOV [EAX+18],EDX ;18H是进程Context相对KPEB的偏移
                    .
                    .
                    .
    ;以下实现将新建的进程的KPEB插入系统KPEB的双向链表
    0008:804ADD22 A184A14680     MOV EAX,[8046A184]

    /*
        8046A180用以下两条SoftICE命令输出结果就可以很容易理解
        :? (@8046a180)-a0
        FE4E1D60 4266532192 (-28435104) "﨨`"
        :? @PsInitialSystemProcess //显示System进程的KPEB
        FE4E1D60 4266532192 (-28435104) "﨨`"
        即实现将新KPEB链插入已有链表尾
        在插入KPEB后,系统就可以根据上面提供的页目录对进程进行调度(即新进程拥有新的私有的进程空间)
    */

    0008:804ADD27 8B4DD0                 MOV ECX,[EBP-30]
    0008:804ADD2A C781A000000080A14680   MOV DWORD PTR [ECX+000000A0],8046A180
    0008:804ADD34 8B4DD0                 MOV ECX,[EBP-30]
    0008:804ADD37 8981A4000000           MOV [ECX+000000A4],EAX
    0008:804ADD3D 8B4DD0                 MOV ECX,[EBP-30]
    0008:804ADD40 81C1A0000000           ADD ECX,000000A0 ;是不是可以找出链状结构相对KPEB的偏移呢?
    0008:804ADD46 8908                   MOV [EAX],ECX
    0008:804ADD48 8B45D0                 MOV EAX,[EBP-30]
    0008:804ADD4B 05A0000000             ADD EAX,000000A0
    0008:804ADD50 A384A14680             MOV [8046A184],EAX


    为了更直观,上面的代码我只是按照系统执行流程列出(相对于实际的磁盘存放顺序).其实系统在创建进程初,首先用ObCreateObject建立section内核对象(section对象并不分配物理内存,Windows 2000 DDK Documentation中有详细介绍),然后才有牵涉到页目录与页表,自己在分析代码时要特别注意,还有可以使用IDA对ntoskrnl.exe进程分析,毕竟IDA对代码流程提供的比较清晰。至于以上所提的进程Context,系统KPEB双向链表等请参阅<<浅析Windows NT/2000环境切换>>,在那我已经进行了比较详细的说明。

    在深入分析Windows的有关内存操作的API(如VirtualAllocEx,CreateFileMapping,HeapAlloc等等)后,还可以发现很多其它方面重要的信息。如跟踪ntoskrnl!NtCreateSection(Windows在装入可执行文件与CreateFileMapping等都最终调用此函数)可发现Copy On Write等机制是如何实现的等等.这些都留着你自己去找找了。Windows 2000支持多个页面文件(命名为Pagefile.sys),这牵涉到原型PTE(Sofice中用ProtoPTE表示),分页文件使用的PTE等等很多的机制,要分析之,还要对Windows 2000的分层驱动程序中的FSD有很好的理解.简单的说即文中只讨论到PTE中的P位为1的情况。好了,还是那句话,以上分析,如有错误,还望指点( tsu00@263.net)!

参考资料:
    1.Jeffrey Richter
        <<Programming Applications for Microsoft Windows,Fourth Edition>>
    2.Intel Corp<<Intel Architecture Software Developer's Manual,Volume 3>>
    3.<<Undocumented Windows NT>>附带源码
    4.Windows 2000 DDK Documentation
YOLOv8【第七章:损失函数篇·第1节】一文搞定,IoU系列损失函数! 🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。 阅读详情

相关推荐

ArcGIS拓扑检查实战:5分钟搞定面重叠与缝隙问题(附详细截图)

本文详细介绍了如何使用ArcGIS进行拓扑检查,快速解决面重叠与缝隙问题。通过搭建拓扑检查环境、配置拓扑规则、五步闪电修复法及批量处理技巧,帮助用户高效完成数据修正,提升GIS数据质量。特别适合地籍管理、规划红线等场景应用。

z4a5b6的博客 741

小议Windows NT/2000分页机制

内存管理是操作系统最重要的一部 分,其决定了操作系统的性能。Intel X86采用分段、分页的内存机制,Windows NT/2000充分利用了此项先进的机制。段内IA-32体系使用页目录(Page Directory)与页表(Page Table,SoftICE中Page命令可以显示出页目录与页表等)形成对4G地址的寻址能力。文中未特别说明,均讨论运行平台为Intel 32位处理器的Windows NT/2000,所提及的Windows也仅指Windows NT/2000。Window

Matrix_Designer的专栏 1131

RDKit实战:5分钟搞定药物分子相似性分析(附Python代码)

本文提供了一套基于RDKit的快速药物分子相似性分析实战指南。通过Python代码演示,从环境搭建、分子指纹(如Morgan指纹)计算到Tanimoto相似性排序与可视化验证,帮助研究者在5分钟内完成从海量化合物库中精准筛选潜在活性分子的全流程,提升药物发现效率。

weixin_29193359的博客 314

为啥页目录的地址为什么是C0300000H

1. Windows 把页目录表映射到0xC0000000到0xC03FFFFF 这4MB的地址空间中,对于这4M的地址空间也有一个页表与之对应。不妨设为Px。既然Windows 把全部页表都映射到上述的4M地址空间,那么页表Px自然也在其中。该页表对应的线性地址为0xC0000000H。利用公式PTE_Addr = (VirtualAddr >> 12) * 4 + 0xC0000000,将0x

babala512的专栏 847

同花顺 python量化交易_开启量化第一步!同花顺iFinD数据接口免费版简易操作教程...

来源:雪球App,作者: 同花顺-iFinD,(https://xueqiu.com/6706494343/141951051)金融市场波动频繁,投资往往会夹杂非理性的情绪。而量化交易,旨在以先进的数学模型替代人为的主观判断,利用计算机技术从庞大的历史数据中海选能带来超额收益的多种“大概率”事件以制定策略,辅助投资者进行理性投资。不过计算机分析存在一定的技术门槛,有没有简单易学的量化交易方式,能够...

weixin_35911805的博客 2万+

【ESP32设备通信】-两个ESP32间UART通信

在本文中,我们将使用 Arduino IDE 的 UART 硬件库在两个 ESP32 板之间执行 UART 或串行通信。 要使用 USB 端口调试和编程 ESP32,需要使用称为通用异步接收器/发送器 (UART) 通信的串行端口。 对于大多数传感器和系统,主要的通信方法被认为是 UART。 为了共享工作负载、信息和执行不同的任务,有时需要在两个 ESP32 开发板之间进行通信。

视觉与物联智能 2992

内存管理----验证试验

由于对内存管理认识较浅 故通过试验重新巩固 并且 验证一下知识 我们知道虚拟地址ADDR_TO_PDE(0xc0000000)可以获得本进程页目录表的虚拟地址位置。 而MMU是根据cr3寄存器中的值来确定物理位置的,先验证下两者是否完全一致 这里的实验地址就是0xc000-0000 接着验证下0xc0000000的pte,其虚拟地址应该为(ULONG)ADDR_TO_PTE(0xc0000000)...

For Geek 372

手把手教你学Simulink实例:基于Simulink的锂电池管理系统(BMS)仿真

是确保锂电池安全、高效运行的关键组件。BMS 主要功能包括监控电池的状态(如电压、电流、温度)、估算荷电状态(State of Charge, SOC)、健康状态(State of Health, SOH),以及实现电池均衡和保护机制等。Simulink 提供了一个强大的仿真环境,可以用来设计、分析和验证锂电池管理系统的控制策略。常见的均衡方法包括被动均衡(通过电阻放电)和主动均衡(通过开关电容或变压器)。通过本教程,我们详细介绍了如何使用 Simulink 进行锂电池管理系统的仿真实例。

MHD0815的博客 538

[转载]深入Windows NT/2000模块的组织 - 专题安全方向{ Security Research Papers } - 邪恶八进制信息安全团队技术讨论组 努力为祖国的信息安全撑起一片蓝天

导读: [转载]深入Windows NT/2000模块的组织 文章作者:WebCrazy信息来源:WebCrazy(http://www.geocities.jp/webcrazyjp/)深入Windows NT/2000模块的组织              WebCrazy(http://web

xu的blog 990

Linux Kernel入门到精通系列讲解 - 总目录

在学习Linux Kernel之前,需要先准备开发环境,方便后续进行开发。可以根据自身情况,选择性阅读该章节。学习该章节之前,你应该了解一下网络概念,明白为什么要讲这些,才能更有方向的去理解学习。Linux ARM平台开发系列讲解(设备树篇) 4.1.1 设备树的原理详解。本项目完全开源,仅供学习使用,如需其他用途,请与本人联系。),本章节你可以学习到。摄像头V4L2子系统、Linux kernel常用的子系统等,ARM架构、RISCV架构、Uboot等。

DSMGUOGUO的博客 1万+

JAVA:Spring Boot 集成 Atomikos 实现分布式事务

摘要: Atomikos是一款基于JTA标准的分布式事务协调器,通过两阶段提交(2PC)协议实现跨数据库/消息系统的事务一致性,适用于金融等高要求场景。本文以Spring Boot集成Atomikos为例,演示多数据源(订单库、库存库)的分布式事务管理。核心步骤包括:引入Atomikos依赖、配置XA数据源、设置两阶段提交的MyBatis多数据源,并通过Atomikos协调全局事务,确保跨库操作原子性。代码示例涵盖配置类、YAML定义及MySQL XA数据源初始化,为分布式系统提供强一致性解决方案。

木头 334

解析Windows 2000/XP进程工作集

<br /> 在《解析Windows  2000/XP物理内存管理》中我详细的介绍了页框数据库(Page Frame Database)的概念,提到在物理内存的组织与管理方面对于每个页面系统都在页框数据库中保存一个结构,用于跟踪页面状态等。但页框数据库并不能真正协调物理内存的使用。我们知道,Windows 是一个多任务的操作系统,而物理内存却是一个相对贫乏的资源,为避免某个进程(或是系统)耗尽这一资源,引入了工作集(WorkingSet)的概念。WorkingSet是内存管理一个相当重要的术语,在Win

Tommy(凌飞)的专栏 1894

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

深入Windows NT/2000模块的组织(http://webcrazy.yeah.net)

深入Windows NT/2000模块的组织                    WebCrazy(http://webcrazy.yeah.net/)   在《小议Windows NT/2000分页机制 》中我对x86平台Windows NT/2000的非分页内存内部机制有了较为详细的说明,从中也可以看出地址空间可以分为进程空间与系统空间,其中每个进程有各自的进程空间,而所有的进程则共享同一个

FreeXploiT 1468

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

浅谈Windows 2000/XP File Cache实现(http://webcrazy.yeah.net)

  浅谈Windows 2000/XP File Cache实现              WebCrazy(http://webcrazy.yeah.net)    声明:本文所叙内容为Windows NT/2000/XP的Cache管理内容,涉及内容均为Microsoft Undocumented内容。我在参考大量资料后分析而成,牵涉内容多多少少可能会有些许错误,甚至根本就是错误的,但本着学习

FreeXploiT 2380

IEM模型,地表粗糙度模拟

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

gloomy的《内存与进程管理器》

来源:https://www.xfocus.net/bbs/index.php?act=ST&f=2&t=58182内存与进程管理器==========================                                   But I fear tomorrow Ill be crying,                                   Y

Purpleendurer@优快云 2381

Gloomy对Windows内核的分析(内存与进程管理器)

内存与进程管理器==========================                                   But I fear tomorrow Ill be crying,                                   Yes I fear tomorrow Ill be crying.                      

峥嵘岁月 4173

NT内核的内存分配(PDE与PTE)

Nt Kernel Memory AllocationSome important Globals concerning memory.These globals are defined in ntos/mm/miglobal.c and setin ntos/mm/mminit.c and ntos/mm/i386/init386.c.

wztgex的专栏 2912

ida动态调试笔记

目标文件:阿里安全挑战赛的第二题 点击打开链接 使用环境:ida6.8点击打开链接,adt bundle点击打开链接 首先打开avd安卓模拟器,界面如下: 在dos下运行adb命令,查看当前连接的设备: adb devices得到结果如下: 上图显示了本机连接的安卓模拟设备。 然后用adb命令安装上面的阿里测试文件: adb install 阿里测试路径/文件名 出现上

woyaojinqu的专栏 1万+

【数据结构-5】单链表的实现

单链表,具体来说是单向的、不带头的、不循环的链表。链表由一个一个节点组成,每个节点分为数据域和指针域,数据域存放数据,指针域存放下一节点的地址。这样,就可以根据指针域中的地址找到下一个节点,节点就串起来了构成了单链表。

weixin_44218929的博客 176

C# 数据结构和特殊类型

.NET Core(现称.NET)提供丰富数据结构,优化性能与开发效率。日常开发首选List<T>和Dictionary<TKey,TValue>;高性能场景推荐Span<T>与Memory<T>减少内存分配;并发环境使用ConcurrentDictionary等线程安全集合;避免非泛型集合如ArrayList。

cccloveforever的博客 672

工厂设备接MES为什么慢:协议识别与点表配置自动化的技术拆解

工业现场设备接 MES 的周期主要消耗在协议识别与点表配置。本文拆解这条链路的四层结构,给出点表映射的数据模型、两种部署架构的对比,以及可直接落地的三项验收口径与校验代码。适合工控集成、MES 实施、设备联网方向的工程师。

emfuture的博客 374

list

的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。迭代到该位置,在这段位置上迭代需要线性的时间开销;最大的缺陷是不支持任意位置的随机访问,比如:要访问。还需要一些额外的空间,以保存每个节点的相关联信息。个元素,必须从已知的位置。

xie0510_的博客 271

向 excel、word、pdf 三种文档中占位符插入数据

【代码】向 excel、word、pdf 三种文档中占位符插入数据。

weixin_45934659的博客 52

C++ STL 反向迭代器:从源码框架到适配器实现

在 C++ STL 中,反向迭代器(reverse_iterator)是一个很典型的设计案例:它本身并不直接持有容器数据,而是通过封装一个正向迭代器,把「前进」变成「后退」,从而让同一个容器既能正序遍历,也能逆序遍历。本文从 SGI-STL 3.0 的源码框架出发,梳理反向迭代器的设计思路,再手写一个通用的迭代器适配器,并在vector和list中完成适配与测试。在 SGI-STL 3.0 中,反向迭代器的核心源码位于。反向迭代器是一个适配器,各个容器再基于它适配出自己的反向迭代器类型。下面截取list。

Godbro的博客 230

软件商店常见问题标准处理流程

从底层软件源来看,/etc/apt/sources.list配置错误、源地址失效、公网与内网离线源混用、ARM64与x86_64架构源串用,以及DNS解析异常、系统代理配置错误,都会导致商店无法获取应用数据;打开该应用,确认可正常运行;软件商店作为系统内置的图形化软件分发与安装入口(UKUI桌面配套组件,底层依赖APT软件源),承担着应用软件的浏览、搜索、下载、安装、升级与卸载等核心功能,是终端软件部署与管理的关键通道。覆盖软件商店启动、页面加载、应用下载、安装、卸载、搜索、自身更新等全部业务场景。

qq_39496637的博客 267
上一篇: 远程唤醒代码
下一篇: 浅析Windows NT/2000环境切换(http://webcrazy.yeah.net)
freexploit
博客等级 码龄22年 315粉丝 778原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值