系统时间变化导致sem_timedwait函数执行异常的一种解决方法

1. 问题简介

4G或5G通信模块通常是以AP+CP方式组合,AP侧运行linux系统,CP侧运行RTOS系统。

一般的Linux系统系统时间可以从硬件RTC获取。RTC可以由主板电池供电,独立于系统运行。当系统关机,断电时依然维护时间。但对于有些嵌入式linxu系统,没有使用硬件RTC,系统时间通常需要linux系统完全跑起来后通过网络方式或其他方式获取。

对于4G或5G通信模块,通常CP侧从网络中获取时间,AP侧再从CP侧同步时间。同步时间的时候,linux系统时间会发生跳变,这会导致某些使用绝对时间做判断的应用存在出现异常的可能。

2. 使用sem_timedwait函数带来的问题

当前很多模组厂商在sdx24/sdx55平台都使用了自研的oem-app应用程序,该程序在模块启动的时候就开始运行,并且在启动运行的过程中存在从CP同步系统时间的操作。oem-app程序多个地方使用sem_timedwait函数进行信号量超时等待,如果在调用sem_timedwait的时候发生了系统时间变化,会导致sem_timedwait提前超时退出,产生非预期的结果,通常会导致程序运行逻辑出现异常。

举oem-app中一个使用sem_timedwait函数的例子。

oem-app中wds模块负责拨号,使用了一个信号量wds_al_init_semaphore,在wds模块完成初始化后post该信号量,以此通知其他模块wds模块已完成初始化。处理AT+GTRNDIS拨号的线程会调用sem_timedwait(&wds_al_init_semaphore, &timeOut);函数等待wds模块完成初始化。如果在这个等待过程中发送系统时间变化,sem_timedwait会提前退出,从而wds模块还没有完成初始化的时候就进行拨号操作,这会导致拨号看起来成功了,实际业务还不通。因此需要尽量避免使用sem_timedwait进行超时等待操作。

3. 问题解决

使用pthread_cond_wait函数可以解决系统时间变化提前超时退出的问题。pthread_cond_timedwait函数可以配置成按相对时间等待超时。
需要配置条件变量的属性,具体如下:
pthread_condattr_init(&oem_sem->sem_cond_attr);
pthread_condattr_setclock(&oem_sem->sem_cond_attr, CLOCK_MONOTONIC);
pthread_cond_init(&oem_sem->sem_cond, &oem_sem->sem_cond_attr);

3.1 自定义sem类型:oem_cmn_sem_t

针对此问题,增加自定义sem类型oem_cmn_sem_t,提供信号量操作的函数,使用相对时间等待。

typedef struct oem_cmn_sem_struct {
pthread_mutex_t sem_mutex;
pthread_cond_t sem_cond;
pthread_condattr_t sem_cond_attr;
int sem_cond_value;
} oem_cmn_sem_t;

提供如下函数进行信号量操作:
extern int oem_cmn_sem_init(oem_cmn_sem_t * oem_sem);
extern int oem_cmn_sem_post(oem_cmn_sem_t * oem_sem);
extern int oem_cmn_sem_timedwait(oem_cmn_sem_t * oem_sem, const oem_clk_Time_t * wait_time);
extern int oem_cmn_sem_reset_value(oem_cmn_sem_t * oem_sem);

3.2 oem_cmn_sem_t操作函数实现

int oem_cmn_sem_init(oem_cmn_sem_t * oem_sem)
{
    if (!oem_sem) return ERR_OEM_SEM_FAIL;
    
    pthread_mutex_init(&oem_sem->sem_mutex, NULL);
    pthread_condattr_init(&oem_sem->sem_cond_attr);
    pthread_condattr_setclock(&oem_sem->sem_cond_attr, CLOCK_MONOTONIC);
    pthread_cond_init(&oem_sem->sem_cond, &oem_sem->sem_cond_attr);

    oem_sem->sem_cond_value = 0;

    return ERR_OEM_SEM_OK;
}

int oem_cmn_sem_post(oem_cmn_sem_t * oem_sem)
{
    if (!oem_sem) return ERR_OEM_SEM_FAIL;

    pthread_mutex_lock(&oem_sem->sem_mutex);
    
    oem_sem->sem_cond_value++;
    pthread_cond_signal(&oem_sem->sem_cond);
    
    pthread_mutex_unlock(&oem_sem->sem_mutex);
}

int oem_cmn_sem_timedwait(oem_cmn_sem_t * oem_sem, const oem_clk_Time_t * wait_time)
{
    int result  = ERR_OEM_SEM_OK;
    if (!oem_sem) return ERR_OEM_SEM_FAIL;

    pthread_mutex_lock(&oem_sem->sem_mutex);

    if (oem_sem->sem_cond_value <= 0)
    {
        if ((NULL == wait_time)
            || (wait_time->sec == 0 && wait_time->usec == 0))
        {
            pthread_cond_wait(&oem_sem->sem_cond, &oem_sem->sem_mutex);
        }
        else
        {
            struct timespec timeOut;
            clock_gettime(CLOCK_MONOTONIC, &timeOut);
            timeOut.tv_sec += wait_time->sec;
            timeOut.tv_nsec += (wait_time->usec * 1000);
            if (timeOut.tv_nsec >= 1000000000) 
            {
                timeOut.tv_nsec -= 1000000000;
                timeOut.tv_sec += 1;
            }
             
            result = pthread_cond_timedwait(&oem_sem->sem_cond, &oem_sem->sem_mutex, &timeOut);
            if ( result == ETIMEDOUT ) 
            {
                 OEM_LOG_INFO(OEM_OEM_LOG_MEDIAM, "Timed out waiting wds al thread init!\n");
                 result = ERR_OEM_SEM_TIMEOUT;
            } 


            else
            {
                result = ERR_OEM_SEM_OK;
            }
        }
        
    }
    
    pthread_mutex_unlock(&oem_sem->sem_mutex);

    return result;
}

int oem_cmn_sem_reset_value(oem_cmn_sem_t * oem_sem)
{
    if (!oem_sem) return ERR_OEM_SEM_FAIL;

    pthread_mutex_lock(&oem_sem->sem_mutex);
    oem_sem->sem_cond_value = 0;
    pthread_mutex_unlock(&oem_sem->sem_mutex);

    return ERR_OEM_SEM_OK;
}

4. 参考文档

https://blog.csdn.net/yichigo/article/details/23459613

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

四儿家的小祖宗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值