一、压力测试基础概念

什么是嵌入式压力测试?

嵌入式压力测试,说白了就是让系统在极限条件下跑一跑,看看它会不会崩。

我刚开始做嵌入式开发时,总觉得这玩意儿是测试工程师的事。直到有一次,我负责的一个智能网关项目,在实验室里跑得好好的,一部署到客户现场就频繁死机。后来一查,原来是并发请求量上来后,内存泄漏了。嗯,从那以后,我再也不敢轻视压力测试了。

嵌入式压力测试,具体来说就是:

  • 给系统施加超出正常范围的负载——比如把CPU跑满、把内存占满、把外设带宽压到极限
  • 持续运行一段时间——不是跑几分钟,而是几小时甚至几天
  • 观察系统行为——看它会不会死机、会不会丢数据、会不会响应变慢

核心要点:压力测试不是功能测试。功能测试是问「系统能不能做这件事」,压力测试是问「系统在极限情况下还能不能好好做这件事」。

为什么需要压力测试?

你想想看,嵌入式系统和普通软件最大的区别是什么?

嵌入式系统通常运行在资源受限的硬件上。内存就那么几百KB,CPU主频也就几十到几百MHz。而且很多场景下,系统一旦部署,就很难再远程升级或者重启。比如车载系统、医疗设备、工业控制器——这些设备出了问题,可不是重启一下就能解决的。

我个人习惯把压力测试的必要性归纳为三点:

  1. 发现资源泄漏问题——内存泄漏、句柄泄漏、任务栈溢出,这些在正常运行时很难暴露,但压力测试下很快就会现原形。我在项目中遇到过最典型的一个案例:一个串口驱动,每次收发数据都申请一个缓冲区,但释放条件写错了。正常通信时看不出来,一旦数据量上来,内存就被吃光了。
  2. 验证系统稳定性——系统能不能连续运行7×24小时不重启?任务调度会不会在极端负载下出现优先级反转?中断响应时间会不会随着运行时间变长而恶化?这些问题只有压力测试能回答。
  3. 评估系统极限——系统最多能处理多少并发请求?最大吞吐量是多少?在什么条件下会触发看门狗复位?这些数据对产品设计和部署至关重要。

注意:千万不要在产品发布前才做压力测试。我曾经见过一个团队,开发阶段完全不考虑压力测试,结果在送检前一周才发现系统跑不过24小时。那场面,真是加班加到怀疑人生。

稳定性指标定义

做压力测试,不能光凭感觉说「系统挺稳的」。得有量化指标。我常用的稳定性指标有以下几个:

指标名称 定义 典型阈值
MTBF(平均无故障时间) 系统连续运行不发生故障的平均时间 ≥ 720小时(消费级)
≥ 8760小时(工业级)
CPU占用率 压力测试期间CPU的平均使用率 ≤ 80%(峰值不超过95%)
内存泄漏率 单位时间内内存增长量 ≤ 0.1%/小时
任务响应时间 从事件触发到任务开始处理的时间 ≤ 10ms(硬实时系统)
≤ 100ms(软实时系统)
看门狗复位次数 压力测试期间系统被看门狗复位的次数 0次

这里我特别想强调一下看门狗复位次数这个指标。很多工程师觉得看门狗是保命用的,复位几次无所谓。但你想过没有,每次复位意味着系统丢失了当前状态,如果是在处理关键数据时复位,数据就丢了。

我的经验:在压力测试中,如果看门狗复位次数超过0,一定要追查原因。不要简单地加大看门狗超时时间了事。我曾经在一个项目中,看门狗频繁复位,我以为是任务跑太久,就把超时时间从5秒改到30秒。结果呢?系统确实不复位了,但任务卡死的问题被掩盖了,最后在现场出了大问题。

还有一个指标我经常用,但很多文档里不会写——任务栈使用率。嵌入式系统里每个任务都有自己的栈空间。压力测试下,任务调用深度会达到最大,这时候栈使用率最能反映问题。我建议在压力测试期间,定期打印每个任务的栈剩余空间,如果某个任务的栈使用率超过80%,就要警惕了。

举个具体的例子,我在做RTOS系统压力测试时,会写一个简单的监控任务:

// 压力测试监控任务示例
void stress_monitor_task(void *param)
{
    while(1)
    {
        // 检查每个任务的栈使用情况
        for(int i = 0; i < NUM_TASKS; i++)
        {
            uint32_t free_stack = uxTaskGetStackHighWaterMark(task_handles[i]);
            uint32_t total_stack = configMINIMAL_STACK_SIZE;
            float usage = 100.0f - (free_stack * 100.0f / total_stack);
            
            if(usage > 80.0f)
            {
                // 记录告警日志
                log_warning("Task %s stack usage: %.1f%%", task_names[i], usage);
            }
        }
        
        // 每5秒检查一次
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

这段代码虽然简单,但在实际项目中帮我抓到了不少栈溢出的隐患。你想想看,如果某个任务的栈使用率在压力测试下达到95%,那正常运行时可能只有60%。一旦某个极端情况触发更深层的函数调用,栈就溢出了。这种bug最难查,因为它在现场复现概率极低。

好了,关于压力测试的基础概念就聊到这里。记住一句话:压力测试不是可选项,而是嵌入式系统开发的必修课。下一节我们会聊具体的压力测试工具和搭建方法,到时候我会分享一些我踩过的坑,保证让你少走弯路。