汇编基础
MS-Windows编程
Win32 控制台编程
链接库用了这么久了,汇编学了这么多了,HelloWorld还不会写。
本章介绍如何用32位Microsoft Windows API(application programming interface)进行控制台窗口编程。
虽然不建议用汇编语言进行扩展图形应用编程,但不影响我们从底层理解高级语言的GUI编程
背景知识
-
一个Windows应用程序开始的时候,要吗创建一个控制台窗口,要吗创建一个图形化窗口。这里我们一直使用
SUBSYSTEAM:CONSOLE,告诉链接器创建一个基于控制台的应用程序 -
控制台程序的外观和操作就像MS-DOS窗口一样,控制台有一个输入缓冲区以及一个或多个屏幕缓冲区:
- 输入缓存区(input buffer)包含一组输入记录(input records),其中每个记录都是一个输入事件的数据。输入事件的例子包括键盘输入,鼠标点击,以及用户调整控制台窗口大小
- 屏幕缓冲区(screen buffer)是字符与颜色数据的二维数组,他会影响控制台窗口文本的外观
-
调用Win32 API函数时会使用两类字符集:8位的ASCII/ANSI字符集和16位的Unicode字符集
-
控制台存在访问级别,用于在简单控制和完全控制直接进行权衡
Win32 API
ps:详细的内容请参考官网
以例子来简单了解如何使用
获取用户输入
1 | INCLUDE Irvine32.inc |
几乎所有的Win32控制台函数都需要句柄(handle),其是一个32位无符号整数,用于唯一标识一个对象,例如一个位图,画笔或任何输入输出设备
输入abcdefg结果如下,发现插还有0Dh,0Ah,这是用户按下Enter产生的行结束符
Dump of offset 003B6000
-------------------------------
61 62 63 64 65 66 67 0D 0A
输出
等待已久的Hello World程序
1 | INCLUDE Irvine32.inc |
动态内存分配
动态内存分配(dynamic memory allocation)又被称为堆分配(heap allocation)
C,C++,Java都有内置运行时堆管理器来处理程序请求的存储分配和释放。程序启动时,堆栈管理器常常从操作系统中分配一大块内存,并为存储块指针创建空闲列表(free list)。当接到一个分配请求时,堆管理器就把适当大小的内存标识为已预留,并返回指向该块的指针。
之后,当接收到对同一个快的删除请求时,对就会释放该内存块,并将其返回空闲列表。
例:使用动态内存分配创建并填充一个1000字节的数组
1 | INCLUDE Irvine32.inc |
下面是一项原型
1 | HeapAlloc PROTO, |
说明:hHeap通常由GetProcessHeap和HeapCreate初始化,dwFlags为标志值,常使用HEAP_ZERO_MEMORY将内存清0
X86 内存管理
我们重点关注的是存储管理的两个主要方面:
- 将逻辑地址转为线性地址
- 将线性地址转为物理地址(分页)
回顾
先回顾一下一些x86存储管理术语:
- 多任务处理(multitasking)允许多个程序或任务同时运行。处理器在所有运行程序中划分其时间
- 段(segment)是可变大小的内存区,用于让程序存放代码或数据
- 分段(segmentation)提供了分隔内存区段的方法。它允许多个程序同时运行又不会相互干扰
- 段描述符(segment descriptior)时一个64位的值,用于标识和描述一个内存段。它包含的信息有段基址,访问权限,段限长,类型和用法
- 段选择符(segment selector)是保存在段寄存器(CS,DS,SS,ES,FS或GS)中的一个16位数值
- 逻辑地址(logic address)就是段选择符加上一个32位的偏移量
我们一直忽略段寄存器,因为用户程序从来不会直接修改这些寄存器,所以只关注了32位数据偏移量,但是,从系统来看十分重要,因为它们包含了对内存段的直接引用

线性地址
线性地址是一个32位整数,其范围为0FFFFFFFFh,它表示一个内存位置。如果禁止分页功能,那么线性地址就是目标数据的物理地址
逻辑地址转为线性地址
多任务操作系统运行多个任务在内存中同时运行,每个程序都有自己的唯一数据区。那这样,每个程序里都有一个变量的偏移量地址为200h会咋样?怎么区分?
x86使用一步或两步处理将变量偏移量转换为唯一的内存地址
1.段值+变量偏移量形成线性地址(linear address)
2.页转换(page translation)
分页
分页是x86的一个重要功能。
处理器初始只装载部分程序到内存,其他仍留在硬盘里。程序使用的内存被分割成若干小区域,称为页(page),通常一页大小为4KB。当程序运行时,处理器会选择内存中不活跃的页面替换出去,而将立即会被请求的也加载到内存里
操作系统通过维护一个页目录(page directory)和一组页表(page table)来持续跟踪当前内存中所有程序使用过的页面。当程序试图访问线性地址空间内的一个地址时,处理器会自动将线性地址转为物理地址。这个过程称为页转换(page translation)。如果不存在内存中,处理器中断产生一个页故障(page fault)。操作系统将被请求的页从硬盘复制到内存,然后程序继续执行。从应用程序来看,页故障和页转换都是自动发生的