初识Linux · 传输层协议TCP · 下

news2025/5/11 14:54:47

目录

前言:

滑动窗口和流量控制机制

流量控制

滑动窗口

1.滑动窗口如何移动

2.滑动窗口的大小如何变化的

3.如果发生了丢包如何解决(快重传)

拥塞控制

延迟应答

面向字节流

RST PSH URG

什么是 PSH?

什么是 URG?

那这俩标志会不会冲突?

对比表格:

RST 一般啥时候出现?

1. 你给了我一个莫名其妙的连接

2. 应用层拒绝服务

3. 半开连接或 zombie 连接清理

看到 RST 要不要慌?


前言:

前文我们从TCP的报头字段开始介绍,从最开始的首部长度,到16位的源端口号和目的端口号,然后逐渐从TCP的缓存管理机制开始理解TCP报头中的标志位ACK,并且顺便引出了32位的序号和确认序号,从中我们知道了TCP管理报文的时候是依赖的环形缓冲队列,并且报文是以sk_buff的结构体的形式管理起来的。

前文最后的大头是三次握手和四次挥手,从三次握手我们理解了标志位SYN,顺带理解了什么是超时重传和知道如果连接异常,那么标志位RST会设置为1,重新建立连接。从四次挥手,我们理解了FIN标志位,理解并且验证了三种状态TIME_WAIT,CLOSE_WAIT,LAST_ACK。

那么本文,介绍TCP的其他策略,如滑动窗口,快重传,流量控制,拥塞控制,TCP的异常情况,延迟应答,粘包问题和什么是面向字节流~

干货较多~我们直接进入主题吧!


滑动窗口和流量控制机制

流量控制

对于流量控制来说,在网络世界中,报文的发送是无时无刻的,那么报文的数量控制就成为了一个很重要的点,如果报文发送的数量控制不好,就会导致网络拥塞。

我们来只有两台主机的情况来说,对于主机A和主机B,主机A的接收缓冲区空间是有限的,如果主机B无限制的往主机A中发送报文,那么势必会导致主机B迟迟收不到多余报文的ACK报文,因为主机A处理不过来了,最后导致的结果就是主机B不停的触发超时重传机制,这对网络资源来说无疑是一种浪费,所以两台主机来说有一个很重要的工作就是确认双方的接收缓冲区的大小,那么在什么时候确认就成为了一个比较重要的问题。

实际中,在双方三次握手建立连接的时候,就会通过对方的ACK报文来确定对方的接收缓冲区的大小。

确定了对方的接收缓冲区的大小,就是如何进行报文传输控制了,那么对于流量控制来说,滑动窗口就是它的核心机制之一。

滑动窗口

介绍滑动窗口之前,相信大家都有一个共同的认识就是在应用层调用read和write的接口的时候,实际上都是在内核中拷贝发送缓冲区和接收缓冲区中的数据,那么问题来了,我们前面不管是介绍确认应答机制还是捎带应答机制,都是基于对方接收缓冲区有足够的空间的情况,如果对方的接收缓冲区没有足够的空间,我们是否还能“无所畏惧”的发送报文?

所以从这里我们得出:发送报文之前,对方的接收能力如何?在后面我们介绍拥塞控制的时候,我们还会考虑到网络状况。

那么影响发送报文的大体可以分为:对方的接受能力和网络状况

在前文我们也形象的描述了OS是如何组织报文的,一个环形队列,那么为了方便学习,我们可以给它简化为一个数组:

形象一点,我们把这里的滑动窗口理解为现实的一个窗口,直接套在了这个数组的上面。而这个滑动窗口套住的就是数据,我们发送缓冲区的数据分为三部分:滑动窗口左边的数据,是已发送已确认的,滑动窗口内部的数据是已发送暂时未确认的,滑动窗口右边的数据是未发送未确认的

问题来了:滑动窗口的大小我们怎么确定呢?我们通过什么属性来确定呢?

对于滑动窗口的大小,我们在TCP的报头中看到过一个字段叫做16位的窗口大小,而每次发送报文的时候,TCP会在报文中实时更新这个属性的大小,那么,另一方接收到了报头,一看16位窗口大小,就知道自己应该发送多少数据了。

而我们在前文所说的,发送报文我们要考虑对方的接收能力,这个接收能力,难道不就是对方的接收缓冲区的大小吗?所以,滑动窗口的大小,依赖于对方的接收缓冲区的大小

那么我们肯定不能光这么说,因为即便是对于抽象的连接来说,也是通过内核数据结构描述的,对于协议也是通过udphdr tcphdr进行描述的,对于滑动窗口的大小,我们暂时认为是对方同步给我们的接收缓冲区的大小

目前对滑动窗口的理解,我们可以列出以下的问题:滑动窗口如何移动?滑动窗口的大小如何变化?滑动窗口可以为0吗?如果发生了丢包,如何解决?

1.滑动窗口如何移动

对于如何移动的问题,首先,我们规定滑动窗口是只能向左移动,不能向右移动,因为发送缓冲区中的数据都是明确规定了应答和发送的关系的,如果滑动窗口向右移动,势必就破坏了规定。

2.滑动窗口的大小如何变化的

滑动窗口的大小我们在逻辑上可以简单的使用win_start和win和win_end简单来代表窗口的起点,窗口大小,窗口结束位置,但是实际上,如果我们阅读源码,我们会发现TCP报头中的16位窗口大小和源码中的窗口大小属性对不上:

/*
 *	RFC793 variables by their proper names. This means you can
 *	read the code and the spec side by side (and laugh ...)
 *	See RFC793 and RFC1122. The RFC writes these in capitals.
 */
 	u32	rcv_nxt;	/* What we want to receive next 	*/
	u32	copied_seq;	/* Head of yet unread data		*/
	u32	rcv_wup;	/* rcv_nxt on last window update sent	*/
 	u32	snd_nxt;	/* Next sequence we send		*/

 	u32	snd_una;	/* First byte we want an ack for	*/
 	u32	snd_sml;	/* Last byte of the most recently transmitted small packet */
	u32	rcv_tstamp;	/* timestamp of last received ACK (for keepalives) */
	u32	lsndtime;	/* timestamp of last sent data packet (for restart window) */

这是滑动窗口在Linux内核中的定义,版本为2.62.32,我们发现好像窗口的属性基本都是32位的,它们对不上的原因实际上是因为我们没有介绍TCP中的选项,选项里由一个叫做“窗口扩大因子”,这个扩大因子,就可以让窗口大小从65535一直到1GB左右,所以我们在源码中看到的实际上是32位。

当接收方收到了报文,并且给发送方返回报文,其中有确认序号,这个确认序号恰好代表了发送方在滑动窗口内的哪些数据被接收了,那么有了确认序号,也就是ack_seq,win_start = ack_seq, 更新窗口的起始位置,因为我们能通过确认序号确定之前的数据接收方都收到了,所以此时满足窗口左边的数据是已发送已确认的,然后win_end = win_start + win,通过对方的ACK报文,我们有它的窗口大小,那么我们已经确定了窗口的起始位置,所以可以通过对方的窗口大小确定,win_end的位置。

这是窗口的大小变化,那么有意思的来了,我们在第一个小问题中知道窗口是只能往右边移动的,那么移动的快慢,是取决于win_start, win_end的,那么如果,对方的返回的窗口大小为0,也处理了一定的报文,那么,它返回的确认序号是不是会更新?win_start就会更新,win_start更新了,但是win为0,那么win_start = win_end,此时窗口大小不就为0吗

那么问题来了,滑动窗口的移动快慢,实际上看的不就是win_start++和win_end谁更新的更快,更多吗?

而在源码的角度来看,win_start和win_end实际上就是序号之间的加法而已,所以源码中的属性基本都是和序号有关的,当然有时间戳变量我们暂时先不管。

3.如果发生了丢包如何解决(快重传)

这个问题才是滑动窗口中问题的重中之重了,对于发送缓冲区的数据,我们可以分为滑动窗口左边的数据,滑动窗口内部的数据,滑动窗口右边的数据,那么实际要发送的数据是滑动窗口内部的报文,对于内部的报文,我们可以这样排序,最左侧的报文,中间的报文,最右侧的报文,我们从三个角度来考虑,如果发生了丢包,我们如何解决?

对于最左侧的报文丢失:

假设发送2001 3001 4001 5001,而实际情况是2001-3000的报文收到,3001到4000的报文收到,4001到5000的报文收到,1001到2000的报文丢失了,此时会返回三个报文的确认序号不会是3001 4001 5001,因为1001到2000的报文丢失,在前文我们已经介绍了确认序号要保证的是TCP的可靠性,这个可靠性实际上是保证的历史报文能被收到,所以实际上三个ACK报文的确认序号是1001,因为1-1001的报文是已经发送,并且已经确定了,对于1001-2000的报文没有收到,为了可靠性,所以确认序号应该为1001

此时,因为三个ACK报文的确认序号都是1001,OS就知道1001-2000的报文没有收到,所以此时就触发高速重发机制,即快重传,那么快重传的触发条件也很明显,OS连续收到三个同样确认序号的报文,就会根据实际情况看是否触发快重传了。

那么有意思来了,我们前文学习过超时重传机制,超时重传机制和快重传的机制有什么区别呢?或者说它们的应用场景有什么不同。

我们不妨把超时重传机制理解为兜底的机制,同样都是重传,因为超时重传机制等待的时间较久,在系统中配置了对应的超时时间,过了这个时间没收到应答在重传,而快重传是连续收到多个一样的确认序号,此时直接重传,不用等待那么久的超时时间。那么它在一定程度上提高了重传的效率,主要是因为它提前判断,响应的更快了

在TCP中结合了这两个机制,在提高重传效率的同时,也有保底机制,极大程度上保证了TCP的效率和可靠性。

对于丢包还有一个小小的问题,如果是ACK丢失了呢?也就是说返回的是2001 3001 5001,其中丢了某个应答,是完全没有关系的,因为TCP的可靠传输。

好了,视角放到中间报文丢失和右侧报文丢失,不管了哪一侧报文丢失,左侧的数据成功发送并应答,那么滑动窗口就会通过确认序号更新,测试win_start变为新的确认序号,此时问题就演化为了最左侧的报文丢失应该怎么办?就和我们上面的问题形成了完美闭环!

那么问题来了,丢失的报文,我们放在哪儿?已发送已确认的报文,我们怎么处理

首先对于第一个问题,丢失的报文,我们放在哪儿?当一个报文段丢失时,TCP 不会立刻把它移出窗口,而是将它保留在发送窗口区间内,等待超时重传或者重传就可以了嘛。

对于第二个问题,已经被接收方确认(ACK)的数据,就说明这段数据已经“安全送达”了。此时 TCP 会将它从发送缓存中移除,释放内存。而用于发送的缓冲区通常是一个环形缓冲区,指针会不断向前推进,旧位置的数据会被新数据覆盖

这就像写磁带一样,头部已经播放完的内容,可以被下一段录音覆盖,节省空间又高效。

TCP 的滑动窗口不仅用于控制发送速率,还承担了“数据暂存与重发控制”的职责


拥塞控制

前文我们说暂时认为滑动窗口的大小是对方的接收缓冲区的大小,但是实际情况上,我们考虑了双方主机的通信能力,却忘记了网络的状态,比如网络拥塞怎么办,网络波动了怎么办?

对于滑动窗口的介绍,我们都是基于网络状况良好的情况下,现在局域网内存在多台主机,而网络资源是有效的,比如网络带宽,所以势必会存在一种情况为:多台主机共享网络资源,导致主机之间通信速度变慢

所以我们需要合理的策略来解决拥塞的问题,就像堵车的马路上会引入交警这个角色一样,来解决拥堵的问题。

首先,在多台主机通信之前,都会获取窗口大小来了对方的接收缓冲区,但是实际上,滑动窗口的大小并不是完全取决于对方的接收缓冲区的,因为要考虑网络资源,所以引入了拥塞窗口的概念,同样,在tcp_sock中描述了滑动窗口的,也描述了拥塞窗口:

/*
 *	Slow start and congestion control (see also Nagle, and Karn & Partridge)
 */
 	u32	snd_ssthresh;	/* Slow start size threshold		*/
 	u32	snd_cwnd;	/* Sending congestion window		*/
	u32	snd_cwnd_cnt;	/* Linear increase counter		*/
	u32	snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */
	u32	snd_cwnd_used;
	u32	snd_cwnd_stamp;

其中cwnd代表的就是拥塞的意思。

其中snd_ssthresh代表的是慢启动门限值,snd_cwnd代表的是当前拥塞窗口的大小,snd_swnd_cnt代表的是每次收到ACK就+1,到了一定的程度就会进行线性增长。snd_cwnd_clamp代表的是拥塞窗口的最大值。

同学们也注意到了,对于以上的字段,都是用来描述拥塞窗口的,并且引入了慢启动算法,我们以下面的图介绍拥塞避免:

首先我们清楚滑动窗口大小=min(对方接收能力,拥塞窗口),那么在通信初期,对方的接收能力可以说是杠杠的,因为初期的报文积攒并不多,所以可以说接收空间非常大。

但是可以很负责任的说,不考虑拥塞窗口的传输都是耍流氓,你别看别人的接受能力那么强你就使劲发报文,比如两个地点,中间有一条马路,A点可以放100辆车,那么你难道就知道整100辆车过去吗?显然不能,因为马路不足以支撑100辆车,而实际上也有别的车要通过,所以我们初期需要发报文探测实际能通过多少车。

网络也是如此,初期从1个报文开始发送,然后按照指数级别增长,为什么不线性增长呢?因为通信的时候,我们虽然要考虑拥塞的问题,但是同时也要兼顾效率,所以前期,是按照指数增长的。那么增长到了ssthresh的时候,就开始按照线性增长了,因为再double一下,就超出了拥塞窗口的范围了。线性增长的过程我们叫做加法增大

而一直加大的过程,总会碰到网络拥塞的时候,此时直接跌到1,重新探测网络状态,并且设置了新的临界值,跌到1的这个过程我们称为乘法减小。

以上的图我们看到的是拥塞避免算法AIMD的核心框架,实际上最后拥塞窗口的值会处于一个带宽的某个极限值震荡,以保证通信的稳定传输。

有个问题就是,我们如何确认是网络拥塞了呢?对于少量的丢包,我们可以认为是网络波动,并且采取超时重传或者是快重传的方式解决,但是如果是大量丢包,我们就有理由认为是网络拥塞了。


延迟应答

延迟应答指的是对方发送报文之后,接收方等会儿再给应答。

对于初学TCP的同学对于延迟应答大概会感觉有点怪,我明明可以快速应答它,为啥还要延迟呢?因为有一种情况是,上层应用处理报文的速度极快,如果接收方收到了报文,马上应答,那么窗口大小是接收大小-该报文的大小

但是当报文以极快的速度处理了之后,接收方再应答,此时窗口的大小就是接收缓冲区的大小了。并且我们在学习Redis的时候我,就像 Redis 推出的 mset 是为了减少多次 set 带来的网络开销,TCP 的延迟应答机制也是为了减少不必要的 ACK 报文数量。与其频繁确认,不如一次性“捎带”更多信息,提高效率。

这里也是一样的,我能一次接收多个报文,为什么要马上应答,然后接收少数的报文呢?

延迟应答也是有一定的配置的,主要是最大延迟时间和数量限制。一般设置的是延迟2个包和延迟时间设置为200ms,当然了不同的系统有不同的配置。

如图这样。


面向字节流

TCP的特点是面向字节流,学习到了这里,我们也能对于UDP的面向数据报和TCP的面向字节流有了一个较为清晰的分辨了。

首先,UDP的面向数据报特点主要是因为UDP发送数据的时候,是直接把整个数据打包发送的,接收方只能一次性全部接受,并且不会存在数据需要拼接的问题,一次性就接收了,那么丢包了无所谓,反正不是我UDP的事儿。

其次,TCP的面向字节流主要是因为TCP的数据是放在缓冲区之后,根据序号发送,而序号的本质是数据在缓冲区按照字节单位编排出来的,而经过TCP的层层封包之后,不同数据包按照序号可能被分配到一个报文里面,那么这个过程是没有人关心哪个数据是哪个包的,我们以一个统一的视角来看待报文,就是基本单位字节,没人关心里面的数据是谁的,我只知道这个数据的基本单位是字节。

那么在C语言阶段学习的文件操作,同样,也是面向字节流的,这里的面向字节流,和TCP的面向字节流是完全一样的,所以在文件操作的时候会用大量的时间去学习,主要就是因为面向字节流我们当时并不理解。

到了这里,我们已经了解了面向字节流,而以字节的统一视角去看待问题就会有其他问题,比如粘包问题,因为不同的数据包的数据按照字节,进到了同一个报文里面,我们如何将不同的数据拆出来呢?

记得我们之前写的HTTP服务器吗?

对于粘包问题我们简单的总结:对于定长数据包,我们按照固定长度来接收,对于变长数据包,我们可以引入一个特殊分隔符,标识数据包的结尾,我们也可以引入一个字段标识数据包的长度。

像这样:

uint32_t len;
recv(sock, &len, 4);  // 读取消息长度
len = ntohl(len);     // 网络字节序转本地
recv(sock, buf, len); // 按长度读取完整消息

RST PSH URG

在 TCP 报头里,大家最熟的无非就是 SYN、ACK、FIN 这些字段,用来建立连接、确认、断开连接。但除了这些“明星字段”,还有两个小透明字段:PSH(Push)URG(Urgent)。大多数人学 TCP 都直接跳过它们,觉得用不到,其实这俩字段还是有点意思的。


什么是 PSH?

PSH 的意思是:“我发的这些数据你就别等了,快点扔给上层应用吧。”

我们知道,TCP 是一个带缓冲的协议,它可能接收了一点数据,但不会立刻交给上层应用,而是等攒多一点再交。但是有些时候,我们希望这个过程别拖了,比如下面这些场景:

  • 你在远程终端(比如 SSH)打了一个字母,对方应该立刻看到结果。

  • 聊天程序,你敲一句“在吗?”,当然希望对方立即看到。

  • 实时指令,比如发送一个“退出”、“取消”命令,不能等。

这些时候就需要 PSH 上场。它的作用就是:告诉对方 TCP 栈,别攒包了,赶紧把这些数据交上去


什么是 URG?

URG 这个就更冷门一点,叫 Urgent,意思是“这里有段数据是紧急的,你优先处理”。

这个字段配合一个 **紧急指针(urgent pointer)**使用,告诉接收方:从某个偏移量开始,有一段字节很重要,你先把它捞出来处理。

举个例子:

  • Telnet 协议早期就用 URG 实现“中断”功能,比如你敲 Ctrl+C,它就发一个带 URG 标志的包,中断对方的执行。

但说实话,URG 现在用得非常非常少,很多系统对它支持也不统一,甚至直接忽略。像我们现在熟悉的 Redis、HTTP、gRPC 等现代协议,根本用不到它。


那这俩标志会不会冲突?

其实不会。

  • PSH 是说:“别等了,我发的这段你马上处理。”

  • URG 是说:“我发的这一部分,特别重要,你先处理这段。”

它们的粒度不一样。PSH 是报文整体行为的建议;URG 是精确到字节范围的优先级提示。而且 URG 需要配合指针使用,PSH 是隐式的,很多时候系统自己会帮你处理。


对比表格:

对比项PSH(Push)URG(Urgent)
功能请求立即将数据交给应用层指定某段数据优先处理(紧急数据)
使用目的提高实时性,减少等待中断、控制信号等特定用途
影响范围整个报文报文中的一段数据(由 urgent pointer 定位)
典型场景SSH、Telnet 交互、实时通信Telnet 中断(Ctrl+C)
现代系统支持✅ 自动处理,常见❌ 多数协议/语言已不支持
应用层是否处理不需要显式处理,系统会交给应用应用需读取“紧急数据”,处理逻辑更复杂
是否常用✅ 常用(甚至自动设置)❌ 非常少用,基本淘汰

说白了,RST 就是 TCP 协议里的“出事了,赶紧断开”按钮。

我们平时连接断开不是都有一个优雅的四次挥手吗?你 FIN,我 ACK,我 FIN,你 ACK……然后大家礼貌说再见。但 RST 可不管那么多,它直接说:“不玩了,掀桌子!”——连接不正常,直接重置。


RST 一般啥时候出现?

1. 你给了我一个莫名其妙的连接

比如你这边给我发了个 ACK,但是我根本没这个连接(也许我重启了),我一脸懵逼,干脆发个 RST 给你,告诉你“这连接别用了”。

2. 应用层拒绝服务

有些服务端一看你这连接不合法,比如权限问题、校验失败,它就用 RST 告诉你:“你走吧,我不收。”

3. 半开连接或 zombie 连接清理

有些时候,一方掉线了,另一方还傻傻等着。这个时候,系统探测之后可能会用一个 RST 来直接清理。


看到 RST 要不要慌?

不用慌,但要注意。

  • 如果你自己在开发应用,一不小心被 RST 了,那得排查:

    • 是不是你用错了端口?

    • 是不是连接早就断了你还在用?

    • 是不是服务器主动踢你了?

  • 如果你只是用现成软件,偶尔看到 RST,其实也正常,比如连接被负载均衡打断啥的,不用太紧张。

项目RST 标志说明
全称Reset
触发时机异常连接请求、资源不存在、主动拒绝等
行为立即断开连接,不走四次挥手
应用层能看到吗能看到 socket 报错,通常是 connection reset
常见场景服务端拒绝连接、半开连接清理、端口未监听
是否正常现象有时是预期行为,有时是 bug,需结合上下文分析

以上就是TCP传输层协议的所有内容~


感谢阅读!

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

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

相关文章

什么是分布式光伏系统?屋顶分布式光伏如何并网?

政策窗口倒计时!分布式光伏如何破局而立? 2025年,中国分布式光伏行业迎来关键转折: ▸ "430"落幕——抢装潮收官,但考验才刚开始; ▸ "531"生死线——新增项目全面市场化交易启动&…

完整进行一次共线性分析

(随便找个基因家族) 1.数据收集 使用水稻、拟南芥、玉米三种作物进行示例 可以直接去ensemble去找最标准的基因组fasta文件和gff文件。 2.预处理数据 这里对于fasta和gff数据看情况要不要过滤掉线粒体叶绿体的基因,数据差异非常大&#…

RabbitMQ--基础篇

RabbitMQ 简介:RabbitMQ 是一种开源的消息队列中间件,你可以把它想象成一个高效的“邮局”。它专门负责在不同应用程序之间传递消息,让系统各部分能松耦合地协作 优势: 异步处理:比如用户注册后,主程序将发…

Quorum协议原理与应用详解

一、Quorum 协议核心原理 基本定义 Quorum 是一种基于 读写投票机制 的分布式一致性协议,通过权衡一致性(C)与可用性(A)实现数据冗余和最终一致性。其核心规则为: W(写成功副本数) …

vue搭建+element引入

vue搭建element 在使用Vue.js开发项目时,经常会选择使用Element UI作为UI框架,因为它提供了丰富的组件和良好的设计,可以大大提高开发效率。以下是如何在Vue项目中集成Element UI的步骤: 1. 创建Vue项目 如果你还没有创建Vue项…

食物数据分析系统vue+flask

食物数据分析系统 项目概述 食物数据分析系统是一个集食物营养成分查询、对比分析和数据可视化于一体的Web应用。系统采用前后端分离架构,为用户提供食物营养信息检索、食物对比和营养分析等功能,帮助用户了解食物的营养成分,做出更健康的饮…

SPDK NVMe of RDMA 部署

使用SPDK NVMe of RDMA 实现多NVMe设备共享 一、编译、安装spdk 1.1、下载 1.1.1 下载spdk源码 首先,我们需要从GitHub上克隆SPDK的源码仓库。打开终端,输入以下命令: git clone -b v22.01 https://github.com/spdk/spdk.git cd spdk1.1.2…

【Redis】缓存和分布式锁

🔥个人主页: 中草药 🔥专栏:【中间件】企业级中间件剖析 一、缓存(Cache) 概述 Redis最主要的应用场景便是作为缓存。缓存(Cache)是一种用于存储数据副本的技术或组件,…

OpenLayers 精确经过三个点的曲线绘制

OpenLayers 精确经过三个点的曲线绘制 根据您的需求,我将提供一个使用 OpenLayers 绘制精确经过三个指定点的曲线解决方案。对于三个点的情况,我们可以使用 二次贝塞尔曲线 或 三次样条插值,确保曲线精确通过所有控制点。 实现方案 下面是…

大模型微调指南之 LLaMA-Factory 篇:一键启动LLaMA系列模型高效微调

文章目录 一、简介二、如何安装2.1 安装2.2 校验 三、开始使用3.1 可视化界面3.2 使用命令行3.2.1 模型微调训练3.2.2 模型合并3.2.3 模型推理3.2.4 模型评估 四、高级功能4.1 分布训练4.2 DeepSpeed4.2.1 单机多卡4.2.2 多机多卡 五、日志分析 一、简介 LLaMA-Factory 是一个…

GLPK(GNU线性规划工具包)介绍

GLPK全称为GNU Linear Programming Kit(GNU线性规划工具包),可从 https://sourceforge.net/projects/winglpk/ 下载源码及二进制库,最新版本为4.65。也可从 https://ftp.gnu.org/gnu/glpk/ 下载,仅包含源码,最新版本为5.0。 GLPK是…

PCB设计实践(十三)PCB设计中差分线间距与线宽设置的深度解析

一、差分信号的基本原理与物理背景 差分信号技术通过两条等幅反相的传输线实现信号传输,其核心优势体现在电磁场耦合的对称性上。根据麦克斯韦方程组的对称解原理,两条线产生的电磁场在远场区域相互抵消,形成以下特性: 1. 共模噪…

2025python学习笔记

一.Python语言基础入门 第一章 01.初识Python Python的起源: 1989年,为了打发圣诞节假期,Gudio van Rossum吉多范罗苏姆(龟叔)决心开发一个新的解释程序(Python维形)1991年,第一个…

【并发编程】基于 Redis 手写分布式锁

目录 一、基于 Redis 演示超卖现象 1.1 Redis 超卖现象 1.2 超卖现象解决方案 二、Redis 的乐观锁机制 2.1 原生客户端演示 2.2 业务代码实现 三、单机部署 Redis 实现分布式锁 3.1 分布式锁的演变和升级 3.2 setnx 实现分布式锁 3.2.1 递归调用实现分布式锁 3.2.2 循…

Jsp技术入门指南【十二】自定义标签

Jsp技术入门指南【十二】自定义标签 前言一、什么是标签二、标签的类型有哪些?1. 空标签2. 带有属性的标签3. 带主体的标签 三、自定义标签的部件3.1 自定义标签的四步骤3.2 标签处理程序3.3 自定义标签的开发及使用步骤第一步:创建标签助手类第二步&…

Java—— 泛型详解

泛型概述 泛型是JDK5中引入的特性&#xff0c;可以在编译阶段约束操作的数据类型&#xff0c;并进行检查。 泛型的格式&#xff1a;<数据类型> 注意&#xff1a;泛型只能支持引用数据类型。 泛型的好处 没有泛型的时候&#xff0c;可以往集合中添加任意类型的数据&#x…

GPT-4o, GPT 4.5, GPT 4.1, O3, O4-mini等模型的区别与联系

大模型时代浪潮汹涌,作为其中的领军者,OpenAI 其推出的系列模型以强大的能力深刻影响着整个行业,并常常成为业界其他公司对标和比较的基准。因此,深入了解 OpenAI 的大模型,不仅是为了使用它们,更是为了理解当前大模型的能力边界和发展趋势,这对于我们评估和选择其他各类…

Kubernetes生产实战(十二):无工具容器网络连接数暴增指南

当线上容器突然出现TCP连接数暴涨&#xff0c;而容器内又没有安装任何调试工具时&#xff0c;如何快速定位问题&#xff1f;本文将分享一套经过大型互联网公司验证的排查方案&#xff0c;涵盖从快速应急到根因分析的全流程。 一、快速锁定问题容器 查看pod 连接数方式&#x…

MySQL的Order by与Group by优化详解!

目录 前言核心思想&#xff1a;让索引帮你“排好序”或“分好组”Part 1: ORDER BY 优化详解1.1 什么是 Filesort&#xff1f;为什么它慢&#xff1f;1.2 如何避免 Filesort&#xff1f;—— 利用索引的有序性1.3 EXPLAIN 示例 (ORDER BY) Part 2: GROUP BY 优化详解2.1 什么是…

软件测试——用例篇(3)

目录 一、设计测试用例的具体方法 1.1等价类 1.1.1等价类概念介绍 1.1.2等价类分类 1.2边界值 1.2.1边界值分析法 1.2.2边界值分类 1.3正交法 1.3.1正交表 1.3.2正交法设计测试用例步骤 1.4判定表法 1.4.1判定表 1.4.2判定表方法设计测试用例 1.5 场景法 1.6错误…