背景:

           起因是我在freertos的一个任务中要利用uart串口发送数据给ESP8266wifi模块,但是每次到这个任务就卡死了,我起初怀疑是串口缓冲区满了等原因,是不是一次性发太多数据的原因?后面排除好像并不是,那卡死只可能是任务分配的栈大小爆了,导致了内存泄漏。

        这个是最初的代码,初始化上电后运行到这就卡死了。

void StartTask_Wifi(void *argument)
{
  /* USER CODE BEGIN StartTask_Wifi */
		 char cmd_buf[32] = {0};
		 char send_buf[50] = {0}; 
  /* Infinite loop */
  for(;;)
  {
		memset(cmd_buf, 0, sizeof(cmd_buf));
		memset(send_buf, 0, sizeof(send_buf));
		//从消息队列中取出数据结构体
		osMessageQueueGet(Queue_HumiTempHandle,dht11_data, 0, 100);
		
		sprintf(cmd_buf, "AT+CIPSEND=0,27\r\n");  // 发送指令头(指定连接ID和长度)
		if(ESP8266_SendATCmd((uint8_t*)cmd_buf, (uint8_t*)">", 2000) == 1) // 等待模块返回">"提示符
		{
				sprintf(send_buf, "Temp:%.2f,C;Humi:%.2f,%%\r\n", dht11_data->temperature, dht11_data->humidity);
				HAL_UART_Transmit_IT(&huart2, (uint8_t*)send_buf, strlen(send_buf));
		}
		
    osDelay(500);
  }
  /* USER CODE END StartTask_Wifi */
}

我设置的任务栈大小是128字节,我开的数组又有点随意大了,32+50=82字节,只剩下40多字节能用。FreeRTOS 任务栈的开销是 当前函数局部变量 + 调用的所有子函数的栈开销 ,我任务中还要进行中断发送等等开销超过了任务栈的大小。

        于是我想到了static关键字,static标识变量为静态变量,当修饰局部变量的时候,会把变量存储在全局 / 静态存储区,而不是栈空间中,因此,只需要加上static关键字就能够正常运行了。

        网上也有代码分区的介绍,我引用一张来回顾,在程序初始化的时候,局部变量会存储在栈空间中,这个空间一般又程序自动分配释放,堆区主要是用来存放用户手动分配的变量(例如进行了malloc,new等操作),static修饰局部变量后,变量会存储在全局静态区域,这是一个单独的空间,里面还分有bss段(未初始化或为0的变量)和data段(已经初始化的变量),代码区就是程序实际要执行的操作代码段。

        Static关键字在修饰局部变量的时候,主要的作用就是延长变量的生命周期,一般来说局部变量随着栈的销毁而消失,但是当变量在全局变量区的时候,该变量就会延长到程序结束的时候一起释放。

   

        此图引用@形而上下

  最后,我发现,如果我没有添加Static关键字,但是我有额外在任务栈中开辟了一空间,例如 char xx[50] 我发现程序竟然可以正常跑起来!难道不是任务栈溢出的原因吗?我问了AI后告诉我这依旧是栈的溢出,但是内存泄漏具有随机与不确定性,可能添加新的空间后发送内存泄漏的地方刚好越过了核心指针内存空间,因此在进行软件开发的时候,我们需要提前去预估程序的最大栈开销,来合理分配栈大小,再通过static等使用合理分配空间。这是我的学习小心得总结 谢谢。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐