You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

POSIX线程临界区信号未屏蔽/意外解除屏蔽问题排查

问题分析与修复方案

你遇到的核心问题是信号屏蔽逻辑存在两处关键错误,导致上下文切换的临界区被SIGALRM(模拟中断信号)打断,进而破坏了任务有效性校验的原子性,出现“数据合法但校验失败”的矛盾现象。

1. 信号掩码保存与恢复逻辑错误

你的uxPortSetInterruptMaskFromISR函数仅保存了一个标志位(原掩码是否包含目标信号),而非完整的原信号掩码,且恢复时无法准确还原线程原本的信号屏蔽状态。更严重的是,使用SIG_BLOCK叠加屏蔽集的方式可能导致预期外的信号未被屏蔽。

修复方案:精准屏蔽目标信号并完整恢复原状态

将信号屏蔽逻辑改为仅针对configPORT_SIGNAL_FOR_IRQ(即SIGALRM)进行操作,避免影响其他信号,同时准确保存和恢复原状态:

// 全局变量,用于保存原信号掩码中目标信号的状态
static portUnsignedBaseType uxOriginalMaskState = 0U;

portUnsignedBaseType uxPortSetInterruptMaskFromISR( void )
{
    sigset_t xTargetMask;
    sigset_t xCurrentMask;

    // 初始化仅包含目标信号的掩码
    sigemptyset(&xTargetMask);
    sigaddset(&xTargetMask, configPORT_SIGNAL_FOR_IRQ);

    // 获取当前线程的信号掩码
    pthread_sigmask(SIG_BLOCK, NULL, &xCurrentMask);

    // 记录原状态:是否已屏蔽目标信号
    uxOriginalMaskState = sigismember(&xCurrentMask, configPORT_SIGNAL_FOR_IRQ) ? 1U : 0U;

    // 仅当原状态未屏蔽时,才屏蔽目标信号
    if (uxOriginalMaskState == 0U)
    {
        pthread_sigmask(SIG_BLOCK, &xTargetMask, NULL);
    }

    return uxOriginalMaskState;
}

void vPortClearInterruptMaskFromISR( portUnsignedBaseType xInterruptsAlreadyMasked )
{
    sigset_t xTargetMask;
    sigemptyset(&xTargetMask);
    sigaddset(&xTargetMask, configPORT_SIGNAL_FOR_IRQ);

    // 仅当原状态未屏蔽时,才解除屏蔽目标信号
    if (xInterruptsAlreadyMasked == 0U)
    {
        pthread_sigmask(SIG_UNBLOCK, &xTargetMask, NULL);
    }
}

2. 信号处理程序的sa_mask设置不当

在vPortInitialiseINTC中,你将信号处理程序的sa_mask设为空集,这会导致信号处理程序运行时,其他信号(包括同类型的SIGALRM)可以打断它,进而可能触发嵌套的上下文切换,破坏临界区的原子性。

修复方案:在信号处理程序中屏蔽目标信号

修改vPortInitialiseINTC中的sa_mask配置,确保处理SIGALRM时不会被同类型信号打断:

void vPortInitialiseINTC( void )
{
struct sigaction xSigActIRQ, xTestSigActIRQ;

    if( iPortIsrSetupDefaultThreadSignalMask() != 0 )
    {
        portERRORHOOKSTR( NULL, "Failed to set up default signal mask", errINVALID_PARAMETERS );
    }

    xSigActIRQ.sa_flags = SA_SIGINFO;
    xSigActIRQ.sa_sigaction = vPortIsrIRQHandler;
    
    // 屏蔽目标信号,避免处理时被同类型信号打断
    sigemptyset( &xSigActIRQ.sa_mask );
    sigaddset( &xSigActIRQ.sa_mask, configPORT_SIGNAL_FOR_IRQ );

    if( sigaction( configPORT_SIGNAL_FOR_IRQ, &xSigActIRQ, NULL ) )
    {
        portERRORHOOKSTR( NULL, "Failed to set up simulated interrupt system", errINVALID_PARAMETERS );
    }
    
}

3. 可选优化:指定定时器信号的递送目标

默认情况下,timer_create创建的定时器信号会递送到进程内任意未屏蔽该信号的线程。如果你的程序存在多线程,可能导致信号被递送到非预期线程,间接影响临界区。可以指定信号仅递送到调度器线程:

portWEAK_FUNCTION void vApplicationSetupTickInterruptHook( portUInt32Type ulClockHz, portUInt32Type ulRateHz )
{
struct sigevent xTimerEv;
struct itimerspec xSysTickTimerSpec;
int iSysRet;
long lNanosecondsPerTick;

    ( void )ulClockHz; /* Unused in this port */

    portcompatASSERT_PRIVILEGED_MODE();

    lNanosecondsPerTick = ( long ) PORT_ONE_SECOND_IN_NS / ( long ) ulRateHz;

    vPortDisableInterrupts();

    xTimerEv.sigev_notify = SIGEV_SIGNAL;
    xTimerEv.sigev_signo = configPORT_SIGNAL_FOR_IRQ;
    xTimerEv.sigev_value.sival_int = configPORT_SYSTICK_INTERRUPT_NUM;
    xTimerEv.sigev_notify_function = NULL;

    // 指定信号递送到当前线程(调度器初始化线程)
    #ifdef _POSIX_THREAD_SIGNALS
    xTimerEv._sigev_un._tid = pthread_self();
    #endif

    iSysRet = timer_create( CLOCK_MONOTONIC, &xTimerEv, &xPortSysTickTimer );
    if(iSysRet)
    {
        portERRORHOOKSTR( NULL, "Failed to create tick timer", errINVALID_PARAMETERS );
    }

    xSysTickTimerSpec.it_interval.tv_sec = 0;
    xSysTickTimerSpec.it_interval.tv_nsec = lNanosecondsPerTick;
    xSysTickTimerSpec.it_value.tv_sec = 0;
    xSysTickTimerSpec.it_value.tv_nsec = lNanosecondsPerTick;

    iSysRet = timer_settime( xPortSysTickTimer, 0, &xSysTickTimerSpec, NULL );
    if(iSysRet)
    {
        portERRORHOOKSTR( NULL, "failed to set & start tick timer", errINVALID_PARAMETERS );
    }
}

验证方法

修复后,重新运行程序并在xPortIsTaskHandleValid的无限循环处设置断点,确认信号处理程序的断点不会触发,说明临界区的信号屏蔽已生效。同时检查任务有效性校验是否能正常通过。

内容的提问来源于stack exchange,提问作者jonny123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:00:54