MOVDQA与MOVDQU指令实验分析及技术疑问问询(x86/x64)
MOVDQA与MOVDQU指令特性探究及问题解答
参考链接
- MOVDQU指令与页边界问题
- MOVUPD vs MOVDQU(x86/x64汇编)
- MOVDQA与MOVAPS x86指令的区别
- 汇编"movdqa"访问违例
- x86指令集文档:MOVDQA条目
- x86指令集文档:MOVDQU条目
实验详情
以下实验基于64位Linux环境下的C++项目,探究MOVDQA与MOVDQU加载数据至XMM寄存器的特性:
初始化1
// Initialization 1 std::string_view lhs{"Once upon a time in Germany"}; // length = 27 std::string_view rhs = lhs.substr(20, 7); // RHS points to "germany"
实验1.1
# Experiment 1.1 # Here: %rax = lhs, %rsi = rhs movdqa (%rax), %xmm11 // SIGSEGV in this line, although it has enough memory allocations movdqa (%rsi), %xmm12
实验1.2
# Experiment 1.2 # Here: %rax = lhs, %rsi = rhs movdqu (%rax), %xmm11 // data successfully loaded into register movdqu (%rsi), %xmm12 // data successfully loaded into register with some garbage
初始化2
// Initialization 2 void *lhs = mmap ( NULL, 27*sizeof(unsigned char), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 ); void *rhs = lhs + 20;
实验2.1
# Experiment 2.1 # Here: %rax = lhs, %rsi = rhs movdqa (%rax), %xmm11 // data successfully loaded into register movdqa (%rsi), %xmm12 // SIGSEGV in this line... Expected!
实验2.2
# Experiment 2.2 # Here: %rax = lhs, %rsi = rhs movdqu (%rax), %xmm11 // data successfully loaded into register movdqu (%rsi), %xmm12 // data successfully loaded into register with some garbage
初始化3
// Initialization 3 unsigned char *lhs = new unsigned char[27]; unsigned char *rhs = lhs + 20;
实验3.1
# Experiment 3.1 # Here: %rax = lhs, %rsi = rhs movdqa (%rax), %xmm11 // data successfully loaded into register movdqa (%rsi), %xmm12 // SIGSEGV in this line
实验3.2
# Experiment 3.2 # Here: %rax = lhs, %rsi = rhs movdqu (%rax), %xmm11 // data successfully loaded into register movdqu (%rsi), %xmm12 // data successfully loaded into register with some garbage
问题解答
1. 实验1.1中MOVDQA触发SIGSEGV的原因?
std::string_view指向的字符串字面量存储在进程的只读数据段(.rodata)。MOVDQA执行16字节对齐加载时,会尝试读取从lhs起始的连续16字节内存——原字符串长度为27字节,lhs起始的16字节虽包含有效数据,但该字符串字面量的后续内存可能未被分配,或属于只读数据段的无权限区域(比如刚好在页边界末尾,下一页未映射)。当MOVDQA尝试访问这些非法内存时,就会触发SIGSEGV。而MOVDQU支持非对齐加载,即使跨页只要目标页存在就不会触发异常,因此能成功加载(末尾补垃圾数据)。
2. MOVDQU并非绝对安全,哪些场景下会触发SIGSEGV?
MOVDQU虽支持非对齐访问,但只要加载操作涉及的任何内存页不存在、无对应访问权限,或地址超出进程地址空间范围,就会触发SIGSEGV,包括:
- 加载的16字节数据中,部分字节位于未映射的内存页
- 目标内存页被标记为
PROT_NONE,或当前进程无读/写权限 - 访问的地址超出进程虚拟地址空间的上下限
3. MOVDQA触发SIGSEGV的场景有哪些?
MOVDQA要求内存地址必须16字节对齐,且加载的所有内存都合法,触发SIGSEGV的场景包括:
- 地址未16字节对齐:x86架构中,非对齐的MOVDQA访问会直接触发一般保护异常(#GP),内核转为SIGSEGV
- 访问的内存页不存在/无权限:即使地址对齐,但加载的16字节跨页时,其中某页未映射、无访问权限,会触发SIGSEGV
- 越界访问合法页的权限外区域:比如对齐地址的16字节范围中,部分字节属于只读页但程序尝试写入(MOVDQA为加载指令,主要是读权限问题),或超出进程分配的内存范围
4. MOVDQA与MOVDQU的性能对比参考资料?
- 官方优化手册:Intel® 64 and IA-32 Architectures Optimization Reference Manual、AMD Software Optimization Guide明确说明:地址对齐时,MOVDQA和MOVDQU性能几乎无差异;地址非对齐时,MOVDQU的性能略低于对齐的MOVDQA(老CPU差异更明显,Skylake及以后的新CPU中,非对齐访问的性能损耗已大幅降低)。
- 实际测试数据:循环加载对齐内存时,两者吞吐量一致;加载非对齐内存时,MOVDQU的延迟比MOVDQA高1-3个周期(取决于CPU微架构)。
内容的提问来源于stack exchange,提问作者RajibTheKing
相关产品推荐
相关产品推荐

