WINDOWS核心编程
Unicode
Unicode宽字节字符集
- 简单一致的字符串表示方法,所有字符都是16位的(两个字节)
- 可以表示65000个字符,远超单字节字符集的256字符数目,能够对世界上书面上所有文字进行编码
为什么使用Unicode?
- 可以简化代码的转换工作,在不同的语言之间进行转换
- 能够分配支持所有语言的单个二进制.exe文件或者DLL文件
- 提高程序的运行效率
C运行期库对Unicode的支持
内核对象
内核对象例子
存取符号对象、事件对象、文件对象、文件映射对象、 I / O完成端口对象、作业对象、信箱对象、互斥对象、管道对象、
进程对象、信标对象、线程对象和等待计时器对象等
这些对象都是通过调用函数来创建的。
什么是内核对象
- 每个内核对象只是内核分配的一个内存块,并且只能由该内核访问
- 该内存块是一个数据结构,他的成员负责维护该对象的各种信息
- 有些数据成员在所有对象之中都是相同的,但是大多数数据成员属于特定的对象类型
应用程序如何访问内核对象
- 当调用一个用于创建内核对象的函数时,该函数就返回一个用于标识该对象的句柄。该句柄可以被视为一个不透明值
- 进程中的任何线程都可以使用这个值。将这个句柄传递给Windows的各个函数,这样,系统就能知道你想操作哪个内核对象
- 进程与句柄值是密切相关的。如果将该句柄值传递给另一个进程的线程,那么这另一个进程使用你的进程的句柄值所做的调用就会失败
- 内核对象的使用计数
- 内核对象由内核拥有,不是进程拥有
- 内核对象的存在时间可以比创建该对象的进程长
- 内核知道有多少进程正在使用某个对象,因为每个对象包含一个使用计数
- 内核对象的使用计数变为0时,内核就撤销该对象(类似与shared_ptr)
跨越进程边界访问内核对象
需要共享的原因
- 文件映射对象使你能够在一台机器上运行的两个进程之间共享数据块
- 邮箱和指定的管道使得应用程序可以在不同的机器上运行的进程之间发送数据
- 互斥对象 信标 事件使得不同进程之间的线程能同步运行
对象句柄的继承性 方法一
- 只有当进程具有父子关系时,才能使用对象句柄的继承性。
- 子进程就可以继承父进程的可继承句柄值,在父进程与子进程中,标识内核对象所用的句柄 值是相同的
- 系统还要递增内核对象的使用计数,因为现在两个进程都使用该对象
- 对象句柄的继承性只有在生成子进程的时候才能使用,已经在运行的子进程将无法继承这些新句柄
命名对象 方法二
- 共享跨越进程边界的内核对象的第二种方法是给对象命名。许多(虽然不是全部)内核对象都是可以命名的
- 糟糕的是,所有这些对象都共享单个名空间
- 使用命名对象进行共享跨越进程边界的内核对象时,两个进程中的 句柄值很可能是不同的值
复制对象句柄 方法三
- 该函数取出一个进程的句柄表中的项目,并将该项目拷贝到另一个进程的句柄表中
Windows内存管理
内存结构
进程的虚拟地址空间
- 32位->4GB 64位->16GB
- 进程之中线程运行时,该线程只可以访问只属于它的进程的内存
- 每个进程都拥有自己的私有地址空间
- 进程虚拟地址空间的区域划分
虚拟地址空间如何分区
- 虚拟地址的空间分区时根据不同的操作系统的基本实现方法来进行的
- 32 位Windows的虚拟地址空间大小为 4 GB (GB) ,分为两个分区:一个分区供进程使用,另一个用于系统使用
线程访问进程地址空间数据块
数据对齐的重要性
- 当数据大小的数据模数的内存地址是0时,数据时对齐的
- 当CPU试图读取的数据值没有正确对齐时, CPU可以执行 两种操作之一。
- 它可以产生一个异常条件,
- 可以执行多次对齐的内存访问,以便读取完整 的未对齐数据值
- 如果CPU执行多次内存访问,应用程序的运行速度就会放慢
- CPU访问对齐的数据时,他的运行效率是最高的
- 为了使应用程序获得最佳的运行性能,编写的代码必须使数据正确地对齐
- x86 CPU是如何进行数据对齐的。X86 CPU的EFLAGS寄存器 中包含一个特殊的位标志,称为 AC(对齐检查的英文缩写)标志。按照默认设置,当 CPU首 次加电时,该标志被设置为0。当该标志是0时, CPU能够自动执行它应该执行的操作,以便成 功地访问未对齐的数据值。然而,如果该标志被设置为 1,每当系统试图访问未对齐的数据时, CPU就会发出一个INT 17H中断
Windows操作系统下的虚拟地址到物理地址是如何进行映射
简单页表
- 页表(Page Table):想要把虚拟内存地址,映射到物理内存地址,最直观的办法,就是来建一张映射表。这个映射表,能够实现虚拟内存里面的页,到物理内存里面的页的映射。这个映射表,在计算机里面,就叫作页表。
- 页表地址转换,把一个内存地址分成页号(Directory) 和偏移量(Offset) 两个部分。以一个 32 位的内存地址,页的大小 4KB 为例,内存地址的 20 位的高位表示页号,12 位(212 = 4KB)的低位表示偏移量。
- 总结
- 把虚拟内存地址,切分成页号和偏移量的组合;
- 从页表里面,查询出虚拟页号,对应的物理页号;
- 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址。
加速地址转换:TLB(快表)
为什么可使用快表?
程序所需要使用的指令,都顺序存放在虚拟内存里面。我们执行的指令,也是一条条顺序执行下去的。也就是说,对于指令地址和需要访问的数据,都存在空间局部性和时间局部性。
TLB
地址变换高速缓冲(Translation-Lookaside Buffer,TLB):是 CPU 中的一块缓存芯片,这块缓存存放了之前已经进行过地址转换的查询结果。有了 TLB ,当同样的虚拟地址需要进行地址转换的时候,我们可以直接在 TLB 查询结果,而不需要多次访问内存来完成一次转换。
Windows实际的地址转换
Win32通过一个两层的表结构来实现地址映射
- 第一层称为页目录,实际就是一个内存页,Win32的内存页有4KB大小,这个内存页以4个字节分为1024项,每一项称为“页目录项”(PDE)
- 第二层称为页表,这一层共有1024个页表,页表结构与页目录相似,每个页表也都是一个内存页,这个内存页以4KB的大小被分为1024项,页表的每一项被称为页表项(PTE),易知共有1024×1024个页表项。每一个页表项对应一个物理内存中的某一个“内存页”,即共有1024×1024个物理内存页,每个物理内存页为4KB,这样就可以索引到4G大小的虚拟物理内存。
优质文章
存储器 - 内存:程序的虚拟内存是如何映射到物理内存 - binarylei - 博客园 (cnblogs.com)
(15条消息) Windows下虚拟地址到物理地址的映射——Windows内存管理_王大碗Dw的博客-CSDN博客_windows地址映射