上下文
我尝试使用信号处理程序在正在运行的进程中更改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);
}