保存和加载FPU、MMX和XMM寄存器
从Intel 80486DX开始,FPU(算术浮点单元)被集成到了CPU中,浮点算术功能用ESCAPE指令来执行,操纵CPU中的浮点寄存器集。显然,当一个进程正在使用ESCAPE指令,那么浮点寄存器的内容就属于它的硬件上下文。
为了加速多媒体程序的执行,Intel在微处理器中引入了新的指令集——MMX,MMX指令也作用于FPU的浮点寄存器。这样,MMX就不能和FPU指令混用,但是OS内核就可以忽略新的MMX指令集,因为保存浮点寄存器的功能代码也能够应用于MMX的状态。
MMX使用SIMD(单指令多数据)流水线,Pentium III增强了这种SIMD能力,引入SSE(Streaming SIMD Extensions)扩展。该功能增强了8个128位寄存器(XMM寄存器)的功能,这些寄存器不和FPU/MMX寄存器重叠,因此能够与FPU/MMX指令混用。
Pentium IV还引入了SSE2扩展,支持高精度浮点值,SSE2和SSE使用同一个XMM寄存器组。
80x86微处理器不在TSS中保存FPU、MMX和XMM寄存器的值,不过还是提供了一些支持,能够在需要时保存它们。cr0寄存器有一个TS(Task-Switching)标志位,每当执行硬件上下文切换时,TS置位,每当TS被置位后进程执行ESCAPE、MMX、SSE或SSE2指令,控制器就产生一个“Device not available”异常。这样,TS标志位就能够让OS内核只有在真正需要时才保存或恢复FPU、MMX和XMM寄存器。
假设进程A使用了数学协处理器,那么当进程A被切换出去的时候,内核设置TS并将浮点寄存器的内容保存到进程A的TSS中(原著这么写,但是应该是保存到进程A描述符的一个字段中,TSS是与CPU关联的,进程没有TSS)。
如果新进程B不使用数学协处理器,那么内核就不需要恢复浮点寄存器的内容,但是,一旦进程B执行FPU、MMX等指令,CPU就产生一个“Device not available”异常,相应的异常处理程序就会用保存在进程B中的相关值来恢复浮点寄存器。
处理FPU、MMX和XMM寄存器的数据结构存放在进程描述符的thread字段的i387子字段中(即thread.i387),由i387_union联合体描述,其格式如下:
struct i387_fsave_struct fsave; /* 保存FPU、MMX寄存器的内容 */
struct i387_fxsave_struct fxsave;/* 保存SSE和SSE2寄存器内容 */
struct i387_soft_struct soft; /* 由无数学协处理器的老式CPU模型使用 */
};
此外,进程描述符中还包含了两个附加的标志:
- thread_info结构中status字段的TS_USEDFPU标志,表示进程当前执行过程中是否使用过FPU、MMX和XMM寄存器。
- task_struct结构的flags字段的PF_USED_MATH标志,表示thread.i387的内容是否有意义。
保存和加载FPU、MMX和XMM寄存器主要用到__unlazy_fpu宏,该宏在__switch_to函数中使用,下一篇会对其进行分析。
内核态使用FPU、MMX和XMM寄存器
OS内核也可以使用FPU、MMX和XMM寄存器,当然,这么做的时候应该避免干扰用户态进程。因此,Linux使用如下方法来解决:
- 在内核使用协处理器之前,如果用户态进程使用了FPU(TS_USEDFPU标志为1),内核就要调用kernel_fpu_begin()函数,该函数里又调用save_init_fpu()来保存寄存器内容,然后重新设置cr0寄存器的TS标志。
- 使用完协处理器之后,内核调用kernel_fpu_end宏设置cr0寄存器的TS标志。
- 当用户态进程恢复执行时,math_state_restore()函数将恢复FPU、MMX和XMM寄存器的内容。
需要注意的是,如果当前用户态进程有在用数学协处理器时,kernel_fpu_begin()函数的执行时间比较长,甚至无法通过FPU、MMX或XMM达到加速的目的。因此,内核只在有限的场合使用FPU、MMX或XMM指令,比如移动或清除大内存区字段、计算校验和等。
转自:http://www.cnblogs.com/wz19860913/archive/2010/05/25/1742583.html
sse memcpy in linux kernel:
The SYSTEM_RUNNING check is to take care of early boot situations where we can't handle FPU exceptions but we use memcpy. There's an aligned and misaligned variant which should handle any buffers and sizes although I've set the SSE memcpy threshold at 512 Bytes buffersize the least to cover context save/restore somewhat.来自:https://lkml.org/lkml/2011/8/14/19