Chinaunix首页 | 论坛 | 博客
  • 博客访问: 370294
  • 博文数量: 64
  • 博客积分: 2975
  • 博客等级: 少校
  • 技术积分: 831
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-14 10:59
文章存档

2014年(2)

2012年(7)

2010年(40)

2009年(5)

2008年(8)

2007年(2)

分类: LINUX

2008-11-18 13:22:35

1.内核对进程的浮点数操作切换好保存机制
1.1 当新进程创建时,默认不使用浮点寄存器
int do_fork(unsigned long clone_flags, unsigned long stack_start,
     struct pt_regs *regs, unsigned long stack_size)
{
copy_flags(clone_flags, p);
}
static inline void copy_flags(unsigned long clone_flags, struct task_struct *p)
{
 unsigned long new_flags = p->flags;
 new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU | PF_VFORK);//<<<取消USEDFPU
 new_flags |= PF_FORKNOEXEC;
 if (!(clone_flags & CLONE_PTRACE))
  p->ptrace = 0;
 if (clone_flags & CLONE_VFORK)//<<<<<<<<<
  new_flags |= PF_VFORK;
 p->flags = new_flags;
}
1.2 当该进程被调度时,被禁止使用浮点指令
void __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
 unlazy_fpu(prev_p);
}
#define unlazy_fpu( tsk ) do { \
 if ( tsk->flags & PF_USEDFPU ) /*如果使用了浮点寄存器*/\
  save_init_fpu( tsk ); /*保存现场*/\
} while (0)
void save_init_fpu( struct task_struct *tsk )
{
 if ( HAVE_FXSR ) {
  asm volatile( "fxsave %0 ; fnclex"
         : "=m" (tsk->thread.i387.fxsave) );
 } else {
  asm volatile( "fnsave %0 ; fwait"
         : "=m" (tsk->thread.i387.fsave) );
 }
 tsk->flags &= ~PF_USEDFPU;//清除标志
 stts();//设置ts
}
#define stts() write_cr0(8 | read_cr0())//ts位=1,fpu不可用
TS CR0的位3是任务已切換(Task Switched)标志。该标志用於推迟保存任务切換时的辅助运算器內容,直到新任务开始实际执行
辅助运算器指令。处理器在每次任务切換时都会设置该标志,並且在执行辅助运算器指令时测试该标志。
如果设置了TS标志並且CR0的EM标志为0,那麼在执行任何辅助运算器指令之前会產生一个设备不存在DNA(Device Not Available
)異常。如果设置了TS标志但沒有设置CR0的MP和EM标志,那么在执行辅助运算器指令WAIT/FWAIT之前不会產生设备不存在 異常
。如果设置了EM标志,那麼TS标志对辅助运算器指令的执行无影响。见表4-1所示。
在任务切換时,处理器並不自动保存辅助运算器的上下文,而是会设置TS标志。这个标志会使得处理器在执行新任务指令流的任
何时候遇到一条辅助运算器指令时 產生设备不存在異常。设备不存在異常的处理程式可使用CLTS指令清除TS标志,並且保存辅助
运算器的上下文。如果任务从沒有使用过辅助运算器,那麼相应 辅助运算器上下文就不用保存。
1.4 idle和init进程的ts设置
asmlinkage void __init start_kernel(void)
{
trap_init()
}
void __init trap_init(void)
{
set_trap_gate(7,&device_not_available);
cpu_init();
}
void __init cpu_init (void)
{
 /*
  * Force FPU initialization:
  */
 current->flags &= ~PF_USEDFPU;
 current->used_math = 0;
 stts();
}
所以默认ts都是设置的
1.5用户进程如果执行浮点时的处理
用户进程执行浮点指令会触发DNA异常(device not available),异常号为7
linux/arch/i386/entry.S
ENTRY(device_not_available)//7
 pushl $-1  # mark this as an int
 SAVE_ALL
 GET_CURRENT(%ebx)
 pushl $ret_from_exception
 movl %cr0,%eax
 testl $0x4,%eax   # EM (math emulation bit)
 je SYMBOL_NAME(math_state_restore) #不模拟,协处理器存在,还原现场
 pushl $0  # temporary storage for ORIG_EIP
 call  SYMBOL_NAME(math_emulate) #软件模拟
 addl $4,%esp
 ret
asmlinkage void math_state_restore(struct pt_regs regs)
{
 __asm__ __volatile__("clts");  /* 允许浮点操作 Allow maths ops (or we recurse) */
 if (current->used_math) {//已使用浮点操作
  restore_fpu(current);//还原现场
 } else {
  init_fpu();//否则初始化
 }
 current->flags |= PF_USEDFPU; /* 正在使用浮点处理器 So we fnsave on switch_to() */
}
void restore_fpu( struct task_struct *tsk )
{
 if ( HAVE_FXSR ) {
  asm volatile( "fxrstor %0"
         : : "m" (tsk->thread.i387.fxsave) );
 } else {
  asm volatile( "frstor %0"
         : : "m" (tsk->thread.i387.fsave) );
 }
}
void init_fpu(void)
{
 __asm__("fninit");
 if ( HAVE_FXSR )
  load_mxcsr(0x1f80);
  
 current->used_math = 1;//是否使用了浮点处理器
}
则现在进程可以使用浮点处理器了
当换出时,系统会自动保存现场,并置ts,见1.2
2.内核中的浮点处理器使用
从上面看出,如果内核使用浮点指令,则也可能会触发dna或破环用户的浮点栈,对当前的用户进程造成不必要的干扰,因此
需要透明地使用浮点处理器。
首先要判断用户使用过处理器且现场已回复,如果是则需要保护,然后再使用。
unlazy_fpu(current);//可能保存用户现场
int old=current->used_math;
__asm__ __volatile__("clts");//内核使用浮点指令
init_fpu();
使用浮点指令
stts();//使用完浮点指令
current->used_math=old;//还原
还要保证执行浮点指令时内核不能被抢占和中断。//不可重入
阅读(2729) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~