The monitor http://x86.renejeschke.de/html/file_module_x86_id_175.html instruction arms the address monitoring hardware using the address specified in RAX/EAX/AX
.Quote from Intel
The state of the monitor is used by the instruction mwait
.
使用的有效地址大小(16、32 或 64 位)取决于编码指令的有效地址大小(即可以用67h
前缀,默认情况下它与代码大小相同)。
中给出的地址rax/eax/ax
是逻辑地址的偏移部分,用于计算用于装备监视器的线性地址。
段部分是ds
默认情况下,可以应用段覆盖前缀来更改段。
作为监视器使用的线性地址,分页不会影响监视。
The availability of the monitor
(and mwait
) instruction is indicated by the bit CPUID.01H:ECX.MONITOR[bit 3]1.
It is a privileged instruction but Intel claims:
这些指令在大于 0 的级别上有条件地可用。
检测这种情况的建议方法是try执行monitor
并处理最终的#UD异常(以操作系统将其报告给用户层程序的自定义方式)。
监控的地址范围must可回写缓存。
由于涉及缓存和缓存一致性子系统size地址范围以最小和最大大小给出。
CPUID.01H:EAX[位 15:0]给出最小范围大小。这是硬件监视器监视的区域的长度。
然而,缓存一致性流量可能适用于较大尺寸的“块”(行),并且如果后者包含在前者中,则与受监控区域相邻的写入仍然会触发它。
这产生了最大范围大小,可以在CPUID.01H:EBX[位 15:0].
要正确使用monitor
确保监视的数据结构适合最小范围大小,但也确保没有代理在其旁边的地址中写入最大范围大小。
例如,如果最小范围大小为 8 个字节,最大大小为 16 个字节,请确保监视的结构适合 8 个字节,但用另外 8 个字节填充它以达到总共 16 个字节,这样就不会从第 8 个字节开始写入到第 16 个字节发生。
在单集群系统中,以上两个值是相等的。我的都是64字节。
BIOS 负责报告高速缓存一致性线大小IA32_MONITOR_FILTER_LINE_SIZE
在多集群系统中。
为了指令排序和访问权的目的,monitor
是一个负载。
monitor
允许程序员指定hints and 扩展.
扩展名指定于ecx
当提示出现时edx
.
不支持的扩展会引发 #GP 异常,不支持的提示将被忽略。
我不知道有任何扩展或提示monitor
,英特尔手册报告
对于奔腾 4
处理器(系列 15,型号 3),未定义扩展或提示。
我相信这条线总体来说是正确的,只是其中有一个过时的处理器型号。
此外,伪代码为monitor
报告#GPIf ECX ≠ 0.
装备显示器而不检查其状态(使用mwait
)不会造成任何伤害。
其内在本质是void _mm_monitor(void const *p, unsigned extensions,unsigned hints)
.
一旦监视器布防,它可以由不同的条件触发:
- 外部中断:NMI、SMM、INIT、BINIT、MCERR
- 故障、中止,包括机器检查
- 架构 TLB 失效,包括对 CR0、CR3、CR4 的写入和某些 MSR 写入
- 由于快速系统调用和远程调用而导致的自愿转换
- 屏蔽中断(如果启用)
- 监视地址范围内的写入
监视器的状态对程序员不可见,但可以使用以下命令进行测试mwait
.
mwait
进入实现定义的低功耗状态,直到监视器处于触发状态。
如果监视器未进入布防状态或已被触发mwait
is a nop
否则它会使处理器停止执行指令,直到触发监视器为止。
mwait
也可以给出扩展 and hints.
扩展名设置在ecx
并提示eax
.
在撰写本文时,唯一的扩展是:
Bit 0即使被屏蔽(例如,即使 EFLAGS.IF=0),也将中断视为中断事件。仅当以下情况时才可设置CPUID.05H:ECX[位 1] = 1.
位 31-1预订的
这些提示让程序员可以指定实现定义的低功耗模式。
Bits 3:0C 状态内的子 C 状态,由位 [7:4] 指示
Bits 7:4目标C状态
值为0表示C1; 1表示C2等
01111B 的值表示 C0
注意:MWAIT 扩展的目标 C 状态是处理器特定的 C 状态,而不是 ACPI C 状态
C 模式的子状态数(因此是可用性)由下式给出:CPUID.05h.EDX:
位 03 - 00:使用 MWAIT 支持的 C0* 子 C 状态数。
位 07 - 04:使用 MWAIT 支持的 C1* 子 C 状态数。
位 11 - 08:使用 MWAIT 支持的 C2* 子 C 状态数。
位 15 - 12:使用 MWAIT 支持的 C3* 子 C 状态数。
位 19 - 16:使用 MWAIT 支持的 C4* 子 C 状态数。
位 23 - 20:使用 MWAIT 支持的 C5* 子 C 状态数。
位 27 - 24:使用 MWAIT 支持的 C6* 子 C 状态数。
位 31 - 28:使用 MWAIT 支持的 C7* 子 C 状态数。
请注意,将 CPU 置于高于 C1 的状态也会禁用其他线程,因此触发监视器的写入必须来自其他代理。
其内在本质是void _mm_mwait(unsigned extensions, unsigned hints)
.
The monitor
/mwait
引入机制是为了帮助线程之间的同步,但它不太适合监视对内存范围的访问,因为触发条件包括频繁发生的事件。
之后mwait
始终强制检查监视范围是否已写入。
有一个例子在这里 https://software.intel.com/en-us/articles/how-to-use-the-monitor-and-mwait-streaming-simd-extensions-3-instructions其中模式如下:
- 监视的结构使用特定值(例如 0)进行初始化。
- The
monitor
/mwait
使用对。
- 在稍后的某个时刻,另一个再次将特定值(例如 1)写入所监视的结构中。
- 监视器被触发并且
mwait
“返回”,将监视的结构值与 1(发生写入)进行比较,如果不相等,则执行跳回 2。
一些样本,untested伪代码可能是:
struct MonitoredType
{
int (*event)(struct MonitoredType const* m); /*Return 0 to keep monitoring*/
struct AnyType data; /*Less, in size, than MIN_MONITOR_RANGE*/
char padding[MAX_MONITOR_RANGE - sizeof(AnyType)];
};
void wait_for_write(struct MonitoredType const* m)
{
/* This may miss a write if it happens before MONITOR, beware of race conditions if necessary */
do
{
_mm_monitor(&m->data, 0, 0);
_mm_mwait(0, 0);
} while ( ! m->event(m));
}
必须注意确保退出条件mwait
是一次写入,而不是其他事件之一。
这就是函数指针的原因event
.
为了监视对线性地址的写入/读取,可以使用以下替代方法:调试寄存器.
参见第 17 章英特尔手册3 https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html并检查操作系统文档以正确使用这些寄存器。
1 Meaning: Execute cpuid
with eax
set to 01h and test the bit 3 of ecx
afterward. Note that IA32_MISC_ENABLE
allows the OS or the firmware to disable monitor/mwait
.