一 背景
原先做的项目,TCP通讯采用stm32f103+freertos+w5500方案,实现的tcp单连接通信,同事提了个需求让实现tcp多连接,于是改了一版代码。测试发现根本跑不起来,程序应该是卡死崩溃了,相关问题代码如下:
c
void do_tcp_server(void)
{
switch(wizGetSnSR(tcp_socket[i].sock_num)) /*获取socket的状态*/
{
case SOCK_CLOSED:
printf("SOCK %d CLOSED\r\n",tcp_socket[i].sock_num); /*socket处于关闭状态*/
socket(tcp_socket[i].sock_num ,Sn_MR_TCP,local_port,Sn_MR_ND); /*打开socket*/
break;
case SOCK_INIT:
printf("SOCK %d INIT\r\n",tcp_socket[i].sock_num); /*socket已初始化状态*/
listen(tcp_socket[i].sock_num); /*socket建立监听*/
break;
...
}
}
在上面这段代码里面,如果我将打印
c
printf("SOCK %d CLOSED\r\n",tcp_socket[i].sock_num);
printf("SOCK %d INIT\r\n",tcp_socket[i].sock_num);
改为
c
printf("SOCK CLOSED\r\n");
printf("SOCK INIT\r\n");
则程序运行正常。
二 定位分析过程
- 大方向是堆栈溢出 问了下chatgpt是说printf("hello")和printf("%d")格式化打印,消耗的堆栈是不一样的,前者纯字符串,压栈的时候只是个地址,而后者则要压入更多内容。这么说来应该是堆栈溢出问题。于是我将打印改为printf("SOCK CLOSED\r\n"")和printf("SOCK INIT\r\n")在另一个任务里面调用vTasklist打印出任务堆栈信息如下:
c
Name State Prio Stack Num
greenLedTask R 3 86 3
yellowLedTask R 3 86 4
beepLedTask R 3 86 6
tcpServerTask R 3 0 1
redLedTask X 3 0 5
mail_station R 3 88 2
IDLE R 0 107 7
关联的任务函数tcpServerTask剩余堆栈大小是0,也就是说此时该任务已经没有堆栈空间了,printf改为%d打印,多一点就可能导致堆栈溢出;
2.堆栈消耗能差多少呢 我将tcpServerTask任务堆栈大小由128word改为256word,然后打印两种情况下的剩余对战情况:
c
//printf("SOCK CLOSED\r\n"")和printf("SOCK INIT\r\n")
Name State Prio Stack Num
greenLedTask R 3 86 3
yellowLedTask R 3 86 4
beepLedTask R 3 86 6
tcpServerTask R 3 106 1
redLedTask X 3 0 5
mail_station R 3 88 2
IDLE R 0 107 7
c
//printf("SOCK %d CLOSED\r\n",tcp_socket[i].sock_num);
//printf("SOCK %d INIT\r\n",tcp_socket[i].sock_num);
Name State Prio Stack Num
redLedTask X 3 0 5
tcpServerTask R 3 57 1
IDLE R 0 109 7
greenLedTask B 3 86 3
yellowLedTask B 3 86 4
beepLedTask B 3 86 6
mail_station B 3 88 2
根据打印结果看printf%d格式化输出比单纯打印纯字符串,任务堆栈要多消耗50个word大小左右,所以在你任务本身剩余堆栈很少的情况下,一个printf打印就可能导致溢出,程序崩溃。
- 最后解决方案
由于多链接,这里打印最好能打印出socket编号,所以
- 增加总的堆内存大小5000-》8192
- 增加printf%d打印任务堆栈大小128-》256
c
configTOTAL_HEAP_SIZE ((size_t)8192)
osThreadDef(tcpServerTask, StartTcpServerTask, osPriorityNormal, 0, 256);