在信号处理程序中更改FPU硬件上下文

时间:2014-04-02 16:43:46

标签: c linux-kernel signals fpu x87

上下文

我尝试使用信号处理程序在正在运行的进程中更改FPU舍入模式行为。虽然我更喜欢便携式解决方案,但我优先考虑的平台是Linux / x86。

使用fesetround()似乎是一个很好的解决方案,除非它在信号处理程序返回后没有任何效果。据我了解,在每次上下文更改时,Linux内核都会保存当前正在运行的进程的硬件上下文,并恢复计划运行的进程的上下文。

虽然这种上下文处理通常发生在内核空间中,但我理解from various resources,在信号处理的情况下,此上下文存储在用户空间中并且可以被访问。我试图用它来修改FPU寄存器并设置所需的舍入模式。

尝试解决方案&问题

以下是我用来执行此操作的代码。在程序开始时调用init()来设置信号处理程序,并发送USR1信号以触发舍入模式切换。

所有这些工作只要在信号处理程序执行后正确返回fegetround()就调用FE_TOWARDZERO。但是,计算结果似乎并没有受到影响。

在执行信号处理程序之后调用fesetround( fegetround() )似乎会改变FPU配置并导致计算结果实际发生变化。所有这些似乎暗示了另一层间接,好像我正确地改变了硬件上下文,但它与实际的FPU状态并不一致。

问题

有人可以解释为什么这不起作用,可能会尝试解决问题吗?


#include <signal.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON

void changeRoundingMode (ucontext_t * ucontext)
{
  /* This doesn't seem to work since the hardware context is restored
     to its old value immediately after the signal handler return */
  fesetround (FE_TOWARDZERO);


  /* try and modify the context */
  {
    /* http://www.website.masmforum.com/tutorials/fptute/fpuchap1.htm#cword
       Rounding mode is encoded in bits 10 & 11 (RC = Rounding Control field) */
    __uint16_t cwd = ucontext->uc_mcontext.fpregs->cwd;

    /* Clear bits 10 & 11 */
    const __uint16_t clearRC = 0xf3ff;
    cwd &= clearRC;

    /* And set them to the desired value
         11 -> TOWARDZERO in this case */
    cwd += 0x0c00;

    ucontext->uc_mcontext.fpregs->cwd = cwd;
  }
}

void signalHandler (int signum, siginfo_t * siginfo, void * ucontext)
{
  changeRoundingMode (ucontext);
}

void init ()
{
  struct sigaction new_action, old_action;

  new_action.sa_sigaction = verrou_signalHandler;
  sigemptyset (&new_action.sa_mask);
  new_action.sa_flags = SA_RESTART | SA_SIGINFO;

  sigaction (SIGUSR1, 0, &old_action);
  if (old_action.sa_handler != SIG_IGN)
    sigaction (SIGUSR1, &new_action, 0);
}

0 个答案:

没有答案