一種動態分配內存錯誤的解決辦法

1、項目背景

? ? ? 一款2年前開發的無線網絡通信軟件在最近的使用過程中出現網絡中傳感器離線的問題,此軟件之前已經使用的幾年了,基本功能還算穩定。這次為什么出了問題。

? ? ? ?先派工程師去現場調試一下,初步的結果是網絡信號弱,并且有個別主干網絡設備離線不工作。這次網絡的中的傳感器數量與比前比相差不多,但是傳感器數據的上報間隔較短。

2、臨時辦法

? ? ?網絡信號弱導致的通信時有中斷,需要通過加裝主干網絡設備增加信號覆蓋解決。同時調整傳感器數據的上報間隔變長,網絡中設備離線的情況不再出現,看來是數據量上報大有關了。

3、產品軟件問題

? ? ?雖然說現場問題臨時解決了,傳感器都上線了。但是這里面還是說明網絡設備的軟件有問題,這個問題是只有在大數量傳輸時會出現。那么就在公司搭建網絡環境,發送大數量測試,經過長達3周的測試,復現出現場的問題。網絡設備會出現內部MCU硬件定時器不工作和內存分配異常導致的無線網絡發送空數據包兩種問題。

3.1 定時器不工作

? ? ? 在某種特定的網絡情況下,網絡中的設備內部的MCU硬件定時器會被連續調用兩次啟動,正常的情況下是調用1次啟動,1次停止。可能由于數據量大,調用停止的操作未成功,就出現了連續兩次調用啟動的情況。經過分析定時器的驅動代碼和實際跟蹤發生問題時的情況,當第二次調用定時器啟動時,HAL_TIM_Base_Start(&htim2);函數是返回失敗,跟蹤代碼后發現定時器內部的狀態是工作狀態是BUSY狀態,返回錯誤,因為定時還在運行狀態。

? ? ? ? ? 分析下面的代碼,發現當調用StartWork函數-》InitTimer函數,此函數會通過寄存器操作把定時器關閉,之后再調用HAL_TIM_Base_Start函數啟動,由于寄存器操作把定時器并不修改定時器的狀態變量,導致再次調用啟動會失敗,正確的操作邏輯基于對外設備定時器的操作應統一方式,不能API函數調用和定時器操作混合使用,會導致異常情況驅動內部的狀態不正常。

/*==========================================================================
brief  : 硬件定時器初始化
param  : None
retval : None
//==========================================================================*/
void InitTimer(TimeIsUpCb_t callback)
{HTimer2Timing.Status = eSltTiming_Idle;HTimer2Timing.CurSltIdx = 0;HTimer2Timing.StartCnt = 0;HTimer2Timing.EndtCnt = myAttributes.TotalSltSize_us - myAttributes.SltSize_us - 1;HTimer2Timing.NextSltTCnt = 0;HTimer2Timing.CallBackWhenTimeIsUp = callback;寄存器操作停止了定時器,不能修改定時器內部的狀態變量__HAL_TIM_DISABLE(&htim2); //停止timier2計數htim2.Instance->CNT = 0;htim2.Instance->ARR = myAttributes.TotalSltSize_us - 1;__HAL_TIM_CLEAR_IT(&htim2, (TIM_IT_UPDATE | TIM_IT_CC1));__HAL_TIM_ENABLE_IT(&htim2, (TIM_IT_UPDATE | TIM_IT_CC1));
}/*==========================================================================
brief  : 開始工作命令處理函數,模塊開始廣播BCH,等待設備接入
param  : puartpkt: 串口幀數據指針,包含串口handle(用于說明串口數據是從哪個串口來的)
retval : None
//==========================================================================*/
void InitEachBuf(void);
void StartWork(UartFrame_t* puartpkt)
{//計算一次發送時不同時隙與最大傳輸字節數據對應表CalMaxBytesInOnePktBySlt(myAttributes.LoRaCfg.ModulationIndex, (myAttributes.SltSize_us),(myAttributes.BCHInfo.GP_Dphy * 100));InitAllTheadPt();InitEachBuf();BCHFrame_Init();InitTimer(Send_DL_Frame);調用定時器通過API函數調用 HAL_TIM_Base_Start(&htim2);myAttributes.WorkStatus = eModuleWorkSt_InNet;
}/*==========================================================================
brief  : 停止工作命令處理函數
param  : puartpkt: 串口幀數據指針,包含串口handle(用于說明串口數據是從哪個串口來的)
retval : None
//==========================================================================*/
void StopWork(UartFrame_t* puartpkt)
{myAttributes.WorkStatus = eModuleWorkSt_Idle;InitRadio();HAL_TIM_Base_Stop(&htim2);InitEachBuf();DevSltList_Init();DevRegList_Init();DevDRxList_Init();
}

? ? ?找到問題,解決辦法很簡單了,重新整理定時器的調用邏輯,全部通過API接口來調用定時器啟動和停止 ,這樣連續兩次調用啟動定時器也沒有問題。

3.2 內存分配異常

? ? ? ? 內存分配異常很難解決,前前后后增加了很多調試代碼,一步一步跟蹤分析,確定是內存分配導致的異常。

? ??


typedef struct 
{uint8_t  PktNum : 7;uint8_t  FlagRewind : 1;uint16_t Size;uint16_t Head;uint16_t Tail;uint16_t DataTail;//有效的數據結尾uint8_t* DataArr;
}StreamBuffer;內存申請代碼:
//申請一個保留空間給外部使用,不支持多線程、多次調用
uint8_t* ApplyMemByStreamBuffer(StreamBuffer *ptStreamBuffer, uint16_t size)
{uint8_t* Result = NULL;do{//如果申請空間大于空間大小則放棄if(size > ptStreamBuffer->Size)break;if(ptStreamBuffer->PktNum == 0){ptStreamBuffer->Head = 0;ptStreamBuffer->Tail = 0;ptStreamBuffer->DataTail = 0;ptStreamBuffer->FlagRewind = 0;}uint16_t newtail = ptStreamBuffer->Tail + size;if(newtail <= ptStreamBuffer->Size){if(ptStreamBuffer->FlagRewind){   //第2種情況,Tail超過Head產生數據覆蓋,不能分配出內存if(newtail > ptStreamBuffer->Head)break;}Result = (ptStreamBuffer->DataArr + ptStreamBuffer->Tail);}else //尾部空間浪費一些,不用了,這樣處理簡單{ptStreamBuffer->Tail = 0;ptStreamBuffer->FlagRewind = 1;if(ptStreamBuffer->PktNum){   //第2種情況,Tail超過Head產生數據覆蓋,不能分配出內存if((ptStreamBuffer->Tail + size) > ptStreamBuffer->Head)break;}//從緩存區頭部開始使用Result = ptStreamBuffer->DataArr;}}while(0);return(Result);
}
存在的問題:當可用內存為10240長度時,每次申請10個字節長度時,可申請出1024塊內存,使用PktNum 7位表不了這么多的內存,當申請的1024塊時,PktNum只能到127,無法表達內部存儲了多少塊?釋放內存時,只能釋放127塊。申請0長度時,能返回可以申請,需要處理一下。

? ? ?根據以上內存代碼,做了初步的代碼邏輯分析,發現一些問題,但實際測試是這些問題點全部未發生,做了分配的原理分析圖:內存分配基本邏輯正常。

? ? ? 后面經過多次測試,發現一個共同的現場,當內部出問題時,內存塊分配出去的計數PktNum與內存中已存儲的有效數據不一樣多。如下圖,計數中有8塊分配,根據內存指針來看已經沒有有效數據了。

? ? ? ?此種問題多次復現,并且出現時PktNum都是8,很有規律,當時內存中申請的數據塊長度為1225字節,整個總內存的長度為10240長度,計算下來正好能存儲8*1225,即8塊。說明內存在用完了后發生第二申請覆蓋,所以只有計數增加了。

? ? ? ?跟蹤內存分析的數據長度,發現如果內存中先分配8塊1225長度塊,再分配1個小塊147長度,最后釋放掉8塊1225長度的塊,再申請8塊1225長度的塊后,內存就滿了,再申請1塊1225長度的內存,內存出現的反轉,開始占用已經申請出去的內存。解決辦法,不允許此種情況下再分配出新的內存了。

uint8_t* ApplyMemByStreamBuffer(StreamBuffer *ptStreamBuffer, uint16_t size)
{uint8_t* Result = NULL;do{//如果申請空間大于空間大小則放棄/* 如果申請的空間是0或是內部存儲的PktNum已經最大,申請的數量已超總內存大小,返回失敗 zhaoshimin 20250429*/if((size > ptStreamBuffer->Size) || (size == 0) || (STREAM_BUFFER_PKTNUM_MAX == ptStreamBuffer->PktNum)){break;}    if(ptStreamBuffer->PktNum == 0){ptStreamBuffer->Head = 0;ptStreamBuffer->Tail = 0;ptStreamBuffer->DataTail = 0;ptStreamBuffer->FlagRewind = 0;}uint16_t newtail = ptStreamBuffer->Tail + size;if(newtail <= ptStreamBuffer->Size){if(ptStreamBuffer->FlagRewind){if(newtail > ptStreamBuffer->Head)break;}Result = (ptStreamBuffer->DataArr + ptStreamBuffer->Tail);}else //尾部空間浪費一些,不用了,這樣處理簡單{/*當內存中先放入1225的8包數據再放入147長度的包后,
釋放掉8包數據后,再申請放入1225長度的8包數據后,內存滿了,
發生第二次產生了反轉  20250429*/if(ptStreamBuffer->FlagRewind){break;}else{ptStreamBuffer->Tail = 0;ptStreamBuffer->FlagRewind = 1;if(ptStreamBuffer->PktNum){if((ptStreamBuffer->Tail + size) > ptStreamBuffer->Head)break;}}   Result = ptStreamBuffer->DataArr;}}while(0);return(Result);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/78974.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/78974.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/78974.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

React 第三十四節 Router 開發中 useLocation Hook 的用法以及案例詳解

一、useLocation基礎用法 作用&#xff1a;獲取當前路由的 location 對象 返回對象結構&#xff1a; {pathname: "/about", // 當前路徑search: "?namejohn", // 查詢參數&#xff08;URL參數&#xff09;hash: "#contact", …

DeepSeek-Prover-V2-671B最新體驗地址:Prover版僅適合解決專業數學證明問題

DeepSeek-Prover-V2-671B最新體驗地址&#xff1a;Prover版僅適合解決專業數學證明問題 DeepSeek 團隊于 2025 年 4 月 30 日正式在Hugging Face開源了其重量級新作 —— DeepSeek-Prover-V2-671B&#xff0c;這是一款專為解決數學定理證明和形式化推理任務而設計的超大規模語…

tornado_登錄頁面(案例)

目錄 1.基礎知識?編輯 2.腳手架&#xff08;模版&#xff09; 3.登錄流程圖&#xff08;processon&#xff09; 4.登錄表單 4.1后&#xff08;返回值&#xff09;任何值&#xff1a;username/password &#xff08;4.1.1&#xff09;app.py &#xff08;4.1.2&#xff…

Android學習總結之自定義view設計模式理解

面試題 1&#xff1a;請舉例說明自定義 View 中模板方法模式的應用 考點分析 此問題主要考查對模板方法模式的理解&#xff0c;以及該模式在 Android 自定義 View 生命周期方法里的實際運用。 回答內容 模板方法模式定義了一個操作的算法骨架&#xff0c;把一些步驟的實現延…

【Scrapy】簡單項目實戰--爬取dangdang圖書信息

目錄 一、基本步驟 1、新建項目 &#xff1a;新建一個新的爬蟲項目 2、明確目標 &#xff08;items.py&#xff09;&#xff1a;明確你想要抓取的目標 3、制作爬蟲 &#xff08;spiders/xxspider.py&#xff09;&#xff1a;制作爬蟲開始爬取網頁 4、存儲內容 &#xff08;p…

開源CMS系統的SEO優化功能主要依賴哪些插件?

在當今互聯網時代&#xff0c;搜索引擎優化&#xff08;SEO&#xff09;是網站獲取流量的核心手段之一。開源內容管理系統&#xff08;CMS&#xff09;因其靈活性和豐富的插件生態&#xff0c;成為許多開發者和企業的首選。本文將以主流開源CMS為例&#xff0c;深入解析其SEO優…

在 JMeter 中使用 BeanShell 獲取 HTTP 請求體中的 JSON 數據

在 JMeter 中&#xff0c;您可以使用 BeanShell 處理器來獲取 HTTP 請求體中的 JSON 數據。以下是幾種方法&#xff1a; 方法一&#xff1a;使用前置處理器獲取請求體 如果您需要在發送請求前訪問請求體&#xff1a; 添加一個 BeanShell PreProcessor 到您的 HTTP 請求采樣器…

在 WSL (Windows Subsystem for Linux) 中配置和安裝 Linux 環境

在 WSL (Windows Subsystem for Linux) 中配置和安裝 Linux 環境 WSL 允許你在 Windows 上運行 Linux 環境&#xff0c;以下是詳細的配置和安裝指南。 1. 安裝前的準備工作 系統要求 Windows 10 版本 2004 及更高版本(內部版本 19041 及更高版本)或 Windows 11 64 位系統 虛…

AlphaFold蛋白質結構數據庫介紹

AlphaFold Protein Structure Database (AlphaFold DB) 是 DeepMind + EMBL-EBI 合作開發的公開蛋白質結構預測數據庫,是利用 AlphaFold2/AlphaFold3 AI模型 預測的全基因組級蛋白質三維結構庫。 網址: https://alphafold.ebi.ac.uk 項目內容主辦單位DeepMind + EMBL-EBI上線…

3.2goweb框架GORM

GORM 是 Go 語言中功能強大的 ORM&#xff08;對象關系映射&#xff09;框架&#xff0c;支持 MySQL、PostgreSQL、SQLite、SQL Server 等主流數據庫。以下是 GORM 的核心概念和用法詳解&#xff1a; ??一、基礎入門?? 1. 安裝 go get -u gorm.io/gorm go get -u gorm.io…

第三部分:特征提取與目標檢測

像邊緣、角點、特定的紋理模式等都是圖像的特征。提取這些特征是許多計算機視覺任務的關鍵第一步&#xff0c;例如圖像匹配、對象識別、圖像拼接等。目標檢測則是在圖像中找到特定對象&#xff08;如人臉、汽車等&#xff09;的位置。 本部分將涵蓋以下關鍵主題&#xff1a; …

Canvas基礎篇:圖形繪制

Canvas基礎篇&#xff1a;圖形繪制 圖形繪制moveTo()lineTo()lineTo繪制一條直線代碼示例效果預覽 lineTo繪制平行線代碼示例效果預覽 lineTo繪制矩形代碼示例效果預覽 arc()arc繪制一個圓代碼實現效果預覽 arc繪制一段弧代碼實現效果預覽 arcTo()rect()曲線 結語 圖形繪制 在…

瑞芯微芯片算法開發初步實踐

文章目錄 一、算法開發的一般步驟1.選擇合適的深度學習框架2.對于要處理的問題進行分類&#xff0c;是回歸問題還是分類問題。3.對數據進行歸納和整理4.對輸入的數據進行歸一化和量化&#xff0c;保證模型運行的效率和提高模型運行的準確度5.在嵌入式處理器上面運行模型&#x…

計算機畢業設計--基于深度學習(U-Net與多尺度ViT)的模糊車牌圖像清晰化復原算法設計與實現(含Github代碼+Web端在線體驗鏈接)

基于深度學習的U-Net架構下多尺度Transformer車牌圖像去模糊算法設計與實現 如果想對舊照片進行模糊去除&#xff0c;劃痕修復、清晰化&#xff0c;請參考這篇CSDN作品&#x1f447; 計算機畢業設計–基于深度學習的圖像修復&#xff08;清晰化劃痕修復色彩增強&#xff09;算…

(Go Gin)Gin學習筆記(四)Gin的數據渲染和中間件的使用:數據渲染、返回JSON、淺.JSON()源碼、中間件、Next()方法

1. 數據渲染 1.1 各種數據格式的響應 json、結構體、XML、YAML類似于java的properties、ProtoBuf 1.1.1 返回JSON package mainimport ("github.com/gin-gonic/gin""net/http" )func main() {r : gin.Default()r.POST("/demo", func(res *gi…

實驗:串口通信

/************************************************* * AT89C52 串口通信實驗&#xff08;實用修正版&#xff09; * 特點&#xff1a; * 1. 解決所有編譯警告 * 2. 保持代碼簡潔 * 3. 完全功能正常 ************************************************/ #include <re…

智駕賽道的諾曼底登陸,Momenta上海車展雄起

作者 |蘆葦 編輯 |德新 今年的上海車展依舊熱鬧非凡&#xff0c;但火熱的車市背后也是暗流涌動。尤其對智駕供應商而言&#xff0c;「智駕平權」帶動了解決方案大量上車&#xff0c;各大主機廠紛紛選定各自的主要供應商&#xff0c;這也意味著賽道機會越發收斂。 正如汽車品牌…

Java 事務詳解

目錄 一、事務的基本概念1.1 什么是事務?1.2 事務的 ACID 特性二、Java 事務管理的實現方式2.1 JDBC 事務管理2.2 Spring 事務管理2.2.1 添加 Spring 依賴2.2.2 配置 Spring 事務管理2.2.3 使用 Spring 事務注解三、事務隔離級別四、最佳實踐4.1 盡量縮小事務范圍4.2 合理選擇…

DirectX12(D3D12)基礎教程七 深度模板視圖\剔除\謂詞

本章主要講遮擋&#xff0c;作者認為比較復雜有難度的知識點&#xff0c;作為基礎教程不會深入講解。 GPU渲染管線 主要包括以下階段 輸入裝配&#xff08;IA&#xff09;&#xff1a;讀取頂點數據 &#xff0c;定義頂點數據結構頂點著色&#xff08;VS&#xff09;&#xf…

溫補晶振(TCXO)穩定性優化:從實驗室到量產的關鍵技術

在現代通信、航空航天、5G基站等對頻率穩定性要求極高的領域&#xff0c;溫補晶振&#xff08;TCXO&#xff09;扮演著不可或缺的角色。其穩定性直接影響系統的性能與可靠性&#xff0c;因此&#xff0c;對TCXO穩定性優化技術的研究與實踐至關重要。 一、溫度補償算法&#xff…