Linux 释放socket资源,LwIP使用select,close socket资源释放不完全问题

本文详细介绍了在基于LwIP+FreeRTOS环境中遇到的DoIP初始化/反初始化失败问题,问题根源在于LwIP的select函数在socket关闭时未能正确清理select_wait标志位,导致socket资源假耗尽。解决方案是通过同步机制避免在select休眠期间关闭socket。文章深入探讨了LwIP的socket、close和select函数实现,并提供了问题的分析和修复思路。
摘要由CSDN通过智能技术生成

这篇文章本应该在4月就写好的,但是博客评论系统一直没有搭建好,走了很多弯路,现在好了,delay这么久,终于要要补过来了。自建博客:金宝的博客

该文章完全原创,除通用、广泛的知识点外,均为个人总结,如需转载还望备注出处,同时如有错误还请指出,虚心接受。

一、简介

1. 题外话

以这篇文章为第一篇技术文章,一是萌生写博客的契机是换工作,另外就是这篇文章是我在怿星解决的最后一个bug。

问题来源是,跑在基于LwIP+FreeRTOS环境的DoIP,在反复初始化/反初始化时几次之后就会失败了。年初由于任务紧张,检查了下初始化和反初始化函数的流程,改掉了几处可能会出现问题的地方,问题依旧。但是同样的上层处理代码,在windows和linux环境下是没问题的,基本怀疑是LwIP某处不完善引起。一直拖到要离职,终于在离开的最后一天解决了,也算是给在怿星的DoIP协议栈画上一个属于自己的句号。

LwIP 全名为 Light weight IP,意思是轻量化的 TCP/IP 协议, 是瑞典计算机科学院(SICS)的 Adam Dunkels 开发的一个小型开源的 TCP/IP 协议栈。 LwIP 的设计初衷是:用少量的资源消耗(RAM)实现一个较为完整的 TCP/IP 协议栈,其中“完整”主要指的是 TCP 协议的完整性, 实现的重点是在保持 TCP 协议主要功能的基础上减少对 RAM 的占用。此外 LwIP既可以移植到操作系统上运行,也可以在无操作系统的情况下独立运行。

2. 原因

引起该问题的根本原因是,LwIP select函数里如果判断对应的socket没有事件产生(读/写/异常),进行简单处理后则改线程休眠,让出cpu控制权。如果在select休眠期间,进行了close socket的操作,会释放对应的socket pcb(close(socket)是成功的),然后在select休眠结束后,判断该socket资源不存在,则直接退出select函数,但是此时该socket的select_wait标志位没被清除。LwIP在分配socket时(资源都是静态分配的,类似于有一个socket数组,若分配则对应标志位为真),socket是否空闲是会对select_wait该标志位进行判断,所以即使该socket没有被使用,调用socket()函数时也会认为该socket是被占用的,所以几次之后,socket资源被假耗尽。

3. 解决

知道原因后,问题就好解决了。有以下两个解决问题的思路。

1. 更改LwIP源码,对对应的标志位进行判断和清除。该解决方案,如果能够push到LwIP主分支,则是一劳永逸的,否则如果要跟随LwIP官方更新,自己得维护一套代码,并持续merge。

2. 使用者,在使用接口时,做同步。即在select休眠期间不允许进行close socket操作,同时在close socket也不允许进入select函数。所以只要在两个函数之间加上条件判断就好。

考虑到维护成本,最终选择方案2.

二、分析

解决思路在上面已经给出,下面主要想从源码级对问题进行分析。原因中,涉及三个函数,

1. socket函数,即lwip_socket,函数原型如下:

int lwip_socket(int domain, int type, int protocol)

2. close函数,即lwip_close,原型如下:

int lwip_close(int s)

3. select函数, 即lwip_select(),原型如下:

intlwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout)

1. 拓展

LwIP本身提供了类似于bsd socket编程模型,同时也实现了简易版的select函数。

关于socket编程的教程是实在太多了,在这不再重复去描述,socket编程参考链接。辅导过一些人进行socket编程,初学者包括我自己,容易忽略的一点就是,作为server时,listen-socket和accept-socket不是一回事。可以理解为listen-socket窗口,窗口只是负责监听有谁要走通道,走哪个通道,并把真正的通道--accept-socket给到上层。对于其他的,感觉跑跑示例程序,单步走一下,就基本理解了。

在不使用select时,并没有发现socket资源释放不完全的问题。本文不展开讲解lwip select的实现,但是对于select的使用需要稍微展开下,select编程参考链接。关于select本质上是一个同步I/O函数,只不过改同步函数可以同时监控多个"IO"通道,所以也称为多路复用。熟悉了上面的socket编程后,如果需要实现多个socket同时通信的话,就应该给每个socket开一个线程,在负载不是特别高的情况下会显得效率特别低,同时线程太多,就不得不考虑资源竞争的问题,如果竞态条件太多,也容易产生问题(多线程资源竞争问题)。多路复用即是用一个线程监听多个通道(描述符),一旦某个描述符就绪(可读、可写或者异常),就通知程序进行相应的读写操作。上庙的描述,看起来select是异步的,

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
lwip是一个轻量级的TCP/IP协议栈,用于嵌入式系统中。它提供了一套API,可以用于在嵌入式设备上进行网络通信。其中,socketlwip中用于进行网络通信的接口之一。 在lwip中,可以使用select函数来实现多路复用,即同时监听多个socket的可读、可写和异常事件。通过select函数,可以在有多个客户端连接时,同时处理多个连接。 根据引用\[1\]中提供的客户端和服务器流程图,可以看出,在lwip中,客户端和服务器之间的通信流程如下: 1. 服务器创建socket,并绑定到指定的IP地址和端口号。 2. 服务器调用listen函数,开始监听客户端的连接请求。 3. 客户端创建socket,并调用connect函数,向服务器发起连接请求。 4. 服务器调用accept函数,接受客户端的连接请求,并创建一个新的socket用于与该客户端进行通信。 5. 服务器使用select函数来监听多个socket的可读事件。 6. 当有客户端发送数据时,服务器通过recv函数接收数据。 7. 服务器处理接收到的数据,并通过send函数向客户端发送响应数据。 8. 客户端使用select函数来监听服务器的响应数据。 9. 当服务器发送响应数据时,客户端通过recv函数接收数据。 10. 客户端处理接收到的数据,并继续发送请求或关闭连接。 根据引用\[2\]和引用\[3\]中提供的信息,可以看出,在使用lwipsocket编程中,可以通过正确的关闭socket来解决服务器阻塞在recv函数处的问题。同时,对于多个客户端连接一台机器的情况,可以采取关闭socket禁止其他连接进来的做法,以避免寄存器的读写互斥问题。 综上所述,lwipsocket编程中可以使用select函数实现多路复用,同时处理多个客户端连接。通过正确关闭socket和限制连接数量,可以解决服务器阻塞和读写互斥的问题。 #### 引用[.reference_title] - *1* *2* [LWIP学习之Socket(应用篇)](https://blog.csdn.net/m0_46577050/article/details/121992022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [LwIPsocket应用--WebServer和Modbus TCP](https://blog.csdn.net/weixin_42131861/article/details/113009586)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值