我正在探索MONITOR
指令(或等效内在函数_mm_monitor
)的用法。虽然我找到了描述它们的文献,但我找不到任何关于如何使用它的具体例子/样本。
有人可以分享一个如何在驱动程序中使用此指令/内在函数的示例吗?基本上,我想用它来观察内存范围。
答案 0 :(得分:7)
monitor
指令使用RAX/EAX/AX
中指定的地址为地址监控硬件提供支持。来自英特尔的报价
监视器的状态由指令mwait
使用。
使用的有效地址大小(16,32或64位)取决于编码指令的有效地址大小(即它可以用67h
前缀覆盖,默认情况下它与代码大小)。
rax/eax/ax
中给出的地址是逻辑地址的偏移部分,用于计算监视器的线性地址。
默认情况下,细分部分为ds
,细分覆盖前缀可应用于更改细分
作为用于监视器的线性地址,分页不会影响监视。
{em> CPUID.01H:ECX.MONITOR [bit 3] 1位表示monitor
(和mwait
)指令的可用性。 SUP>。
这是特权指令,但英特尔声称:
指令有条件地以大于0的级别提供。
检测此类情况的建议方法是尝试执行monitor
并处理最终的#UD异常(以自定义方式将OS报告给用户程序)。
受监控的地址范围必须可回写缓存
由于涉及缓存和缓存一致性子系统,地址范围的 size 是根据最小和最大尺寸给出的。
CPUID.01H:EAX [bit 15:0] 给出最小范围大小。这是硬件监视器监视的区域的长度
但是,缓存一致性流量可能与" chunks"如果后者包含在前者中,则较大尺寸的(线)和与被监视区域相邻的写入将触发它。
这会产生最大范围大小,可以在 CPUID.01H:EBX [bit 15:0] 中找到。
要正确使用monitor
,请确保监视的数据结构符合最小范围大小,但也要确保没有代理在其旁边的地址中写入最大范围大小。
例如,如果最小范围大小为8个字节且最大大小为16个字节,请确保观察到的结构适合8个字节,但要用8个字节填充它以达到总共16个字节,这样就不会写入发生第8到第16个字节。
在单个群集系统中,上述两个值相等。我的都是64字节
BIOS负责报告多集群系统中IA32_MONITOR_FILTER_LINE_SIZE
的缓存一致性行大小。
出于指令排序和访问权限的目的,monitor
是一个负载。
monitor
允许程序员指定提示和扩展。
扩展名在ecx
中指定,而提示位于edx
不支持的扩展会引发#GP异常,忽略不支持的提示
我不知道monitor
的任何扩展或提示,英特尔手册报告
奔腾4 处理器(系列15,型号3),没有定义扩展或提示。
我认为这条线路一般都是正确的,它只有一个过时的处理器模型
此外,monitor
的伪代码报告#GP If 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
为nop
,否则会使处理器停止执行指令,直到监视器被触发为止。
mwait
也可以提供扩展程序和提示。
扩展程序在ecx
中设置,并在eax
中提示
在撰写本文时,唯一的扩展名是:
位0 即使屏蔽了中断,也将中断视为中断事件(例如,即使EFLAGS.IF = 0)。只有在设置时才可以设置 CPUID.05H:ECX [bit 1] = 1 比特31-1 保留
提示允许程序员指定实现定义的低功耗模式。
位3:0 C状态下的子C状态,由位[7:4]表示
比特7:4 目标C状态
值0表示C1; 1表示C2等等 值01111B表示C0
注意:MWAIT扩展的目标C状态是特定于处理器的C状态,而不是ACPI C状态
在 CPUID.05h.EDX 中给出了C模式的子状态数(因此也就是可用性):
位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)
。
引入monitor
/ mwait
机制来帮助线程之间的同步,它不适合监视对内存范围的访问,因为触发条件包括频繁发生的事件。
在mwait
之后始终必须检查是否写入了受监视的范围
有一个example here,其格式如下:
monitor
/ mwait
对。mwait
"返回",被监视的结构值被比较为1(发生写入),如果它不相等,则执行跳回到2. 一些示例未经测试的伪代码可能是:
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
的原因。
为了监控写入/读取到线性地址,另一种方法是使用调试寄存器。
请参阅Intel manual 3的第17章,并检查操作系统文档以正确使用这些寄存器。
1 含义:执行cpuid
并将eax
设置为01h,然后测试ecx
的第3位。请注意,IA32_MISC_ENABLE
允许操作系统或固件禁用monitor/mwait
。