LWIP一探究竟

news2024/10/6 18:29:46

1.网卡接收数据的流程

我们网卡接收数据基本上就是开发板上eth接收完数据后产生一个中断,然后释放一个信号量通知网卡接收线程去处理这些接收的数据,然后将这些数据封装成信息,投递到tcpip_mbox邮箱中,LWIP内核线程得到这个消息,就对消息进行解析,根据消息中数据包类型进行处理,实际上是调用ethernetif_input()函数决定是否递交到IP层,如果是ARP包,内核就不会递交给IP层,而是更新ARP缓存表,对于IP数据包则递交给IP层去处理,这就是一个数据从网卡到内核的过程.

2.内核超时处理

在LWIP中很多时候都要用到超时处理,例如ARP缓存表项的时间管理,IP分片数据报的重装等待超时,TCP中的建立连接超时,重传超时机制等,因此超时处理的实现是TCP/IP协议栈中一个重要部分,Lwip为每个与外界网络连接的任务都有设定了timeout属性,即等待超时时间,超时处理的相关代码在timeouts.c与timeout.h中.

2.1sys_timeo结构体与超时链表

LWIP通过一个sys_timeo类别的数据结构管理与超时链表相关的所有超时事件.

LWIP使用这个结构体记录下内核中所有被注册的超时事件,这些结构体会以链表的形式一个个连接在超时链表中,而内核中只有一个超时链表,那么怎么对超时链表进行管理呢?

LWIP定义了一个sys_timeo类型的指针next_timeout,并且将next_timeout指向当前内核中链表头部,所有被注册的超时事件都会按照被处理的先后顺序排列在超时链表中.sys_timeo结构体与超时链表源码如下图

(1) 指向下一个超时事件的指针,用于超时链表的连接

(2)当前超时事件的等待时间

(3)指向超时的回调函数,该事件超时后就执行对应的回调函数

(4)向回调函数传入参数

(5)指向超时链表第一个超时事件.

2.2注册超时函数

LWIP虽然使用超时链表进行管理所有的超时事件,那么它首先需要知道有哪些超时事件才能去管理,而这些超时事件就是通过注册的方式被挂载在链表上,简单来说就是这些超时事件要在内核中登记一下,内核才会去处理.

LWIP中注册超时事件的函数是 sys_timeout()

实际上调用的是sys_timeout_abs()函数

(1)根据当前时间计算出超时的时间,然后调用sys_timeout_abs()函数将当前事件插入超时链表

(2)从内存池中申请一个 MEMP_SYS_TIMEOUT 类型内存,保存对应超时事件的相关信息.

(3)填写对应的超时事件信息,超时回调函数,函数参数,超时的时间

(4)如果超时链表中没有超时事件,那么新添加的事件就是链表的第一个

(5)如果新插入的超时事件比链表上第一个事件的时间短,则将新插入的超时事件设置成链表的第一个

(6)否则就是遍历链表,寻找合适的插入节点,超时链表根据超时事件的升序排列  

在timeouts.c中,有一个名字为lwip_cyclic_timer的结构,Lwip使用该结构存放了其内部使用的循环超时事件.这些超时事件在Lwip初始化时通过函数sys_timeouts_init()调用定时器注册函数sys_timeout()注册进入超时链表中,lwip_cyclic_timer的结构具体见代码

lwip_cyclic_timer数组中存放了每个周期性的超时事件回调函数及超时时间,在LWIP初始化的时候就将这些事件一个个插入超时链表中,具体如下

在每个 sus_timeo结构体中有着一个成员,函数指针,一直记录的对应的超时回调函数,对于周期性的回调函数,LWIP在他们初始化的时候将他们注册到lwip_cyclic_timer()函数中,每次处理回调函数之后,就调用sys_timeout_abs()函数将其重新注册到超时链表中

2.3超时检查

其实前面的超时处理,在实际开发中,我们用户基本不用考虑的,但是学一下做好铺垫也是很重要,我们既然有超时处理,那肯定我们都可以堆砌进行超时检查并且去处理,LWIP中以下两个函数可以实现对超时的处理

void sys_check_timeouts(void):这是用于裸机的函数,用户需要在裸机应用程序中周期性调用该函数,每次调用的时候LWIP都会检查超时链表上的第一个sys_timo结构体是否到期,如果没有到期,直接退出该函数,否则.执行sys_timeo结构体中对应的超时回调函数,并从链表上删除它,然后继续检查下一个sys_timeo结构体,直到sys_time结构体没有超时才退出.

tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox,void** msg):这个函数在操作系统的线程中循环调用,主要是等待tcpip_mbox信息,是可阻塞的,如果在等待tcpip_mbox的过程中发生超时事件,则会同时执行超时事件处理,即调用超时回调函数.LWIP是这样子处理的,如果已经发生超时,LWIP就会内部调用sys_check_timeouts()函数去检查超时的sys_timeo结构体并调用其对应的回调函数,如果没有发生超时,那就一直等待消息,其等待的事件为下一个超时时间的时间,一举两得.LWIP中tcpIP线程就是靠这种方法,即处理了上层及底层的tcpip_mbox消息,同时处理了所有需要超时处理的事件.

(1)调用sys_timeouts_sleeptime()函数得到距离事件超时的时间并保存在sleeptime变量中.

(2)如果sleeptime为SYS_TIMEOUTS_SLEEPTIME_INFINITE ,表示当前系统没有超时函数,那只要一直等待mbox消息就行了,所以调用sys_arch_mbox_fetch()函数进行等待消息,等待时间是一直等待.

(3)如果sleeptime为0表示已经发生超时了,那就调用sys_check_timeouts()去检查以下到底是哪个事件发生超时并且去处理其超时回调函数.

(4)对于其他时间,LWIP就在等待tcpip_mbox的消息的同时就去处理超时事件,等待tcpip_mbox的消息的时间为sleeptime,然后在时间到达的时候就处理超时事件.如果接收到消息,并且超时时间还没到,那就去处理tcpip_mbox的消息,然后再回来重新计算等待时间sleeptime,如此反复,这样子既不会错过tcpip_mbox的消息,也不会错过超时的事件


3.tcpip_thread线程

Lwip在操作系统的环境下,Lwip内核是作为操作系统的一个线程运行,在协议栈初始化的时候就会创建tcpip_thread线程,那么这个线程如下图所示:

(1)Lwip将函数tcpip_timeouts_mbox_fetch()定义为带参宏TCPIP_MBOX_FETCH,所以在这里就是等待消息并且处理超时事件.

(2)如果没有等到消息就继续等待

(3)等待到消息就对消息进行处理,    tcpip_thread_handle_msg(msg);这个函数如下图:

(1)(2) 根据消息中的不同类型,进行不同的处理,对于TCPIP_MSG_API类型,就执行对应的API函数

(3)对于 TCPIP_MSG_INPKT 类型,直接交给ARP层处理

(4)对于 TCPIP_MSG_TIMEOUT类型,表示上层注册一个超时事件,直接执行注册超时事件即可

(5)相反的,对于TCPIP_MSG_UNTIMEOUT,表示上层删除一个超时事件,直接执行删除事件即可

(6)(7) 对于 TCPIP_MSG_CALLBACK 或者是 TCPIP_MSG_CALLBACK_STATIC 类型,表示上层通过回调方式执行一个回调函数,那么执行对应的回调函数即可.

4.LWIP中的信息

主要是讲数据包消息与API消息,整个内核的运作都要依赖他们.

4.1消息结构

从前面的章节,我们知道消息有多种类型,Lwip中消息是有多种结构的,对于不同的消息类型其封装是不一样的,我们上面的tcpip_thread线程是通过tcpip_msg描述消息的,

tcpip_thread线程接收到消息后,根据消息的类型进行不同的处理

Lwip中使用tcpip_msg_type枚举类型定义了系统中可能出现的消息的类型,消息结构msg字段是一个共用体,其中定义了各种消息类型的具体内容,每种类型的消息对应了共用体中的一个字段,其中定义了各种消息类型的具体内容,每种类型的信息对应了共用体中的一个字段,其中注册与删除事件的消息使用了同一个tmo字段.LWIP中的API相关的信息内容很多,不适合直接放在tcpip_msg中,所以Lwip用一个api_msg结构体来描述API消息,在tcpip_msg中只存在指向api_msg结构体的指针.

(1) 消息的类型,目前有 7 种

(2) API消息主要由两部分组成,一部分是用于表示内核执行的API函数,另一部分是执行函数时候的参数,都会被记录在api_msg中

(3)与API消息差不多,也是由两部分组成,一部分是tcpip_api_call_fn类型的函数,另一部分是其对应的形参,此外还有用于同步的信号量.

(4)Inp 用于记录数据包信息的内容,p指向接收到的数据包,netif表示接收到数据包的网卡,input_fn表示输入的函数接口,在tcpip_inpkt进行配置.

(5)cb用于记录回调函数与其对应的形参

(6)tmo用于记录超时相关信息,如超时的时间,如超时回调函数,参数等

4.2数据包信息

对于每种类型的信息,Lwip内核都必须有一个产生与之对应的消息函数,在产生该类型的信息后就将其投递到系统邮箱tcpip_mbox中,这样子tcpip_thread线程就会从邮箱中得到信息并且处理,从而使内核完美运作,

从这个图中我们可以发现对应数据包的消息,是通过tcpip_input()函数对消息进行构造并且投递的,但是真正执行这些操作的函数时tcpip_inpkt(),

(1)调用tcpip_inpkt()函数将ethernet_input()函数作为结构体的一部分传递给内核,然后内核接收到这个数据包就调用该函数.

(2)申请存放消息的内存空间

(3)构造信息,消息的类型就是数据包信息,初始化消息结构中msg共用体的inp字段,p指向数据包

网卡就是对应的网卡,处理的函数就是我们熟悉的ethernet_input()函数

(4)构造信息完成,就调用sys_mbox_trypost进行投递信息,这其实就是对操作系统的API简单封装,如果成功就是ERR_OK

(5)如果投递失败,就释放对应的消息结构空间

总的来说,我们无论是裸机系统 还是 操作系统 都是通过ethernet_input()函数去处理接收到的数据包,只不过在操作系统中,我们分成两个线程处理 ,接收线程只负责接收数据包,构造信息,最后投递,内核线程再去处理 这样子 可以同步进行更加高效.

4.3 API信息

Lwip使用api_msg结构体描述一个API信息的内容

api_msg只包含了三个字段,描述连接信息的com,内核返回的执行结果err,还有mag,

mag是一个共用体,根据不一样的API接口使用不一样的数据结构.在conn中,他保存了当前连接的重要信息,如信号量,邮箱等,lwip_netconn_do_xxx(xxx表示不一样的NETCONN API接口)类型的函数执行需要用这些信息来完成与应用线程的通信与同步,内核执行lwip_netconn_do_xxx类型的函数返回结果会被记录在err中,msg的各个产业记录各个函数执行时需要的详细参数.

我们了解底层的数据包信息,那么同理对于上层的API函数,想要与内核进行数据交互,也是通过Lwip的消息机制,API消息由用户线程发出,与内核进行交互,因为用户的应用程序并不是与内核处于同一线程中,简单来说就是用户使用NETCONN API接口的时候,Lwip会将对应API函数与参数构造成消息造成信息传递到tcpip_thread线程中,然后根据对应的API函数执行对应的操作.

与数据包信息类似,也是有独立的API消息投递函数去处理,那就是netconn_apimsg()函数,在NTCONN API中构造完成数据包,就会调用netconn_apimsg()函数进行投递信息

(1)根据netconn_bind()传递的参数初始化 api_msg结构体.

(2)调用neticonn_apimsg()函数投递这个api_msg结构体,这个函数实际上时调用tcpip_send_msg_wait_sem()函数投递API消息的,并且等待tcpip_thread线程的回应

(3)构造API信息,类型为TCPIP_MSG_API,函数为API对应的函数lwip_netconn_do_bind,将msg的指针指向api_msg结构体

(4)调用sys_mbox_post()函数向内核进行投递信息

(5)同时调用sys_arch_sem_wait()函数等待信息处理完毕

总的来说,用户的应用线程与内核也是相互独立的,依赖操作系统的ICP通信机制进行数据交互与同步(邮箱,信号量等),LWIP提供上层NETCONN API接口,会自动帮我们处理这些事情,只需要我们根据API接口传递正确的参数接口就好

其实这个运作示意图不是最好的,这种运作的方式在每次发送数据的时候,会进行一次线程的调度,这无疑是增大了系统的开销,而将LWIP_TCPIP_CORELOCKNG宏定义设置为1则无需操作系统邮箱与信号量的参与,直接在用户线程中通过回调函数调用对应的处理,当然在这个过程中,内核线程是无法获得互斥量而运行的,因为是通过互斥量进行保护用户线程的处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1583439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker使用canal

1. 准备MySql主库 1.1.在服务器新建文件夹 mysql/data,新建文件 mysql/conf.d/my.cnf 其中my.cnf 内容如下 [mysqld] log_timestampsSYSTEM default-time-zone8:00 server-id1 log-binmysql-bin binlog-do-db mall # 要监听的库 binlog_formatROW配置解读&#…

Harmony鸿蒙南向驱动开发-Regulator

Regulator模块用于控制系统中各类设备的电压/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过Regulator关闭其电源…

网络基础三——其他周边问题

3.1ARP原理 ​ ARP不是一个单纯的数据链路层的协议,而是一个介于数据链路层和网络层之间的协议; ​ 以广播的形式(主机号填成全1)构建Mac帧,发送ARP请求包,告诉所有在局域网的主机我的IP地址和Mac帧,与目的IP相同的主…

意得辑意得辑

你是否也曾遇到过在发表论文时英语写作水平不尽如人意的困境?审稿意见总是指出语言表达不够好,需要找英语母语者修改?不用担心,我和你一样,也曾历经这样的挑战。但是,我找到了一家值得信赖的专业润色机构—…

深入分析Linux上下文与上下文切换

Linux 进程运行空间与特权等级 在 Linux 操作系统中,进程的运行空间被划分为内核空间和用户空间,这种划分是为了保护系统的稳定性和安全性。这两个空间对应着 CPU 的特权等级,分别为: Ring 0(内核态)Ring…

全国青少年劳动技能与智能设计大赛安徽省赛区赛前培训在肥东六中顺利举办

为推进我省青少年劳动教育和素质教育高质量发展,提升青少年家国情怀、多元思维、劳动技能、创新能力和综合素质,发现和培养创新后备人才。4月10日,肥东县教体局联合安徽省青少年劳动技能与智能设计大赛组委会在肥东六中创新科教基地成功举行全…

人工智能分类算法概述

文章目录 人工智能主要分类算法决策树随机森林逻辑回归K-均值 总结 人工智能主要分类算法 人工智能分类算法是用于将数据划分为不同类别的算法。这些算法通过学习数据的特征和模式,将输入数据映射到相应的类别。分类算法在人工智能中具有广泛的应用,如图…

目标检测——车牌图像数据集

一、重要性及意义 车牌图像识别的重要性及意义主要体现在以下几个方面: 智能交通管理:车牌图像识别技术是智能交通系统(ITS)的核心组成部分。通过自动识别车辆车牌,可以实现对交通违章行为的自动记录和处理&#xff…

SSD涨价停不下来!

随着HBM内存产能短缺问题的出现,存储市场正遭遇另一波供应短缺。在2021年存储市场陷入低迷后,SSD价格已连续下滑约两年。面对市场变化,存储厂商减少了NAND闪存的生产。随着减产策略的有效执行,需求部分回升,导致SSD供应…

jni 开发 调用dll 函数的流程

jni 调用dll方法以及dll内调用java方法的流程 编写java类 public class abc{static{System.loadLibrary("abc.dll");}public String getResponse(String ReqStr) {return "ok";}public native void InitDiagObj();public native void CarryabcEntry(Stri…

C++的并发世界(十一)——线程池

0.线程池的概念 1.线程池使用步骤 ①初始化线程池:确定线程数量,并做好互斥访问; ②启动所有线程 ③准备好任务处理基类; ④获取任务接口:通过条件变量阻塞等待任务 2.atomic原子操作 std:atomic是C11标准库中的一个…

双碳目标下太阳辐射预报模式【WRF-SOLAR】及改进技术在气象、农林、电力等相关领域中的应用

太阳能是一种清洁能源,合理有效开发太阳能资源对减少污染、保护环境以及应对气候变化和能源安全具有非常重要的实际意义,为了实现能源和环境的可持续发展,近年来世界各国都高度重视太阳能资源的开发利用;另外太阳辐射的光谱成分、…

A Note on LoRA

A Note on LoRA 摘要Additional InsightsPractical ImprovementsLooking Ahead 摘要 LoRA已成为一种首选的方法,用以高效地适应大型语言模型(LLM),其简便性和有效性令人瞩目。本文档扩展了原始LoRA论文,提供了最初未讨…

Spring中的适配器模式

在Spring MVC框架中,适配器模式主要体现在对不同类型的处理器(即Controller)的统一处理上。Spring MVC通过适配器模式来保证无论Controller的实现方式如何多样化,都能够被DispatcherServlet统一调用和管理。具体使用方式如下&…

李廉洋:4.10黄金原油晚间走势最新分析及策略。

鉴于美联储官员对1月和2月通胀数据反应不足,3月通胀数据过热可能导致其反应过度的风险更大。美联储试图避免根据一两个数据点来制定政策,但今年迄今为止经济活动的韧性意味着,在年中降息的理由取决于通胀是否恢复自去年下半年以来的稳步下降趋…

uniapp小程序编译报错

说明 微信小程序编译每次都出现[ project.config.json 文件内容错误] project.config.json: libVersion 字段需为 string, 解决 找到manifest.json文件 添加:"libVersion": "latest",重新编译即可。

Redis常见数据类型(2)

目录 String字符串 常见命令 SET GET MGET MSET SETNX 计数命令 INCR INCRBY DECR DECRBY INCRFLOAT 其它命令 APPEND GETRANGE SETRANGE STRLEN String字符串 字符串是Redis最基础的数据类型, 关于字符串需要特别注意: (1)首先Redis中所有的键的类型都是字符…

[leetcode]remove-duplicates-from-sorted-list-ii

. - 力扣(LeetCode) 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 示例 1: 输入:head [1,2,3,3,4,4,5] 输出:[1,2,5]示例 2&…

jvm中jdk常用的几个命令总结

1.jmap 此命令可以用来查询内存信息&#xff0c;实例个数及占用内存大小 1.1 查看堆内存概要信息&#xff08;内存分配统计&#xff09; jmap -histo[:live] <pid> .-histo&#xff1a;显示堆中对象的统计信息&#xff0c;包括每个类的实例数量、占用内存大小等 :live…

互联网产品经理必备知识详解

1. 前言 本文档全面探讨了产品经理在产品管理过程中的关键环节,包括市场调研、产品定义及设计、项目管理、产品宣介、产品市场以及产品生命周期。通过深入剖析这些方面,本文旨在帮助产品经理系统地理解和掌握产品管理的核心要素,从而提升产品开发的效率和成功率。在市场调研…