记一次lwip中 遇到 pcb == pcb-next 的pcb死循环debug过程
生活随笔
收集整理的這篇文章主要介紹了
记一次lwip中 遇到 pcb == pcb-next 的pcb死循环debug过程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
amoBBS 阿莫電子論壇
標題:? 記一次lwip中 遇到 pcb == pcb->next 的pcb死循環debug過程? [打印本頁]作者:? kayatsl? ?? 時間:? 2013-10-11 17:44
標題:? 記一次lwip中 遇到 pcb == pcb->next 的pcb死循環debug過程
本帖最后由 kayatsl 于 2013-10-11 17:50 編輯?
[attach]144489[/attach]
如圖中, 在tcp_slowtmr() 中, 歷遍 tcp_tw_pcbs 過程中, 其中一個 pcb -> next 指向pcb自身
導致while循環永久死在這里跳不出去, 程序死機..? ???這種問題, 雖然說必然會發生, 但發生的時間隨機性很強, 很難捕捉到什么時候一定會出現, 所以這種bug很難解決
這種情況在國外論壇搜了下, 都是同一個回答: 當跑os的時候,有多個線程同時爭搶就會導致這種問題產生
但到底怎么產生的, 和如何避免, 沒有作詳細介紹,也沒有案例分析..
這現象以前也有遇到過, 其實這種情況通過寫一個定時檢測是否有pcb指向自身 和加入看門狗, 也能保證程序不會一直死在這,但這次一個偶然的機會,決定跟個所以然.
[attach]144490[/attach]
這次由于時間片之間程序運行的先后順序不同, 會導致hardfault產生, hardfault沒有截圖, 系統是記錄到我讀取一個非法地址, 引致hardfault
從堆棧中跟蹤得知, hardfault前跑的函數是 memp_malloc 中 memp_tab[type] = memp->next 引起的, 而memp 自身被指向了一個非內存地址, 再讀取 ->next 必然導致出錯
藍色圈是我加入的條件斷點, 來捕捉異常狀態.
這里得知memp_tab[2] 中的地址指向出問題了, 但這和 pcb的死循環有什么關系呢? 到這里還是看不出頭緒.. 思路斷了, 那就回去繼續跟蹤 pcb的問題
[attach]144491[/attach]
既然已經知道死循環的 pcb是屬于 tcp_tw_pcbs 鏈表里面的, 那就 ctrl+shift+f 把所有對 tcp_tw_pcbs 有操作的地方都找出來, 統統下條件斷點
看執行到哪一句后會出現死循環的現象
程序果然乖乖的死在陷阱里面, 這樣看來, 明顯是運行了 tcp_reg 后才出現的現象, 但為什么運行這個后會導致死循環呢? 沒什么頭緒? 那就跟另一條線索
[attach]144502[/attach]
跟蹤一下 memp_tab的情況,
圖中紅字部分已經說得差不多了,??這里給了我們一個很重要的線索, 就是 memp_tab[]->next 和 tcp_tw_pcbs -> local_ip 為什么會重疊了..
試圖跟著這個函數返回一下, 看看會有什么情況
[attach]144493[/attach]
沒錯, 這里又重新給這個pcb賦值了, 然后又重新遞回到上層做處理..
也就是說, 系統給我們分配了一個 正在使用的內存區域, 來處理新的東西, 那必然會導致和舊的東西發生沖突.. 要驗證這句話? 繼續下條件斷點
[attach]144494[/attach]
圖中可以看到, 進來的pcb, 本來是屬于 active_pcbs 鏈表的, 剛從 active 鏈表中移除, 準備放到 timewait鏈表里面去, 但此時發現, 這個pcb的內存空間,正是tw_pcbs的內存空間
其實 TCP_REG 很簡單, 就是把指針指一下, 就注冊過去了,??但就是因為太簡單了, 沒有做任何判斷, 所以自己指回了自己也不知道.
其實到這里, 可以在這個定義里面寫個判斷, 遇到指向自己的就不進行操作, 但這樣還是沒找到最原始導致的原因..??所以必須繼續跟蹤下去
[attach]144495[/attach]
再截一個圖, 就是 死循環的圖..??大家可以比較一下這幾個圖里面紅圈圈著的地址,??截圖順序是按照程序執行順序來的
[attach]144496[/attach]
回想一下, malloc的時候, 分配到同一個地址空間, 那明顯就是上一次分配的時候, ->next指針又指回自己了, 驗證這個猜想, 繼續下條件斷點..
果然不出所料, memp == memp->next , 而這個是分配的時候導致的還是釋放的時候導致的, 暫時還不確定,
但是這個出錯的現場, 可以在堆棧中繼續追溯是哪里調用的, 但這里還不是第一現場, 為了還原第一現場, 那就繼續跟著 callstack回去找
[attach]144497[/attach]
這圖中很有意思..??這里程序開始的時候, 我使用 tcp_new 創建一個pcb, 而 tcp_new 又調用 malloc 來取出一個 pcb的內存空間
而我在 tcp_abort 這里打了個斷點, 按道理來說, 我還沒有 abort掉這個鏈接, 空間是不應該被釋放掉的, 但奇怪的是 memp_tab[2] 中指向的地址,確實是我正在使用的地址
memp_tab[] 鏈表是空閑內存的鏈表, 而這個內存塊是我正在使用的, 怎么會出現在空閑鏈表中??
越來越接近真相了,??就是這塊內存申請后, 在使用中, 意外被別人釋放了, 那罪灰禍首到底是誰??
[attach]144508[/attach]
那就繼續下條件斷點去捕捉釋放前的現象,.?
圖中可以看到, memp_Free 地址是跟 memp_alloc一樣的 ..??這里的原因是,我鏈接很頻繁,一個pcb還沒完全關閉, 又建立另一個鏈接, 所以會導致剛剛free出來的一個塊, 馬上又被用來使用了
所以這兩個地址一樣并不驚訝, 驚訝的是, 同一個地址, 為什么會出現在 memp_tab[2] 中???
這明顯是 free完這個塊, 然后 malloc到來用, 但用的過程中, 還沒等我釋放, 就被別的地方釋放了, 所以導致這塊正在使用的內存塊會跑到了空閑內存塊中去..
可以斷定是free多了一次出問題了, 就好辦了..
[attach]144499[/attach]
由上面的現象可以估計 是pcb的內存空間剛剛分配到, 很短時間又被釋放了, 既然是這樣, 就在 memp_free中下條件斷點??當現在free的,剛好是剛剛malloc出來的內存塊就斷
因為正常情況下, free的肯定是舊的空間, 不可能剛剛malloc就被free掉的.??
等了一段時間, 中陷阱了..??其實看到這個現場沒什么作用, 本來就已經猜到的事情,??而在這里下斷點的目的, 是要跟蹤, 到底是誰把它釋放掉的.
看 call stack 就一目了然了..??繼續跟回去 tcp_input中
[attach]144500[/attach]
這時候清晰了.. 這個pcb收到了關閉請求, 然后關閉掉pcb鏈接, 順便把內存區域也 free掉了..
好了, 看到這里,??其實大家往回讀就知道到底 pcb死循環是怎么引起的了...
我正在開啟一條到服務器的鏈接, 但在等待確定鏈接已經保持的過程中, 意外被服務器關閉了, 而由于時間太短, 這時候, 我應用程序認為 服務器并沒有連接上, 然后把這個tcp的控制塊 abort掉
而 abort 的時候調用 free 內存的函數,??卻不知道這塊內存其實已經是free掉的了, 再free一次, 就會出現 memp_tab中 元素的 -next 指回自身.
而這時候再 malloc一個新內存空間的時候, 由于 next永遠是指回同一塊內存區域, 所以導致下次再分配的時候, 還是分配同一塊空間給應用層
但應用層不知道分配的是同一塊空間, 而繼續使用, 然后就導致兩條鏈接共用同一個內存區域.. 但lwip自己并不知道.
當兩條鏈接最終注冊入同一個 pcbs鏈表去管理的時候, 就會導致自己指回自己, 因為本來就是同一塊內存,.. 最后就 pcb死循環了..
跟蹤到此結束
跟蹤完當然是解決問題了..
[attach]144503[/attach]
在 memp_free中 ,free之前做一個判斷 由于type == 2 屬于 tcp_pcb, 而 tcp_pcb 的 前四個字節是不可能為0x00的,?
所以可以判斷為當 memp->next 為 NULL 的時候, 這片內存已經釋放過了, 就不再重新釋放, 直接跳到程序末尾, 解鎖
經驗證, 方法正常, 因為 出問題通常是 tcp_pcb 的問題,因為 tcp控制太復雜了, 所以目前只控制 type==2 的地方就可以保證程序正常了.
到此 結貼.
作者:? kayatsl? ?? 時間:? 2013-10-11 18:04
大家有什么好方法避免的..??提一下.. 集思廣益啊...
作者:? 圣騎士by? ?? 時間:? 2013-10-11 18:06
我僅頂一下 表示我看不懂……
作者:? kayatsl? ?? 時間:? 2013-10-11 19:21
因為內存空間是直接分配出來的..??所以最后解決只能這么判斷
不過呵..
/* No sanity checks
* We don't need to preserve the struct memp while not allocated, so we
* can save a little space and set MEMP_SIZE to 0.
*/
#define MEMP_SIZE? ?? ?? ???0
這個memp_sizze 其實可以定義一下, 這里定義了4的話 , 每個頭部都會有4個空余字節.
然后每次 在malloc中 memp = (struct memp*)((u8_t*)memp + MEMP_SIZE); 之前, 都先 memp->next = 賦一個固定的值;
然后 free的時候, 檢查下 ->next 指針 如果不是這個固定值的話, 就說明已經被free過了, 如果還是固定值的話, 就free一下..
理論上想了下, 應該可以這么做, 只是每個區間都再加4個字節的話.. 這片內存區域耗用還是挺龐大的..
作者:? kayatsl? ?? 時間:? 2013-10-11 19:28
網上都說 多線程訪問導致的, 而這次的情況, 確實也是因為兩條線程都對一個對象訪問導致的..
但由于我調用的是 rawapi , 所以在connect的時候, 必須vtaskdelay 將cpu控制權讓出來給 lwip內核來跑, 才能connect上服務器
這個等待時間很難斷定 , 網速無法保證, 而就在等待過程中, 剛連上服務器,又被服務器踢掉, 踢掉的時候, 是用lwip的內核線程來處理的.
所以...不從內存分配的方向去解決的話, 這問題基本是無解了...
希望給遇到同樣問題的人, 看到之后, 能快速定位到自己程序哪里引起這問題吧..
先寫到這了...
作者:? zf8848? ?? 時間:? 2013-10-11 21:25
感謝樓主共享自己的經驗, lwip 確實有一些莫名奇妙的 bug.
作者:? fengyunyu? ?? 時間:? 2013-10-11 21:57
這個分析很到位啊。前面有個帖子似乎也提到了同樣的問題。
作者:? yiyu? ?? 時間:? 2013-10-11 21:57
本帖最后由 yiyu 于 2013-10-11 22:09 編輯?
lwip是單線程的,所有api都是通過隊列通訊的,使用row是在裸奔情況下,應用和lwip在一個線程內,
作者:? fengyunyu? ?? 時間:? 2013-10-11 22:04
yiyu 發表于 2013-10-11 21:57?
lwip是單線程的,所有api都是通過隊列通訊的,
lwip是單線程,如何理解?是不能用在使用操作系統的多任務的環境中?還是指?
作者:? yiyu? ?? 時間:? 2013-10-11 22:17
本帖最后由 yiyu 于 2013-10-11 23:19 編輯?
lwip在os中作為一個獨立線程實現,外部應用只能使用api調用,可以看一下api的實現,很多都是把row函數通過消息隊列發送到lwip線程調用
api投遞消息
err_t
tcpip_apimsg(struct api_msg *apimsg)
{
??struct tcpip_msg msg;
??
??if (mbox != SYS_MBOX_NULL) {
? ? msg.type = TCPIP_MSG_API;
? ? msg.msg.apimsg = apimsg;
? ? ?sys_mbox_post(mbox, &msg);
? ? sys_arch_sem_wait(apimsg->msg.conn->op_completed, 0);
? ? return ERR_OK;
??}
??return ERR_VAL;
}
lwip線程
static void
tcpip_thread(void *arg)
{
??struct tcpip_msg *msg;
??LWIP_UNUSED_ARG(arg);
#if IP_REASSEMBLY
??sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
#endif /* IP_REASSEMBLY */
#if LWIP_ARP
??sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
#endif /* LWIP_ARP */
#if LWIP_DHCP
??sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
??sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
#endif /* LWIP_DHCP */
#if LWIP_AUTOIP
??sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
#endif /* LWIP_AUTOIP */
#if LWIP_IGMP
??sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
#endif /* LWIP_IGMP */
#if LWIP_DNS
??sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
#endif /* LWIP_DNS */
??if (tcpip_init_done != NULL) {
? ? tcpip_init_done(tcpip_init_done_arg);
??}
??LOCK_TCPIP_CORE();
??while (1) {? ?? ?? ?? ?? ?? ?? ?? ???/* MAIN Loop */
? ?? sys_mbox_fetch(mbox, (void *)&msg);
? ? switch (msg->type) {
#if LWIP_NETCONN
? ? case TCPIP_MSG_API:
? ?? ?LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
? ?? ?msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
? ?? ?break;
#endif /* LWIP_NETCONN */
? ? case TCPIP_MSG_INPKT:
? ?? ?LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
#if LWIP_ARP
? ?? ?if (msg->msg.inp.netif->flags & NETIF_FLAG_ETHARP) {
? ?? ???ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
? ?? ?} else
#endif /* LWIP_ARP */
? ?? ?{ ip_input(msg->msg.inp.p, msg->msg.inp.netif);
? ?? ?}
? ?? ?memp_free(MEMP_TCPIP_MSG_INPKT, msg);
? ?? ?break;
#if LWIP_NETIF_API
? ? case TCPIP_MSG_NETIFAPI:
? ?? ?LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
? ?? ?msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
? ?? ?break;
#endif /* LWIP_NETIF_API */
? ? case TCPIP_MSG_CALLBACK:
? ?? ?LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
? ?? ?msg->msg.cb.f(msg->msg.cb.ctx);
? ?? ?memp_free(MEMP_TCPIP_MSG_API, msg);
? ?? ?break;
? ? case TCPIP_MSG_TIMEOUT:
? ?? ?LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
? ?? ?sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
? ?? ?memp_free(MEMP_TCPIP_MSG_API, msg);
? ?? ?break;
? ? case TCPIP_MSG_UNTIMEOUT:
? ?? ?LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
? ?? ?sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
? ?? ?memp_free(MEMP_TCPIP_MSG_API, msg);
? ?? ?break;
? ? default:
? ?? ?break;
? ? }
??}
}
作者:? kayatsl? ?? 時間:? 2013-10-11 23:19
如果是用 netconn 來做的話, 確實是通過調用 tcpip_apimsg 來傳遞信息
但也可以直接調用raw_api 和設置自己的回調函數來做處理? ?lwip本身也是提供 raw_api 來給應用做更快速的處理的..??處理得到位就不會出現爭搶的現象了.
作者:? fengyunyu? ?? 時間:? 2013-10-12 11:05
kayatsl 發表于 2013-10-11 23:19?
如果是用 netconn 來做的話, 確實是通過調用 tcpip_apimsg 來傳遞信息
但也可以直接調用raw_api 和設置自 ...
lwip到底能否商用還值得商榷。附網上看到一篇文章。
成功實現了LWIP的keepalive功能
原來的lwip沒有啟動keepalive功能,導致tcp客戶端工作不可靠,主要就是無法處理服務器的斷線、斷網、down機等異常。表現是服務器故障后,tcp客戶端總是等待無法返回,造成鎖死。
處理方法:
1,使用TCPkeepalive功能,定時探測連接的狀態,當發生掉線時,自動關閉連接,正常返回。
2,檢測網線狀態PHY寄存器中有標準位。(沒有上一種方法好。)
下面詳細介紹如何啟動keepalive
1,打開keepalive的標志使能。
2,修改keepalive各個計數值,主要是改小,方便測試。
3,在pcb中需要置位keepalive的一個選項。pcb->so_options |= SOF_KEEPALIVE;
4,修改pcb的一處bug,該bug可以通過給我匯款獲得。
啟動了keepalive才是真正的tcp連接,用起來穩定可靠,異常爽快。
by 天遠易 發表于:2012/7/13 10:04:43?
作者:? kayatsl? ?? 時間:? 2013-10-12 11:33
fengyunyu 發表于 2013-10-12 11:05?
lwip到底能否商用還值得商榷。附網上看到一篇文章。
keepalive 只是一個心跳包的作用..
本身應用層有設計心跳包機制, 就不需要底層再做了..
作者:? fengyunyu? ?? 時間:? 2013-10-12 11:35
kayatsl 發表于 2013-10-12 11:33?
keepalive 只是一個心跳包的作用..
本身應用層有設計心跳包機制, 就不需要底層再做了..
原文中的“4,修改pcb的一處bug,該bug可以通過給我匯款獲得。"
作者:? kayatsl? ?? 時間:? 2013-10-12 13:58
fengyunyu 發表于 2013-10-12 11:35?
原文中的“4,修改pcb的一處bug,該bug可以通過給我匯款獲得。"
哈哈哈... 肯定不是這個bug... 嚴格來說 這個bug并不屬于pcb自身的..
作者:? DOER? ?? 時間:? 2013-10-12 20:50
密切關注中......
作者:? SNOOKER? ?? 時間:? 2013-10-12 22:10
高級技術帖,頂起
作者:? farmerzhangdl? ?? 時間:? 2013-10-14 11:11
我最近出現了一個lwip的問題,也是連接上的。。。還在查
作者:? farmerzhangdl? ?? 時間:? 2013-10-14 11:12
keepalive并不一定需要,網上那個說開啟了keepalive才是真的tcp我認為是錯誤的,完全可以通過自定義心跳包解決
作者:? kayatsl? ?? 時間:? 2013-10-14 11:16
farmerzhangdl 發表于 2013-10-14 11:12?
keepalive并不一定需要,網上那個說開啟了keepalive才是真的tcp我認為是錯誤的,完全可以通過自定義心跳包 ...
哎.. 現在還是會遇到 指向自己, 只不過導致的原因不是原來的地方, 跑到新的地方了.. 而且跑大半天甚至幾天才能觸發一次.. 更不好跟了
作者:? fengyunyu? ?? 時間:? 2013-10-14 11:53
本帖最后由 fengyunyu 于 2013-10-14 17:38 編輯?
kayatsl 發表于 2013-10-14 11:16?
哎.. 現在還是會遇到 指向自己, 只不過導致的原因不是原來的地方, 跑到新的地方了.. 而且跑大半天甚至幾 ...
LZ是做Server還是做Client?據說做Server比較穩定,做Client則問題較多。
作者:? kayatsl? ?? 時間:? 2013-10-14 12:03
同時做 TCP的Client? ?UDP的Server 和 Client
作者:? kayatsl? ?? 時間:? 2013-10-14 12:04
fengyunyu 發表于 2013-10-14 11:53?
LZ是做Server還是做Client?據說做Server比較穩定,做Client則問題較多。另外,裸奔似乎更穩定些。公司有 ...
做Server比較穩定,做Client則問題較多
這個說法我也看到過..??后來經過一段時間后判斷是他不會用..
作者:? inurl? ?? 時間:? 2013-10-14 15:04
? ? npcb = tcp_alloc(pcb->prio);
作者:? inurl? ?? 時間:? 2013-10-14 15:05
? ? npcb = tcp_alloc(pcb->prio);??后面加個 ? ? ? ? if(npcb == tcp_active_pcbs ){
? ? ? ? ? ? ? ? tcp_close(npcb)? ? ? ? ;
? ? ? ? ? ? ? ? return ERR_OK;
? ? ? ? }
作者:? inurl? ?? 時間:? 2013-10-14 15:05
判一下這個npcb 是不是在tcp_active_pcbs 里面
作者:? kayatsl? ?? 時間:? 2013-10-14 15:34
inurl 發表于 2013-10-14 15:05?
npcb = tcp_alloc(pcb->prio);??后面加個 ? ? ? ? if(npcb == tcp_active_pcbs ){
? ? ? ? ? ? ? ? tcp_close(npcb)? ? ? ? ;
? ? ? ? ? ? ? ? retu ...
這個需要歷遍一下 active的鏈表,??不一定就是第一個pcb
同時, 不一定在 active鏈表, 也可能在 tw鏈表..
但是我感覺, 避免發生, 總比發生了之后解決要好..
得找到發生的原因
作者:? farmerzhangdl? ?? 時間:? 2013-10-14 23:00
我是好多設備只有一臺發生過這個問題,但是沒有查到原因,加了個狗,但是狗也沒起作用。。。
作者:? a317606001? ?? 時間:? 2013-10-15 10:08
mark? ?? ?? ?? ?? ?
作者:? inurl? ?? 時間:? 2013-10-16 08:41
mark?
作者:? inurl? ?? 時間:? 2013-10-16 11:19
你先把hard_fault解決了吧, 應該跟lwip沒關系, 除非是lwip主任務堆棧溢出或者是任務退出了
作者:? kayatsl? ?? 時間:? 2013-10-16 11:48
inurl 發表于 2013-10-16 11:19?
你先把hard_fault解決了吧, 應該跟lwip沒關系, 除非是lwip主任務堆棧溢出或者是任務退出了??...
hardfault 就是因為memp指針指出了內存區域..
解決hardfault就是解決lwip的問題
作者:? fengyunyu? ?? 時間:? 2013-10-16 12:07
kayatsl 發表于 2013-10-16 11:48?
hardfault 就是因為memp指針指出了內存區域..
解決hardfault就是解決lwip的問題 ...
看來內存管理的水很深。
作者:? inurl? ?? 時間:? 2013-10-16 19:06
我發現tcp_tw_pcbs在tcp_close或者tcp_abort的時候不會釋放, 只能等到tcp_alloc失敗的時候才去釋放
作者:? inurl? ?? 時間:? 2013-10-16 19:09
??我覺得有可能是一個代碼重入的問題。
作者:? inurl? ?? 時間:? 2013-10-16 19:11
??我覺得有可能是一個代碼重入的問題。導致了重復釋放
作者:? kayatsl? ?? 時間:? 2013-10-17 09:16
inurl 發表于 2013-10-16 19:06?
我發現tcp_tw_pcbs在tcp_close或者tcp_abort的時候不會釋放, 只能等到tcp_alloc失敗的時候才去釋放 ...
tcp_close 是要等待服務器回復才會釋放? ?abort 調用 abanden, 是馬上釋放的.
是不是重入問題還需要跟蹤下, 現在這問題比之前更加不定時了,, 之前起碼說一個小時內還能出錯, 現在幾天都沒死一次, 真還原不了現場
作者:? fengyunyu? ?? 時間:? 2013-10-17 09:24
kayatsl 發表于 2013-10-17 09:16?
tcp_close 是要等待服務器回復才會釋放? ?abort 調用 abanden, 是馬上釋放的.
是不是重入問題還需要跟蹤 ...
說lwip穩定的,一般是做server。做client,LZ算是吃螃蟹的。
作者:? 菜包? ?? 時間:? 2013-10-17 09:37
LZ好有耐心啊,這么一步一步跟進分析,收藏一下以后備用,我的LwIP沒用在OS下面,跑得很正常
作者:? kayatsl? ?? 時間:? 2013-10-17 09:56
fengyunyu 發表于 2013-10-17 09:24?
說lwip穩定的,一般是做server。做client,LZ算是吃螃蟹的。
應該不會吧... 做Client的人也不少的其實..
其實做Server和做Client, 感覺做起來差別倒不是很大..
作者:? inurl? ?? 時間:? 2013-10-17 21:06
kayatsl 發表于 2013-10-17 09:16
tcp_close 是要等待服務器回復才會釋放? ?abort 調用 abanden, 是馬上釋放的.
是不是重入問題還需要跟蹤 ...
abort只在tcp_malloc的時候調用。你狂點tcp連接試試
作者:? fengyunyu? ?? 時間:? 2013-10-17 21:29
inurl 發表于 2013-10-17 21:06?
abort只在tcp_malloc的時候調用。你狂點tcp連接試試
那就相當于DoS攻擊了。LWIP不管是做server還是client,這種情況都會崩潰
作者:? kayatsl? ?? 時間:? 2013-10-18 00:46
inurl 發表于 2013-10-17 21:06?
abort只在tcp_malloc的時候調用。你狂點tcp連接試試
因為第一次malloc的時候找不到空的內存塊的話, 會將一個現有但已經關閉的pcb釋放掉來用, 釋放就調用abort了.
作者:? inurl? ?? 時間:? 2013-10-21 19:35
有啥發現沒?
作者:? inurl? ?? 時間:? 2013-10-21 19:36
inurl 發表于 2013-10-17 21:06
abort只在tcp_malloc的時候調用。你狂點tcp連接試試
不會的,限制tcp就不會崩潰
作者:? kayatsl? ?? 時間:? 2013-10-22 00:34
inurl 發表于 2013-10-21 19:35?
有啥發現沒?
這幾天在忙別的東西..??明天再寫個陷阱給他吧..
作者:? fengyunyu? ?? 時間:? 2013-10-22 08:55
inurl 發表于 2013-10-21 19:36?
不會的,限制tcp就不會崩潰
限制tcp,如何限制?
作者:? inurl? ?? 時間:? 2013-10-23 08:41
fengyunyu 發表于 2013-10-22 08:55?
限制tcp,如何限制?
計數 and tcp_accept()拒絕
作者:? fengyunyu? ?? 時間:? 2013-10-28 01:06
LZ,有新進展了么?
作者:? xilou? ?? 時間:? 2013-10-28 09:13
技術貼,mark一下。
作者:? kayatsl? ?? 時間:? 2013-10-28 09:17
fengyunyu 發表于 2013-10-28 01:06?
LZ,有新進展了么?
哎.. 沒連調試器的機子久不久都出問題.. 連著調試器的. 跑了一個星期都沒出問題.. 郁悶...
偶爾有一次進去了. 結果忘了開中斷, 截不到原始記錄
作者:? fengyunyu? ?? 時間:? 2013-10-30 20:00
kayatsl 發表于 2013-10-28 09:17?
哎.. 沒連調試器的機子久不久都出問題.. 連著調試器的. 跑了一個星期都沒出問題.. 郁悶...
偶爾有一次進 ...
估計不用操作系統,裸奔能解決問題。最近也碰到過類似問題,最后改成RAW模式未再測試到問題。
作者:? kayatsl? ?? 時間:? 2013-10-31 00:19
fengyunyu 發表于 2013-10-30 20:00?
估計不用操作系統,裸奔能解決問題。最近也碰到過類似問題,最后改成RAW模式未再測試到問題。 ...
整個系統都搭好了, 不能說不用操作系統
前兩天找到解決方案了, 復現了兩三次, 能夠解決了.
再測試多幾天, 確定沒問題就可以結貼了...
作者:? kayatsl? ?? 時間:? 2013-11-1 15:35
kayatsl 發表于 2013-10-31 00:19?
整個系統都搭好了, 不能說不用操作系統
前兩天找到解決方案了, 復現了兩三次, 能夠解決了.
[attach]149398[/attach]
問題解決方案如上..??至今測試正常..
作者:? kayatsl? ?? 時間:? 2013-11-1 15:39
fengyunyu 發表于 2013-10-30 20:00?
估計不用操作系統,裸奔能解決問題。最近也碰到過類似問題,最后改成RAW模式未再測試到問題。 ...
54樓 解決方案
作者:? kayatsl? ?? 時間:? 2013-11-1 15:40
inurl 發表于 2013-10-21 19:35?
有啥發現沒?
54樓 解決方案
作者:? hecong129? ?? 時間:? 2013-11-13 10:26
mark 一下? ?新人學習了
作者:? gotearcom? ?? 時間:? 2013-11-15 15:28
kayatsl 發表于 2013-11-1 15:35?
問題解決方案如上..??至今測試正常..
正在做的項目也用到lwip協議棧,也遇到你貼中死機的問題,能否發一下解決的相關代碼,非常感謝!
作者:? gotearcom? ?? 時間:? 2013-11-15 15:29
郵箱:? gotearcom@163.com
作者:? kinsno? ?? 時間:? 2013-11-15 16:23
kayatsl 發表于 2013-11-1 15:40?
54樓 解決方案
我頂。。。。。真不錯啊。。。。你算是第一個把LWIP進行商用了嗎,真正的用于產品上了的吧。
作者:? kayatsl? ?? 時間:? 2013-11-18 11:02
kinsno 發表于 2013-11-15 16:23?
我頂。。。。。真不錯啊。。。。你算是第一個把LWIP進行商用了嗎,真正的用于產品上了的吧。 ...
我肯定不是第一個..? ?很早就有人商用了其實.. 只不過國內拿出來開帖探討的不是很多...
作者:? kayatsl? ?? 時間:? 2013-11-18 11:06
gotearcom 發表于 2013-11-15 15:28?
正在做的項目也用到lwip協議棧,也遇到你貼中死機的問題,能否發一下解決的相關代碼,非常感謝! ...
死機方式像, 不代表就是這種死法... 你得設陷阱跟蹤...
我改棧加入的代碼基本都貼出來了.. 其余代碼都是產品上的了, 需保密..
作者:? ljt80158015? ?? 時間:? 2013-11-18 16:11
學習了 !? ? 謝謝樓主!
作者:? kinsno? ?? 時間:? 2013-11-18 22:52
kayatsl 發表于 2013-11-18 11:02?
我肯定不是第一個..? ?很早就有人商用了其實.. 只不過國內拿出來開帖探討的不是很多... ...
你們的項目是做設備端的嗎?
我們最近也要上一從機設備端,LWIP源碼里需要改動的多嗎?需要全部啃過去嗎?還是可以直接DEBUG仿到哪改哪!當然改前,肯定要了解前生今世才可以改的啊。之所以這樣問,是因為啃過一次UCOS的代碼,就不愿意再肯其它代碼了,實在是太傷神了。
作者:? kayatsl? ?? 時間:? 2013-11-19 09:43
kinsno 發表于 2013-11-18 22:52?
你們的項目是做設備端的嗎?
我們最近也要上一從機設備端,LWIP源碼里需要改動的多嗎?需要全部啃過去嗎 ...
是設備端..
其實很多問題在國外論壇上一般能找到答案的..
只是這個問題我搜了好久都沒找到問題最根源的引起原因, 所以才寫個帖, 記錄一下跟蹤過程
基本上你遇到問題,只要找準問題的關鍵字, 一搜就很多了.. 必要時,有些國外論壇要出國加速才能打開那些頁面..
作者:? kinsno? ?? 時間:? 2013-11-19 09:47
kayatsl 發表于 2013-11-19 09:43?
是設備端..
其實很多問題在國外論壇上一般能找到答案的..
3KS, 好久不番墻了.
作者:? jzhang123? ?? 時間:? 2013-12-13 09:35
樓主牛B!!!!
作者:? migrant? ?? 時間:? 2013-12-13 10:05
查錯精神不錯,學習學習
作者:? migrant? ?? 時間:? 2013-12-13 10:15
你的err_handle應該能收到這個錯誤啊,怎么沒有處理?
作者:? yangzi8000? ?? 時間:? 2014-2-19 22:21
這個帖子 很高級
作者:? skyformat? ?? 時間:? 2014-2-21 21:45
這個難度很大啊謝謝樓主分享經驗之談
作者:? 右手戒指? ?? 時間:? 2014-3-10 14:35
表示講的比較詳細深入,值得學習
作者:? 有點囂張? ?? 時間:? 2014-3-20 15:28
根據樓主的分析是pcb被釋放了2次,而多出來的那一次釋放是因為lz在應用層調用abort導致了,那么為什么不在應用層避免使用abort來解決這個問題。而要在free函數中來避免呢?
作者:? kayatsl? ?? 時間:? 2014-3-20 19:56
有點囂張 發表于 2014-3-20 15:28
根據樓主的分析是pcb被釋放了2次,而多出來的那一次釋放是因為lz在應用層調用abort導致了,那么為什么不在 ...
確實, 應用層能解決這問題
如果僅僅是個人項目的話, 完全可以在應用層解決,
但如果是開放性項目, 不排除別人以后用你的棧也會出現同樣情況
從底層出發保證穩定是絕對的穩定可靠
就像stm32的編程, 出現任何意外地址讀取寫入, 直接給你拋個hardfault, 而不是等你盲匆匆地去找.
作者:? 有點囂張? ?? 時間:? 2014-3-23 18:09
kayatsl 發表于 2014-3-20 19:56
確實, 應用層能解決這問題
如果僅僅是個人項目的話, 完全可以在應用層解決,
第一種方案,判斷memp->next == NULL 不成功的原因應該是,釋放了這個memp并不是把各個成員變量賦值為NULL,而是將這個memp放回到對應的空閑鏈表中,第二次釋放的時候memp-> next的值應該是memp_tab的地址,而不為NULL,所以還是會進行第二次釋放。
第二種方案,預留了MEMP_SIZE,這種方案可行,但是也要保證flag的值與memp_tab的值不相等;
作者:? kayatsl? ?? 時間:? 2014-3-24 10:04
有點囂張 發表于 2014-3-23 18:09
第一種方案,判斷memp->next == NULL 不成功的原因應該是,釋放了這個memp并不是把各個成員變量賦值為NUL ...
這分析對,?
第二個, 內存地址范圍已知的, 所以只要flag隨便賦一個不挨邊的地址就可以了.
作者:? fengyunyu? ?? 時間:? 2014-4-3 16:15
本帖最后由 fengyunyu 于 2014-4-3 16:17 編輯?
今天,認真看了lz的分析過程,貌似沒有說出現問題的原因,只是在底層加了些容錯代碼。另請教lz,if (type == 2)中,2代表什么?還有就是,lz應用層代碼是不是寫法有問題,才導致出現該問題?
作者:? kayatsl? ?? 時間:? 2014-4-3 17:28
fengyunyu 發表于 2014-4-3 16:15
今天,認真看了lz的分析過程,貌似沒有說出現問題的原因,只是在底層加了些容錯代碼。另請教lz,if (type = ...
原因總結就是一句話.??abort 被調用了2次. 一次是我調的, 一次是內核調的
作者:? fengyunyu? ?? 時間:? 2014-4-3 20:20
kayatsl 發表于 2014-4-3 17:28
原因總結就是一句話.??abort 被調用了2次. 一次是我調的, 一次是內核調的
要是內核在進行abort的時候,有一個回調函數通知應用層就好了。
作者:? fengyunyu? ?? 時間:? 2014-4-30 15:13
http://www.amobbs.com/thread-4369748-1-1.html
”我移植的是裸機版的lwip,在程序跑的過程中發現有時候會產生死機的情況
檢查發現停在了類似下面的地方
此處是void tcp_fasttmr(void)中的一句代碼
/* send delayed ACKs */
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next)
在這里tcp_active_pcbs和tcp_active_pcbs->next會出現相等的情況,然后for循環就回移植循環下去。抓狂中……
經過測試可能和tcp連接建立和斷開有關系,但是找不到問題的根源。請問有誰碰到過類似的情況?是怎么解決的呀。“
這個帖子中的問題,LZ遇到過么?
作者:? fengyunyu? ?? 時間:? 2014-4-30 15:22
http://blog.csdn.net/hecong_kit/article/details/24415693
這里貌似有一個新的類似問題的解決方案。
作者:? ywhbn? ?? 時間:? 2014-4-30 15:28
用兩句話總結下?
作者:? kayatsl? ?? 時間:? 2014-4-30 15:36
fengyunyu 發表于 2014-4-30 15:13
http://www.amobbs.com/thread-4369748-1-1.html
”我移植的是裸機版的lwip,在程序跑的過程中發現有時候 ...
lwip 里面有很多處 for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next)??的地方
void tcp_fasttmr(void) 只是其中一處
但不管死在哪處,??只要鏈表鏈回自身 基本都可以歸為同一個問題.
你下面貼的那個, 粗略看了下, 跟我這個問題貌似關系不大
作者:? fengyunyu? ?? 時間:? 2014-4-30 16:21
kayatsl 發表于 2014-4-30 15:36
lwip 里面有很多處 for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next)??的地方
void tcp_fast ...
請問54樓圖中memp_use_flag是自定義的一個常量么?另外,memp_free中,跳到Free_Already后,如何處理的,直接return么?
作者:? fengyunyu? ?? 時間:? 2014-4-30 17:12
另外,LZ把tcp_abort(clippcb)改成tcp_close(clippcb)估計就對了。
作者:? fengyunyu? ?? 時間:? 2014-5-1 01:05
http://blog.sina.com.cn/s/blog_5d18cf7f010100nj.html
這里貌似有一個正確的答案。
作者:? kayatsl? ?? 時間:? 2014-5-3 11:01
fengyunyu 發表于 2014-5-1 01:05
http://blog.sina.com.cn/s/blog_5d18cf7f010100nj.html
這里貌似有一個正確的答案。
你這個貼就是我為什么用 abort 而不用close的原因, 但不是我這個問題的正解
作者:? fengyunyu? ?? 時間:? 2014-5-3 14:00
kayatsl 發表于 2014-5-3 11:01
你這個貼就是我為什么用 abort 而不用close的原因, 但不是我這個問題的正解
可能,用close更合適些。close后,pcb的釋放是由lwip自己進行。應用層,重新進行tcp_new應該就行了。
作者:? kayatsl? ?? 時間:? 2014-5-4 09:23
close 和 abort 最大的區別是 close會發送關閉請求給對方, 然后等到對方關閉的ack后才會真正釋放掉pcb
但我的函數是用在連接的時候的, 連接還沒真正建立, 是不需要發送關閉請求的, 可以直接abort
作者:? kayatsl? ?? 時間:? 2014-5-4 09:23
fengyunyu 發表于 2014-5-3 14:00
可能,用close更合適些。close后,pcb的釋放是由lwip自己進行。應用層,重新進行tcp_new應該就行了。 ...
close 和 abort 最大的區別是 close會發送關閉請求給對方, 然后等到對方關閉的ack后才會真正釋放掉pcb
但我的函數是用在連接的時候的, 連接還沒真正建立, 是不需要發送關閉請求的, 可以直接abort
喔.. 忘了按回復來發.. 重發一條吧...
作者:? fengyunyu? ?? 時間:? 2014-5-4 10:16
kayatsl 發表于 2014-5-4 09:23
close 和 abort 最大的區別是 close會發送關閉請求給對方, 然后等到對方關閉的ack后才會真正釋放掉pcb
...
直接用tcp_abort可能有些問題,以下是tcp_abort()的部分代碼:
...
??if (pcb->state == TIME_WAIT) {
? ? tcp_pcb_remove(&tcp_tw_pcbs, pcb);
? ? memp_free(MEMP_TCP_PCB, pcb);
??} else {
? ? seqno = pcb->snd_nxt;
? ? ackno = pcb->rcv_nxt;
? ? ip_addr_copy(local_ip, pcb->local_ip);
? ? ip_addr_copy(remote_ip, pcb->remote_ip);
? ? local_port = pcb->local_port;
? ? remote_port = pcb->remote_port;
#if LWIP_CALLBACK_API
? ? errf = pcb->errf;
#endif /* LWIP_CALLBACK_API */
? ? errf_arg = pcb->callback_arg;
? ? tcp_pcb_remove(&tcp_active_pcbs, pcb);
? ? if (pcb->unacked != NULL) {
? ?? ?tcp_segs_free(pcb->unacked);
? ? }
? ? if (pcb->unsent != NULL) {
? ?? ?tcp_segs_free(pcb->unsent);
? ? }
#if TCP_QUEUE_OOSEQ? ??
? ? if (pcb->ooseq != NULL) {
? ?? ?tcp_segs_free(pcb->ooseq);
? ? }
#endif /* TCP_QUEUE_OOSEQ */
? ? memp_free(MEMP_TCP_PCB, pcb);
? ? TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
? ? if (reset) {
? ?? ?LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
? ?? ?tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
? ? }
代碼中的 tcp_pcb_remove(&tcp_active_pcbs, pcb);可能會出現問題。
作者:? fengyunyu? ?? 時間:? 2014-5-4 10:19
kayatsl 發表于 2014-5-4 09:23
close 和 abort 最大的區別是 close會發送關閉請求給對方, 然后等到對方關閉的ack后才會真正釋放掉pcb
...
tcp_close則處理了連接還沒有真正建立的情況,tcp_close的部分代碼如下:
...
??switch (pcb->state) {
??case CLOSED:
? ? /* Closing a pcb in the CLOSED state might seem erroneous,
? ???* however, it is in this state once allocated and as yet unused
? ???* and the user needs some way to free it should the need arise.
? ???* Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
? ???* or for a pcb that has been used and then entered the CLOSED state?
? ???* is erroneous, but this should never happen as the pcb has in those cases
? ???* been freed, and so any remaining handles are bogus. */
? ? err = ERR_OK;
? ? if (pcb->local_port != 0) {
? ?? ?TCP_RMV(&tcp_bound_pcbs, pcb);
? ? }
? ? memp_free(MEMP_TCP_PCB, pcb);
? ? pcb = NULL;
? ? break;
其中TCP_RMV(&tcp_bound_pcbs, pcb)處理了連接還沒有真正建立的情況。
作者:? kayatsl? ?? 時間:? 2014-5-4 16:52
fengyunyu 發表于 2014-5-4 10:19
tcp_close則處理了連接還沒有真正建立的情況,tcp_close的部分代碼如下:
...
其實你是沒有搞懂我上面描述的問題在哪.
其實問題在于儲存pcb的地方, 系統自身釋放了一次, 而我不知道, 變量存在我身上, 而我又再釋放一次.. 所以就卡擦了..
這跟調哪個函數是沒關系的, 最終都是mempfree
作者:? fengyunyu? ?? 時間:? 2014-5-4 17:06
kayatsl 發表于 2014-5-4 16:52
其實你是沒有搞懂我上面描述的問題在哪.
其實問題在于儲存pcb的地方, 系統自身釋放了一次, 而我不知道,??...
你在mem_free中加了補丁,這個是看到了。我是覺得,你在應用層對raw_api的使用“方法”可能有問題,引起了這個“問題”。
作者:? kayatsl? ?? 時間:? 2014-5-4 17:13
fengyunyu 發表于 2014-5-4 17:06
你在mem_free中加了補丁,這個是看到了。我是覺得,你在應用層對raw_api的使用“方法”可能有問題,引起 ...
上面回帖已經說了,??問題就在于 "變量存在我身上"
而不是調了哪個函數
作者:? fengyunyu? ?? 時間:? 2014-5-5 11:06
貌似老外使用lwip 1.4.1,raw模式也碰到類似問題。
以下轉自網絡:
lwip tcp_tw_pcbs list problem in tcp_slowtmr()
I have been having a problem in the tcp_slowtmr() function in tcp.c.??I have been using the raw api for a quite a while to implement TCP servers listening on several different ports.??I have not really had any problems so far.??Recently I have also implemented TCP client connection which quickly open up a TCP client connection read/write some data and then close the connection.??This sequence repeats itself over and over connecting to several different remote TCP servers.??Things seem to work fairly well until I start introducing some error conditions like extending remote server resonse times or reducing my timeouts waiting for data to be received.??This causes my code to timeout and close the TCP connection (probably while some responses may come back in later).??I have also tried disconnecting some of my remote servers network connections so that the initial client connection attempts will fail.??I am just mainly trying to do some general stress testing with normal conditions that may occur when deploying the application.?
The problem that I am having occurs when adding these additional stress tests, and possibly with normal conditions after an extended period of time.??I have not nailed down the exact cause as of yet.??I finally get into a lock up condition when calling the tcp_slowtmr() function.??The lockup occurs cycling through the code lines highlighted below from the tcp_slowtmr() function:
??/* Steps through all of the TIME-WAIT PCBs. */
prev = NULL;
??pcb = tcp_tw_pcbs;
??while (pcb != NULL) {
? ? LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
? ? pcb_remove = 0;
? ? /* Check if this PCB has stayed long enough in TIME-WAIT */
? ? if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
? ?? ?++pcb_remove;
? ? }
? ?
? ? /* If the PCB should be removed, do it. */
? ? if (pcb_remove) {
? ?? ?struct tcp_pcb *pcb2;
? ?? ?tcp_pcb_purge(pcb);
? ?? ?/* Remove PCB from tcp_tw_pcbs list. */
? ?? ?if (prev != NULL) {
? ?? ???LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
? ?? ???prev->next = pcb->next;
? ?? ?} else {
? ?? ???/* This PCB was the first. */
? ?? ???LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
? ?? ???tcp_tw_pcbs = pcb->next;
? ?? ?}
? ?? ?pcb2 = pcb;
? ?? ?pcb = pcb->next;
? ?? ?memp_free(MEMP_TCP_PCB, pcb2);
? ? } else {
? ?? ?prev = pcb;
? ?? ?pcb = pcb->next;
? ? }
??}
The problem occurs when the first item on the tcp_tw_pcbs list points back to itself:??pcb->next == pcb, so the code never exits the while loop.??The tcp_ticks value never changes in this part of the code so pcb_remove is never set > 0 either.
This is a single threaded application and only the standard interrupt handling function are being used.??This is an application running on an LPC4350 using the LPCOpen library from NXP with lwip v1.4.1.
It seems like the problem is created if I start calling tcp_close() to close my client connections.??If I use tcp_abort() instead then I don’t seem to have to problem – It does however cause undesirable sequences in wireshark.?
What is the recommended sequence to close a tcp client session using the raw api?
Any suggestions as to what I may be doing wrong here or could this possibly be a bug that has been seen before in lwip?
Thanks,
Greg Dunn
作者:? hxke? ?? 時間:? 2014-6-5 17:30
收藏,頂起。
作者:? jsszdfdn? ?? 時間:? 2014-9-17 21:45
樓主的分析實在精辟,佩服。
作者:? embeddev_1? ?? 時間:? 2014-9-22 20:59
mark!!!
作者:? xaper? ?? 時間:? 2014-9-22 21:17
好詳細 ?
| 歡迎光臨 amoBBS 阿莫電子論壇 (https://www.amobbs.com/) | Powered by Discuz! X3.4 |
總結
以上是生活随笔為你收集整理的记一次lwip中 遇到 pcb == pcb-next 的pcb死循环debug过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lwip之数据收发流程_3
- 下一篇: FreeRTOS — 临界段和开关中断