|
本帖最后由 kayatsl 于 2013-10-11 17:50 编辑
如图中, 在tcp_slowtmr() 中, 历遍 tcp_tw_pcbs 过程中, 其中一个 pcb -> next 指向pcb自身
导致while循环永久死在这里跳不出去, 程序死机.. 这种问题, 虽然说必然会发生, 但发生的时间随机性很强, 很难捕捉到什么时候一定会出现, 所以这种bug很难解决
这种情况在国外论坛搜了下, 都是同一个回答: 当跑os的时候,有多个线程同时争抢就会导致这种问题产生
但到底怎么产生的, 和如何避免, 没有作详细介绍,也没有案例分析..
这现象以前也有遇到过, 其实这种情况通过写一个定时检测是否有pcb指向自身 和加入看门狗, 也能保证程序不会一直死在这,但这次一个偶然的机会,决定跟个所以然.
这次由于时间片之间程序运行的先后顺序不同, 会导致hardfault产生, hardfault没有截图, 系统是记录到我读取一个非法地址, 引致hardfault
从堆栈中跟踪得知, hardfault前跑的函数是 memp_malloc 中 memp_tab[type] = memp->next 引起的, 而memp 自身被指向了一个非内存地址, 再读取 ->next 必然导致出错
蓝色圈是我加入的条件断点, 来捕捉异常状态.
这里得知memp_tab[2] 中的地址指向出问题了, 但这和 pcb的死循环有什么关系呢? 到这里还是看不出头绪.. 思路断了, 那就回去继续跟踪 pcb的问题
既然已经知道死循环的 pcb是属于 tcp_tw_pcbs 链表里面的, 那就 ctrl+shift+f 把所有对 tcp_tw_pcbs 有操作的地方都找出来, 统统下条件断点
看执行到哪一句后会出现死循环的现象
程序果然乖乖的死在陷阱里面, 这样看来, 明显是运行了 tcp_reg 后才出现的现象, 但为什么运行这个后会导致死循环呢? 没什么头绪? 那就跟另一条线索
跟踪一下 memp_tab的情况,
图中红字部分已经说得差不多了, 这里给了我们一个很重要的线索, 就是 memp_tab[]->next 和 tcp_tw_pcbs -> local_ip 为什么会重叠了..
试图跟着这个函数返回一下, 看看会有什么情况
没错, 这里又重新给这个pcb赋值了, 然后又重新递回到上层做处理..
也就是说, 系统给我们分配了一个 正在使用的内存区域, 来处理新的东西, 那必然会导致和旧的东西发生冲突.. 要验证这句话? 继续下条件断点
图中可以看到, 进来的pcb, 本来是属于 active_pcbs 链表的, 刚从 active 链表中移除, 准备放到 timewait链表里面去, 但此时发现, 这个pcb的内存空间,正是tw_pcbs的内存空间
其实 TCP_REG 很简单, 就是把指针指一下, 就注册过去了, 但就是因为太简单了, 没有做任何判断, 所以自己指回了自己也不知道.
其实到这里, 可以在这个定义里面写个判断, 遇到指向自己的就不进行操作, 但这样还是没找到最原始导致的原因.. 所以必须继续跟踪下去
再截一个图, 就是 死循环的图.. 大家可以比较一下这几个图里面红圈圈着的地址, 截图顺序是按照程序执行顺序来的
回想一下, malloc的时候, 分配到同一个地址空间, 那明显就是上一次分配的时候, ->next指针又指回自己了, 验证这个猜想, 继续下条件断点..
果然不出所料, memp == memp->next , 而这个是分配的时候导致的还是释放的时候导致的, 暂时还不确定,
但是这个出错的现场, 可以在堆栈中继续追溯是哪里调用的, 但这里还不是第一现场, 为了还原第一现场, 那就继续跟着 callstack回去找
这图中很有意思.. 这里程序开始的时候, 我使用 tcp_new 创建一个pcb, 而 tcp_new 又调用 malloc 来取出一个 pcb的内存空间
而我在 tcp_abort 这里打了个断点, 按道理来说, 我还没有 abort掉这个链接, 空间是不应该被释放掉的, 但奇怪的是 memp_tab[2] 中指向的地址,确实是我正在使用的地址
memp_tab[] 链表是空闲内存的链表, 而这个内存块是我正在使用的, 怎么会出现在空闲链表中??
越来越接近真相了, 就是这块内存申请后, 在使用中, 意外被别人释放了, 那罪灰祸首到底是谁??
那就继续下条件断点去捕捉释放前的现象,.
图中可以看到, memp_Free 地址是跟 memp_alloc一样的 .. 这里的原因是,我链接很频繁,一个pcb还没完全关闭, 又建立另一个链接, 所以会导致刚刚free出来的一个块, 马上又被用来使用了
所以这两个地址一样并不惊讶, 惊讶的是, 同一个地址, 为什么会出现在 memp_tab[2] 中???
这明显是 free完这个块, 然后 malloc到来用, 但用的过程中, 还没等我释放, 就被别的地方释放了, 所以导致这块正在使用的内存块会跑到了空闲内存块中去..
可以断定是free多了一次出问题了, 就好办了..
由上面的现象可以估计 是pcb的内存空间刚刚分配到, 很短时间又被释放了, 既然是这样, 就在 memp_free中下条件断点 当现在free的,刚好是刚刚malloc出来的内存块就断
因为正常情况下, free的肯定是旧的空间, 不可能刚刚malloc就被free掉的.
等了一段时间, 中陷阱了.. 其实看到这个现场没什么作用, 本来就已经猜到的事情, 而在这里下断点的目的, 是要跟踪, 到底是谁把它释放掉的.
看 call stack 就一目了然了.. 继续跟回去 tcp_input中
这时候清晰了.. 这个pcb收到了关闭请求, 然后关闭掉pcb链接, 顺便把内存区域也 free掉了..
好了, 看到这里, 其实大家往回读就知道到底 pcb死循环是怎么引起的了...
我正在开启一条到服务器的链接, 但在等待确定链接已经保持的过程中, 意外被服务器关闭了, 而由于时间太短, 这时候, 我应用程序认为 服务器并没有连接上, 然后把这个tcp的控制块 abort掉
而 abort 的时候调用 free 内存的函数, 却不知道这块内存其实已经是free掉的了, 再free一次, 就会出现 memp_tab中 元素的 -next 指回自身.
而这时候再 malloc一个新内存空间的时候, 由于 next永远是指回同一块内存区域, 所以导致下次再分配的时候, 还是分配同一块空间给应用层
但应用层不知道分配的是同一块空间, 而继续使用, 然后就导致两条链接共用同一个内存区域.. 但lwip自己并不知道.
当两条链接最终注册入同一个 pcbs链表去管理的时候, 就会导致自己指回自己, 因为本来就是同一块内存,.. 最后就 pcb死循环了..
跟踪到此结束
跟踪完当然是解决问题了..
在 memp_free中 ,free之前做一个判断 由于type == 2 属于 tcp_pcb, 而 tcp_pcb 的 前四个字节是不可能为0x00的,
所以可以判断为当 memp->next 为 NULL 的时候, 这片内存已经释放过了, 就不再重新释放, 直接跳到程序末尾, 解锁
经验证, 方法正常, 因为 出问题通常是 tcp_pcb 的问题,因为 tcp控制太复杂了, 所以目前只控制 type==2 的地方就可以保证程序正常了.
到此 结贴. |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
该献的血还是要献的。你不献他不献。难道让我去献? --- 出自坛友:lovejp1981
|