lwIP 细节之三:errf 回调函数是何时调用的

使用 lwIP 协议栈进行 TCP 裸机编程,其本质就是编写协议栈指定的各种回调函数。将你的应用逻辑封装成函数,注册到协议栈,在适当的时候,由协议栈自动调用,所以称为回调

注:除非特别说明,以下内容针对 lwIP 2.0.0 及以上版本。

向协议栈注册回调函数有专门的接口,如下所示:

tcp_err(pcb, errf);							//注册 TCP 接到 RST 标志或发生错误回调函数 errf
tcp_connect(pcb, ipaddr, port, connected);	//注册 TCP 建立连接成功回调函数 connecter
tcp_accept(pcb, accept);					//注册 TCP 处于 LISTEN 状态时,监听到有新的连接接入
tcp_recv(pcb, recv);						//注册 TCP 接收到数据回调函数 recv
tcp_sent(pcb, sent);						//注册 TCP 发送数据成功回调函数 sent
tcp_poll(pcb, poll, interval);				//注册 TCP 周期性执行回调函数 poll

本节讲述 errf 回调函数。

errf 回调函数

在 TCP 控制块中,函数指针 errf 指向用户实现的 TCP 错误处理函数,当 TCP 连接发送错误时,由协议栈调用此函数。
函数指针 errf 的类型为 tcp_err_fn ,该类型定义在 tcp.h 中:

/** Function prototype for tcp error callback functions. Called when the pcb* receives a RST or is unexpectedly closed for any other reason.** @note The corresponding pcb is already freed when this callback is called!** @param arg Additional argument to pass to the callback function (@see tcp_arg())* @param err Error code to indicate why the pcb has been closed*            ERR_ABRT: aborted through tcp_abort or by a TCP timer*            ERR_RST: the connection was reset by the remote host*/
typedef void  (*tcp_err_fn)(void *arg, err_t err);

从注释得知,错误处理函数在接收到 RST 标志,或者连接意外关闭时,由协议栈调用。
注意,当这个函数调用的时候,TCP 控制块已经释放掉了。

协议栈通过宏 TCP_EVENT_ERR(last_state,errf,arg,err) 调用 errf 指向的错误处理函数,宏 TCP_EVENT_ERR 定义在 tcp_priv.h 中:

#define TCP_EVENT_ERR(last_state,errf,arg,err)                 \do {                                                         \LWIP_UNUSED_ARG(last_state);                               \if((errf) != NULL)                                         \(errf)((arg),(err));                                     \} while (0)

可以看到这个宏的第 4 个参数就是传递给错误处理函数的错误码
以关键字 TCP_EVENT_ERR 搜索源码,可以搜索到 4 处使用:

TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_RST);
TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_CLSD);
TCP_EVENT_ERR(last_state, errf, errf_arg, ERR_ABRT);
TCP_EVENT_ERR(last_state, err_fn, err_arg, ERR_ABRT);

用到了 3 个错误码:ERR_RSTERR_CLSDERR_ABRT ,分别表示连接复位、连接关闭和连接异常。

1.连接复位

当远端连接发送 RST 标志,并且报文序号正确是,调用错误类型为 ERR_RST 的错误处理回调函数,这一过程在 tcp_input 函数中执行。

void
tcp_input(struct pbuf *p, struct netif *inp)
{// 经过一系列检测,没有错误flags = TCPH_FLAGS(tcphdr);	// 这里获取数据包的 [标志] 字段/* 在本地找到有效的控制块 pcb */if (pcb != NULL) {tcp_input_pcb = pcb;err = tcp_process(pcb);		// [标志]中有 RST, 且报文序号正确:recv_flags |= TF_RESET/* A return value of ERR_ABRT means that tcp_abort() was calledand that the pcb has been freed. If so, we don't do anything. */if (err != ERR_ABRT) {if (recv_flags & TF_RESET) {/* TF_RESET means that the connection was reset by the otherend. We then call the error callback to inform theapplication that the connection is dead before wedeallocate the PCB. */TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_RST);tcp_pcb_remove(&tcp_active_pcbs, pcb);tcp_free(pcb);} }} return;
}

tcp_process 函数中关于 RST 标志的判断代码:

static err_t
tcp_process(struct tcp_pcb *pcb)
{/* Process incoming RST segments. */if (flags & TCP_RST) {		// flags 保存数据包的 [标志] 字段,在 tcp_input 函数中取得		/* First, determine if the reset is acceptable. */if (pcb->state == SYN_SENT) {/* "In the SYN-SENT state (a RST received in response to an initial SYN),the RST is acceptable if the ACK field acknowledges the SYN." */if (ackno == pcb->snd_nxt) {acceptable = 1;}} else {/* "In all states except SYN-SENT, all reset (RST) segments are validatedby checking their SEQ-fields." */if (seqno == pcb->rcv_nxt) {acceptable = 1;} else  if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,pcb->rcv_nxt + pcb->rcv_wnd)) {/* If the sequence number is inside the window, we send a challenge ACKand wait for a re-send with matching sequence number.This follows RFC 5961 section 3.2 and addresses CVE-2004-0230(RST spoofing attack), which is present in RFC 793 RST handling. */tcp_ack_now(pcb);}}if (acceptable) {LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));recv_flags |= TF_RESET;tcp_clear_flags(pcb, TF_ACK_DELAY);return ERR_RST;}}
}

2.连接关闭

这是为了处理异常情况。

当接收到远端的 FIN 标志,表示远端要关闭 TCP 连接,此时协议栈会通知应用程序。通常情况下,应用程序应该调用 tcp_close 函数关闭连接。但是,若应用才程序没有执行这个步骤,则 TCP 状态处于 LADT_ACK 并且接收到远端的 ACK 标志后,调用错误类型为 ERR_CLSD 的错误回调函数,简化后的代码为:

void tcp_input(struct pbuf *p, struct netif *inp)
{err = tcp_process(pcb);/* A return value of ERR_ABRT means that tcp_abort() was calledand that the pcb has been freed. If so, we don't do anything. */if (err != ERR_ABRT) {if (recv_flags & TF_CLOSED) {	/*处于 LAST_ACK 状态时收到 ACK 标识*//* The connection has been closed and we will deallocate the PCB. */if (!(pcb->flags & TF_RXCLOSED)) {/* Connection closed although the application has only shut down thetx side: call the PCB's err callback and indicate the closure toensure the application doesn't continue using the PCB. */TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD);	// <--- 这里}tcp_pcb_remove(&tcp_active_pcbs, pcb);memp_free(MEMP_TCP_PCB, pcb);}}
}

3.连接异常

3.1 由 tcp_abandon 函数调用

tcp_abandon 函数会调用错误类型为 ERR_ABRT 的错误回调函数,简化后的代码为:

void
tcp_abandon(struct tcp_pcb *pcb, int reset)
{if (pcb->state == TIME_WAIT) {tcp_pcb_remove(&tcp_tw_pcbs, pcb);tcp_free(pcb);} else {// 从链表中移除 TCP_PCB// 按需释放[未应答]、[未发送]、[失序]报文内存// 按需发送 RST 标志// 释放 TCP_PCB :tcp_free(pcb)TCP_EVENT_ERR(last_state, errf, errf_arg, ERR_ABRT);	// <-- 这里}
}

tcp_abandon 函数又是谁在调用呢?

3.1.1 tcp_listen_input 函数中

tcp_listen_input 函数中,检测接收到 SYN 标志报文,则创建新的 TCP_PCB,然后发送 SYN|ACK 标志报文。在这一过程中,若发送 SYN|ACK 标志报文失败,则调用 tcp_abandon 函数放弃这个连接,在 tcp_abandon 函数内部会调用错误类型为 ERR_ABRT 的错误处理回调函数。简化后的代码为:

static void
tcp_listen_input(struct tcp_pcb_listen *pcb)
{/* In the LISTEN state, we check for incoming SYN segments,creates a new PCB, and responds with a SYN|ACK. */if (flags & TCP_SYN) {npcb = tcp_alloc(pcb->prio);/* 这里 TCP PCB 申请成功,初始化新的 PCB*/// ...npcb->state = SYN_RCVD;// .../* Send a SYN|ACK together with the MSS option. */rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);if (rc != ERR_OK) {tcp_abandon(npcb, 0);		// <-- 这里return;}tcp_output(npcb);}return;
}

3.1.2 tcp_kill_state 函数中
在《lwIP 细节之二:协议栈什么情况下发送 RST 标志》博文中,有提到 tcp_alloc 函数,tcp_alloc 函数设计原则是尽一切可能返回一个有效的 TCP_PCB 控制块,因此,当 TCP_PCB 不足时,函数可能 “杀死”(kill)正在使用的连接,以释放 TCP_PCB 控制块!
具体就是:

  1. 先调用 tcp_kill_timewait 函数,试图找到 TIME_WAIT 状态下生存时间最长的连接,如果找到符合条件的控制块 pcb ,则调用 tcp_abort(pcb) 函数 “杀” 掉这个连接,这会发送 RST 标志,以便通知远端释放连接;
  2. 如果第 1 步失败了,则调用 tcp_kill_state 函数,试图找到 LAST_ACKCLOSING 状态下生存时间最长的连接,如果找到符合条件的控制块 pcb ,则调用 tcp_abandon(pcb, 0) 函数 “杀” 掉这个连接,注意这个函数并不会发送 RST 标志,处于这两种状态的连接都是等到对方发送的 ACK 就会结束连接,不会有数据丢失;
  3. 如果第 2 步也失败了,则调用 tcp_kill_prio(prio) 函数,试图找到小于指定优先级(prio)的最低优先级且生存时间最长的有效(active)连接!如果找到符合条件的控制块 pcb ,则调用 tcp_abort(pcb) 函数 “杀” 掉这个连接,这会发送 RST 标志。

这里的第 2 步,调用 tcp_abandon(pcb, 0) 函数 “杀” 掉这个连接时,会调用 tcp_abandon 函数放弃这个连接,在 tcp_abandon 函数内部会调用错误类型为 ERR_ABRT 的错误处理回调函数。简化后的代码为:

static void
tcp_kill_state(enum tcp_state state)
{inactivity = 0;inactive = NULL;/* Go through the list of active pcbs and get the oldest pcb that is in stateCLOSING/LAST_ACK. */for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {if (pcb->state == state) {if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {inactivity = tcp_ticks - pcb->tmr;inactive = pcb;}}}if (inactive != NULL) {LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_closing: killing oldest %s PCB %p (%"S32_F")\n",tcp_state_str[state], (void *)inactive, inactivity));/* Don't send a RST, since no data is lost. */tcp_abandon(inactive, 0);}
}

3.1.3 tcp_abort 函数中
tcp_abort 函数中止一个连接,会向远端主机发送一个 RST 标志。当从其中一个 TCP 回调函数中调用时,请确保一定返回 ERR_ABRT 错误码(其它情况绝不要返回 ERR_ABRT 错误码,否则可能会有内存泄漏的风险或者访问已经释放的内存!
tcp_abort 函数代码简单,原始无简化代码为:

void
tcp_abort(struct tcp_pcb *pcb)
{tcp_abandon(pcb, 1);
}
3.2 由 tcp_slowtmr 函数调用

tcp_slowtmr 函数中完成重传和超时处理,当重传达到设定次数,或者超时达到设定时间,则调用错误类型为 ERR_ABRT 的错误处理回调函数。

重传和超时事件有:

  • PCB 控制块处于 SYN_SENT 状态,重传次数达到 TCP_SYNMAXRTX 次(默认 6 次)
  • PCB 控制块处于其它状态,重传次数达到 TCP_MAXRTX 次(默认 12 次)
  • 坚持定时器探查窗口达到 TCP_MAXRTX 次(默认 12 次)
  • PCB 控制块处于 FIN_WAIT_2 状态,超时达到 TCP_FIN_WAIT_TIMEOUT 秒(默认 20 秒)
  • PCB 控制块处于 SYN_RCVD 状态,超时达到 TCP_SYN_RCVD_TIMEOUT 秒(默认 20 秒)
  • PCB 控制块处于 LAST_ACK 状态,超时达到 2 * TCP_MSL 秒(默认 120 秒)
  • 使能保活、PCB 控制块处于 ESTABLISHEDCLOSE_WAIT 状态,超时达到 pcb->keep_idle + TCP_KEEP_DUR(pcb) 秒(默认 2 小时 10 分 48 秒)

tcp_slowtmr 函数简化后的代码为:

/*** Called every 500 ms and implements the retransmission timer and the timer that* removes PCBs that have been in TIME-WAIT for enough time. It also increments* various timers such as the inactivity timer in each PCB.** Automatically called from tcp_tmr().*/
void
tcp_slowtmr(void)
{while (pcb != NULL) {/* 这里表明处于 CLOSED、LISTEN 和 TIME_WAIT 状态的连接不会有重传 */LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);if (pcb->state == SYN_SENT && pcb->nrtx >= TCP_SYNMAXRTX) {++pcb_remove;				// 处于SYN_SENT 状态,重传达到 6 次LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));} else if (pcb->nrtx >= TCP_MAXRTX) {++pcb_remove;				// 其它状态,重传达到 12 次LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));} else {if (pcb->persist_backoff > 0) {if (pcb->persist_probe >= TCP_MAXRTX) {++pcb_remove; 		// 探查次数达到 12 次 */}}if (pcb->state == FIN_WAIT_2) {if (pcb->flags & TF_RXCLOSED) {if ((u32_t)(tcp_ticks - pcb->tmr) >TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {++pcb_remove;			// 处于 FIN_WAIT_2 状态,超时达到 20 秒}}}/* 注意只有 ESTABLISHED 和 CLOSE_WAIT 状态才会有 KEEPALIVE(保活) */if (ip_get_option(pcb, SOF_KEEPALIVE) &&((pcb->state == ESTABLISHED) ||(pcb->state == CLOSE_WAIT))) {if ((u32_t)(tcp_ticks - pcb->tmr) >(pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) {++pcb_remove;			// 使能保活,超时 2 小时 10 分钟 48 秒++pcb_reset;} }if (pcb->state == SYN_RCVD) {if ((u32_t)(tcp_ticks - pcb->tmr) >TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {++pcb_remove;			// 处于 SYN_RCVD 状态,超时达到 20 秒}}if (pcb->state == LAST_ACK) {if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {++pcb_remove;			// 处于 LAST_ACK 状态,超时达到 120 秒}}/* 需要移除 PCB 控制块 */if (pcb_remove) {tcp_pcb_purge(pcb);		// 释放 PCB 中的数据缓冲区(refused_data、unsent、unacked、ooseq)if (prev != NULL) {		// 从 tcp_active_pcbs 列表中移除 PCBprev->next = pcb->next;} else {tcp_active_pcbs = pcb->next;}if (pcb_reset) {			// 根据需要发送 RST 标志tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,pcb->local_port, pcb->remote_port);}tcp_free(pcb2);			// 释放 PCB 控制块内存/* 调用错误回调函数 */TCP_EVENT_ERR(last_state, err_fn, err_arg, ERR_ABRT);} }
}






读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)
千金难买知识,但可以买好多奶粉

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

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

相关文章

大模型应用_chuanhu川虎

https://github.com/GaiZhenbiao/ChuanhuChatGPT 1 功能 整体功能&#xff0c;想解决什么问题 官网说明&#xff1a;为ChatGPT等多种LLM提供了一个轻快好用的Web图形界面和众多附加功能 当前解决了什么问题&#xff0c;哪些问题解决不了 支持多种大模型&#xff08;也可接入本…

Pytorch学习概述

目录 学习目标人工智能1. 智能&#xff08;Intelligence&#xff09;1.1 人类智能1.2 机器学习&#xff08;人工智能&#xff09;1.3 深度学习1.4 学习系统的发展历程传统的机器学习策略 2. 传统机器学习算法的一些挑战3. 神经网络的简要历史3.1 Back Propagation&#xff08;反…

公司怎么防止办公文件数据\资料外泄?

PC访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 公司可以采取以下措施来防止办公文件数据和资料外泄&#xff1a; 文件加密&#xff1a;对文件进行加密是一种常见的数据安全保护手段。加密后的文件只能在单位内部电脑上…

如何开发嵌入式中断控制系统?

目录 1、中断向量表 2、NVIC&#xff08;内嵌向量中断控制器&#xff09; 3、中断使能 4、中断服务函数 在嵌入式开发过程中&#xff0c;中断处理是一个不可或缺的环节。本篇博文将以STM32微控制器为核心案例&#xff0c;深入解析中断处理在MCU开发中的关键步骤和策略。主要有以…

mysql自动安装脚本(快速部署mysql)

mysql_install - 适用于生产环境单实例快速部署 MySQL8.0 自动安装脚本 mysql8_install.sh&#xff08;执行前修改一下脚本里的配置参数&#xff0c;改成你自己的&#xff09;&#xff08;博客末尾&#xff09; my_test.cnf&#xff08;博客末尾&#xff09;&#xff08;这个…

力扣24 两两交换链表中的节点 Java版本

文章目录 题目解题方法Code 题目 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;hea…

【五】Python 代理模式

文章目录 5.1 代理模式概述5.1.1 代理介绍5.1.2 代理模式的作用 5.2 代理模式的UML类图5.3 了解不同类型的代理5.3.1虚拟代理5.3.2 远程代理5.3.3 保护代理5.3.4 智能代理 5.4 现实世界中的代理模式5.5 代理模式的优点5.6 门面模式和代理模式之间的比较 5.1 代理模式概述 5.1.…

VSCode 配置自动生成头文件

相关文章 VSCode 开发C/C实用插件分享——codegeex VSCode 开发C/C实用插件分享——koroFileHeader VSCode 配置自动生成头文件 一、snippets二、配置步骤三、效果展示 一、snippets 相信大家对C、C都头文件都不陌生&#xff0c;都会发现每个头文件都会包括下面的这些格式&…

Cent OS7 磁盘挂载:扩展存储空间和自动挂载

文章目录 &#xff08;1&#xff09;概述&#xff08;2&#xff09;查看磁盘使用情况&#xff08;3&#xff09;VMware虚拟机挂载磁盘&#xff08;4&#xff09;物理机磁盘挂载&#xff08;5&#xff09;ntfs硬盘处理 &#xff08;1&#xff09;概述 在Linux系统中&#xff0c…

智能优化算法应用:基于共生生物算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于共生生物算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于共生生物算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.共生生物算法4.实验参数设定5.算法结果6.…

拓展 Amazon S3 技术边界:Amazon S3 Express One Zone 的创新之路

授权说明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 自 Amazon S3 服务推出以来&#xff0c;一直是全球各行各业数百万客…

学习MS Dynamics AX 2012编程开发 1. 了解Dynamics AX 2012

在本章中&#xff0c;您将了解开发环境的结构以及Microsoft Dynamics AX中的开发人员可以访问哪些工具。在本书的第一步演练之后&#xff0c;您将很容易理解著名的Hello World代码&#xff0c;您将知道应用程序对象树中的不同节点代表什么。 以下是您将在本章中学习的一些主题…

linux7安装python3.12.1教程

1.下载tar.gz包 地址&#xff1a;Python Release Python 3.12.1 | Python.org 2.上传包到linux服并解压 cd /home/local/ ll tar -zxvf Python-3.12.1.tgz 3.安装编译python所需环境 yum install -y gcc yum install -y zlib* yum -y install zlib-devel bzip2-devel opens…

大象elephant目标检测数据集VOC+YOLO格式2300张

大象是长鼻目象科的哺乳动物&#xff0c;有两个属&#xff0c;是世界上最大的陆生动物。其像柱子一样的四肢和宽厚的脚掌可以稳稳支撑住庞大的身体。巨大的头上长有蒲扇状的大耳朵和长且有弹性的鼻子。象耳上有丰富的血管&#xff0c;可以有效散热。鼻子和上唇合而为一的象鼻由…

计算机网络——网络层——OSPF协议的介绍

什么是 OSPF &#xff1f; OSPF 是一个基于链路状态的自治系统内部路由协议&#xff0c;在 TCP/IP 的网络层中进行路由选择&#xff0c;常用于构建大型企业网络或者服务上的骨干网络。在互联网核心路由器之间也可以使用。 OSPF 概述 OSPF 使用的是 Dijkstra&#xff08;最短…

制作蓝牙小车(一)

制作控制蓝牙小车app 想制作一个蓝牙小车&#xff0c;通过手机app程序操控小车运行&#xff0c;制作分2个部分&#xff08;app制作&#xff0c;蓝牙小车硬件以及程序制作&#xff09;&#xff0c;先完成第一个部分app制作&#xff0c;本次app是通过androidstudio软件来制作安卓…

centos安装了curl却报 -bash: curl: command not found

前因 我服务器上想用curl下载docker-compress&#xff0c;发现没有curl命令&#xff0c;就去下载安装&#xff0c;安装完成之后&#xff0c;报-bash: curl: command not found 解决方法 [rootcentos ~]# rpm -e --nodeps curl warning: file /usr/bin/curl: remove failed: …

python每日学10:关于python实用版本的选择

用python也有好几年了&#xff0c;也会经常安装python&#xff0c;因为有工作需要&#xff0c;可能在各个地方使用python&#xff0c;自己的电脑也经常重装&#xff0c;重装后会装python&#xff0c;还有的时候&#xff0c;装的包太多了&#xff0c;影响整个环境的使用&#xf…

数字IC验证快速入门全攻略,你想知道的都在这!

芯片行业是个高风险、高投入的行业&#xff0c;做一款芯片仅仅是开模的费用就是百万起。 从设计到制造都是环环相扣的&#xff0c;设计过程中的BUG或者错误能够达到上千个。 所以验证是保证芯片功能正确性和完整性最重要的一环。&#xff08;文末有学习视频哦~&#xff09; …

21、状态模式(State Pattern)

状态模式指给对象定义不同的状态&#xff0c;并为不同的状态定义不同的行为&#xff0c;在对象的状态发生变换时自动切换状态的行为。 状态模式是一种对象行为型模式&#xff0c;它将对象的不同行为封装到不同的状态中&#xff0c;遵循了“单一职责”原则。同时&#xff0c;状…