Linux 网络:调试、追踪、调优

文章目录

  • 1. 前言
  • 2. 网络状态观察
    • 2.1 硬件:网络硬件 调试观察
      • 2.1.1 网络 PHY 芯片 调试观察
        • 2.1.1.1 观察方法
        • 2.1.1.2 源码实现
          • 2.1.1.2.1 用户空间部分
          • 2.1.1.2.2 内核空间部分
      • 2.1.2 网卡调试观察
        • 2.1.2.1 网卡性能评估
          • 2.1.2.1.1 网卡性能评估方法
          • 2.1.2.1.2 代码实现
        • 2.1.2.2 网卡 统计数据 观察
        • 2.1.2.3 网卡 调优
    • 2.2 软件:网络协议栈 调试观察
      • 2.2.1 查看 TCP 套接字信息数据
        • 2.2.1.1 观察方法
        • 2.2.1.2 源码实现
          • 2.2.1.2.1 用户空间部分
          • 2.2.1.2.2 内核部分
  • 3. 后记

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 网络状态观察

本节描述对网络硬件、网络子系统进行观察的方式方法和工具。内容的组织形式是:先给出对应目标的观察方法,然后分析其相关的源码实现(包括用户空间内核空间两部分)。

2.1 硬件:网络硬件 调试观察

2.1.1 网络 PHY 芯片 调试观察

2.1.1.1 观察方法

调试观察 网络 PHY 芯片,主要是查看和修改 PHY 芯片的寄存器。有一些开源工具,如 mii-tool,mdio-tools,phytool 等等,都可对 PHY 芯片进行调试和观察。本文只对 phytool 进行分析,没什么原因,因为它最简单易用,可直接读写 PHY 寄存器。
来看下如何为 arm32 架构交叉编译 phytool,从前面给出的地址下载源码,解压后切换到源码目录,将交叉编译器的路径扩展到 PATH,然后运行下面的命令:

make CC=arm-linux-gnueabihf-gcc # CC 修改为你实际使用的交叉编译器

然后会生成一个名为 phytool 可执行文件。接下来,示范下通过 phytool 如何读写 PHY 寄存器:

# 下面示范对 YT8531C PHY 寄存器读写操作# 写寄存器 0xa0
$ phytool write eth0/1/0x1e 0xa0 # 写的扩展寄存器,先要选择寄存器 page
$ phytool write eth0/1/0x1f 0xa8d0 # 寄存器 0xa0 写入值 0xa8d0# 读寄存 0xa3
$ phytool write eth0/1/0x1e 0xa4
$ phytool read eth0/1/0x1f
0x00a6

phytool 语法有点独特,phy write 后的参数格式:网络接口/PHY地址/寄存器地址 写入值。相对于 phytool writephytool read 不需要最后一个参数。phytool 还有几个其它的指令,都相对简单,感兴趣的读者,可自行研究。

2.1.1.2 源码实现

phytool核心逻辑是:通过网卡驱动的 ioctl(SIOCGMIIREG) 读取 PHY 寄存器,通过 ioctl(SIOCSMIIREG) 写 PHY 寄存器。

2.1.1.2.1 用户空间部分
/* phytool/phytool.c *//* 读操作 */
main()phytool_read()phytool_parse_loc() /* 参数解析 */val = phy_read (&loc);int err = __phy_op(loc, &val, SIOCGMIIREG);static int sd = -1;struct ifreq ifr;struct mii_ioctl_data* mii = (struct mii_ioctl_data *)(&ifr.ifr_data);int err;if (sd < 0)sd = socket(AF_INET, SOCK_DGRAM, 0);...strncpy(ifr.ifr_name, loc->ifnam, sizeof(ifr.ifr_name));mii->phy_id  = loc->phy_id; /* PHY 地址 */mii->reg_num = loc->reg; /* PHY 寄存器地址 */mii->val_in  = *val; /* 写操作: 要写入的值 */mii->val_out = 0;/** 读寄存器:发送 SIOCGMIIREG 命令* 写寄存器:发送 SIOCSMIIREG 命令*/err = ioctl(sd, cmd, &ifr);*val = mii->val_out; /* 读操作: 读回的 PHY 寄存器值 */......printf("%#.4x\n", val); /* 打印读回来的值 *//* 写操作 */
main()phytool_write()phytool_parse_loc() /* 参数解析 */...val = strtoul(argv[1], NULL, 0); /* 要写入的值 */err = phy_write (&loc, val);int err = __phy_op(loc, &val, SIOCSMIIREG);/* 参考读操作解析 */...
2.1.1.2.2 内核空间部分
sock_ioctl() /* net/socket.c */sock_do_ioctl()dev_ioctl() /* net/core/dev_ioctl.c */...switch (cmd) {...case SIOCGMIIPHY:case SIOCGMIIREG: /* 读取 PHY 寄存器 */case SIOCSIFNAME:...ret = dev_ifsioc(net, &ifr, cmd);...struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);const struct net_device_ops *ops;// &stmmac_netdev_opsops = dev->netdev_ops; /* 网卡驱动接口,以 STMicro 的 MAC 为例 */switch (cmd) {...default:if ((cmd >= SIOCDEVPRIVATE &&...cmd == SIOCGMIIREG ||cmd == SIOCSMIIREG ||...)if (ops->ndo_do_ioctl) {...ops->ndo_do_ioctl(dev, ifr, cmd) = stmmac_ioctl()switch (cmd) {case SIOCGMIIPHY:case SIOCGMIIREG: /* 读 PHY 寄存器 */case SIOCSMIIREG: /* 写 PHY 寄存器 */...ret = phy_mii_ioctl(dev->phydev, rq, cmd);break;...}}}......case SIOCSMIIREG: /* 写 PHY 寄存器 */......ret = dev_ifsioc(net, &ifr, cmd);/* 后续路径同 读操作 一样 */...return ret;}// 上接 phy_mii_ioctl()
phy_mii_ioctl()switch (cmd) {case SIOCGMIIPHY:mii_data->phy_id = phydev->mdio.addr;/* fall through */case SIOCGMIIREG:/* 通过 MDIO 接口读 PHY 寄存器 */mii_data->val_out = mdiobus_read(phydev->mdio.bus,mii_data->phy_id,mii_data->reg_num);case SIOCSMIIREG:.../* 通过 MDIO 接口写 PHY 寄存器 */mdiobus_write(phydev->mdio.bus, mii_data->phy_id,mii_data->reg_num, val);......}

2.1.2 网卡调试观察

2.1.2.1 网卡性能评估
2.1.2.1.1 网卡性能评估方法

网卡性能的评估,通常会用 iperf 分别测试 TCPUDP 通信的带宽。iperf 是一个 服务端/客户端 模式 的测试工具:一台机器运行 iperf 服务端,一台机器运行 iperf 客户端。默认情形下,客户端负责发送数据,服务端负责接收数据,因此此时重点测试的是服务端的 RX 和 客户端的 TX ,虽然各自的另外一个方向也有数据传输,但数据量很小。如果要测试各自另一个传输方向,调换彼此的角色即可:服务端变客户端,客户端变服务端。
TCP 方式是可靠连接,其测试的是可靠通信的带宽。TCP 方式下,数据中间经过各式各样的缓冲,以及数据滑动窗口、数据拥塞控制,传输随可靠但有延迟,但排除交换机、路由器等中间设备的影响后(可以用网线直连测试的机器),正常情况下,测试的带宽速度应接近网卡本身的带宽。看一个测试 TCP 可靠连接下带宽的例子:
在这里插入图片描述
上例中测试的是一个 1000Mbps 网卡的情形,由于存在一些问题,可以看到 TCP 可靠通信的带宽,远远低于 1000Mbps,平均只有 420Mbps 的带宽。
UDP 方式(通过 -u 参考指定)是非可靠方式,会出现丢包的情况,通过 -b 参数去指定请求的带宽速度(当然最大无法指定超过网卡的带宽速度),然后看丢包的比例。当然,同样要排除交换机、路由器等中间设备的影响后(可以用网线直连测试的机器)。看一个例子:
在这里插入图片描述
还是上面这个有问题的网卡,看到了吗?平均 76% 的丢包率,相当恐怖。

2.1.2.1.2 代码实现
// iperf3 -b 1000M
setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &rate, sizeof(rate)); // 流量控制

(未完待续)

2.1.2.2 网卡 统计数据 观察

通过 ethtool 命令 ethtool -S <IFACE> 观察网卡的收发统计数据,<IFACE> 为网卡名。

$ ethtool -S eth0
# ethtool -S eth0
NIC statistics:Good Rx Frames: 10079353Broadcast Rx Frames: 75797Multicast Rx Frames: 96406Pause Rx Frames: 0Rx CRC Errors: 0Rx Align/Code Errors: 0Oversize Rx Frames: 0Rx Jabbers: 0Undersize (Short) Rx Frames: 0Rx Fragments: 0Rx Octets: 1021757198Good Tx Frames: 749537Broadcast Tx Frames: 6Multicast Tx Frames: 15Pause Tx Frames: 0Deferred Tx Frames: 0Collisions: 0Single Collision Tx Frames: 0Multiple Collision Tx Frames: 0Excessive Collisions: 0Late Collisions: 0Tx Underrun: 0Carrier Sense Errors: 0Tx Octets: 1117837490Rx + Tx 64 Octet Frames: 250017Rx + Tx 65-127 Octet Frames: 68086Rx + Tx 128-255 Octet Frames: 34404Rx + Tx 256-511 Octet Frames: 6330Rx + Tx 512-1023 Octet Frames: 1307359Rx + Tx 1024-Up Octet Frames: 9162694Net Octets: 2139594688Rx Start of Frame Overruns: 2998092Rx Middle of Frame Overruns: 0Rx DMA Overruns: 2998092Rx DMA chan 0: head_enqueue: 1Rx DMA chan 0: tail_enqueue: 6988545Rx DMA chan 0: pad_enqueue: 0Rx DMA chan 0: misqueued: 571Rx DMA chan 0: desc_alloc_fail: 0Rx DMA chan 0: pad_alloc_fail: 0Rx DMA chan 0: runt_receive_buf: 0Rx DMA chan 0: runt_transmit_bu: 0Rx DMA chan 0: empty_dequeue: 0Rx DMA chan 0: busy_dequeue: 505265Rx DMA chan 0: good_dequeue: 6984450Rx DMA chan 0: requeue: 446Rx DMA chan 0: teardown_dequeue: 0Tx DMA chan 0: head_enqueue: 32715Tx DMA chan 0: tail_enqueue: 716822Tx DMA chan 0: pad_enqueue: 0Tx DMA chan 0: misqueued: 1284Tx DMA chan 0: desc_alloc_fail: 0Tx DMA chan 0: pad_alloc_fail: 0Tx DMA chan 0: runt_receive_buf: 0Tx DMA chan 0: runt_transmit_bu: 11816Tx DMA chan 0: empty_dequeue: 32712Tx DMA chan 0: busy_dequeue: 472547Tx DMA chan 0: good_dequeue: 749537Tx DMA chan 0: requeue: 22009Tx DMA chan 0: teardown_dequeue: 0

具体数据的含义,每个网卡驱动都有可能不同,需要查看网卡驱动的 struct ethtool_ops
(未完待续)

2.1.2.3 网卡 调优

调优之前,要确定目标:吞吐量(更高延迟) 或 高响应(更小延迟)。如果要吞吐量,可以在如下方面进行优化:

. 中断聚合,即调整中断次数,将多帧数据在一个中断内提取。这要求硬件支持,也要求更大的硬件缓冲。用户空间可通过ethtool -C <IFACE> rx-usecs N # 每 N 微妙 生成一次 RX 中端ethtool -C <IFACE> rx-frames N # 每 N 帧 数据生成一次 RX 中端ethtool -C <IFACE> tx-usecs N # 每 N 微妙 向网卡提交一次数据ethtool -C <IFACE> tx-frames N # 聚合 N 帧 向网卡提交一次数据等命令来进行配置:如果注重吞吐量,则将 N 调大;否则,N 应尽量小。
. 调整 ring buffer:更大的吞吐量,需要更多的 ring buffer 。
. 如果网卡驱动使用 NAPI 方式提取接收的数据,则要按需求适当调整 poll weight 。
......

(未完待续)

2.2 软件:网络协议栈 调试观察

2.2.1 查看 TCP 套接字信息数据

2.2.1.1 观察方法

内核通过 /proc/net/tcp (IPv4) 和 /proc/net/tcp6 (IPv6) 导出系统中 TCP 套接字的信息:

$ cat /proc/net/tcp # 导出 IPv4 TCP 套接字信息sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     0: 00000000:C875 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31122 1 0000000000000000 100 0 0 10 0                     1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 25241 1 0000000000000000 100 0 0 10 0                     2: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 40284 1 0000000000000000 100 0 0 10 0                     3: 00000000:0801 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 28384 1 0000000000000000 100 0 0 10 0                     4: 00000000:C887 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31106 1 0000000000000000 100 0 0 10 0                     5: 00000000:9C0D 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 28398 1 0000000000000000 100 0 0 10 0                     6: 00000000:D00F 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31114 1 0000000000000000 100 0 0 10 0                     7: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31028 1 0000000000000000 100 0 0 10 0
$ cat /proc/net/tcp6 # 导出 IPv6 TCP 套接字信息sl  local_address                         remote_address                        st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode0: 00000000000000000000000000000000:0016 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 25243 1 0000000000000000 100 0 0 10 01: 00000000000000000000000001000000:0277 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 40283 1 0000000000000000 100 0 0 10 02: 00000000000000000000000000000000:BCF9 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31110 1 0000000000000000 100 0 0 10 03: 00000000000000000000000000000000:0801 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 28395 1 0000000000000000 100 0 0 10 04: 00000000000000000000000000000000:9087 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31126 1 0000000000000000 100 0 0 10 05: 00000000000000000000000000000000:8D2D 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 28400 1 0000000000000000 100 0 0 10 06: 00000000000000000000000000000000:A2ED 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31118 1 0000000000000000 100 0 0 10 07: 00000000000000000000000000000000:006F 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31031 1 0000000000000000 100 0 0 10 0

简要说明下 /proc/net/tcp/proc/net/tcp6 的输出格式:

sl: socket 的一个索引编号,没有太多意义。
local_address: socket 本地地址,用 ip:port 的形式描述,数字都是十六进制,ip 需要手工转换为点分十进制格式。
remote_address: socket 远端地址,用 ip:port 的形式描述,数字都是十六进制,ip 需要手工转换为点分十进制格式。
st: 套接字状态,用十六进制数字描述,如 0A 表示 LISTEN 状态。这个和内核 include/net/tcp_states.h 中定义状态对应,如 0x0A 对应 TCP_LISTEN 。
tx_queue rx_queue:这两个信息的两个十六进制数据以 XXXXXXXX:XXXXXXXX 形式放在一起。对于处于不同状态的套接字,它们表示的含义有所不同。对于 LISTEN 状态的套接字(譬如 server 的监听套接字),rx_queue 表示 SYNC 队列长度(半连接);而对于其它状态的套接字,rx_queue 表示套接字接收缓冲队列长度,tx_queque 表示套接字发送缓冲队列长度。
tr tm->when: 套接字的定时器信息,如重传定时器。这两个信息的两个十六进制数据以 XX:XXXXXXXX 形式放在一起。tr 不为零表示套接字当前有某类型的定时器激活,tm->when 表示激活的定时剩余的超时时间。
retrnsmt: 超时重传次数。
uid: 套接字所属用户的 UID 。
timeout: 未应答的窗口探测包次数。
inode: 套接字对应的 inode 。
其它剩余信息:见后面对应的内核源码分析。

这些数字,很不直观,难于理解,可以通过 netstat -ant 命令来翻译下这些信息:

$ netstat -ant # 命令通过读取 /proc/net/tcp 和 /proc/net/tcp6,将其中的部分信息转换为人类友好格式
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:51317           0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:2049            0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:51335           0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:39949           0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:53263           0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN     
tcp6       0      0 :::22                   :::*                    LISTEN     
tcp6       0      0 ::1:631                 :::*                    LISTEN     
tcp6       0      0 :::48377                :::*                    LISTEN     
tcp6       0      0 :::2049                 :::*                    LISTEN     
tcp6       0      0 :::36999                :::*                    LISTEN     
tcp6       0      0 :::36141                :::*                    LISTEN     
tcp6       0      0 :::41709                :::*                    LISTEN     
tcp6       0      0 :::111                  :::*                    LISTEN
2.2.1.2 源码实现

本小节分析 netstat -ant 命令的 用户空间内核空间 相关的代码实现细节。

2.2.1.2.1 用户空间部分

netstat 是 net-tools 网络工具包中的其中一个,由该源码包中 netstat.c 实现。现在我们来简要分析下 netstat -ant 命令的 IPv4 协议相关部分:

/* net-tools/netstat.c */int main(int argc, char *argv[])
{...if (!flag_arg || flag_tcp) {i = tcp_info();if (i)return (i);}...
}static int tcp_info(void)
{// net-tools/lib/pathnames.h://// #define _PATH_PROCNET_TCP  "/proc/net/tcp"// #define _PATH_PROCNET_TCP6  "/proc/net/tcp6"INFO_GUTS6(_PATH_PROCNET_TCP, _PATH_PROCNET_TCP6, "AF INET (tcp)",tcp_do_one, "tcp", "tcp6");
}static void tcp_do_one(int lnr, const char *line, const char *prot)
{.../** 提取文件 /proc/net/tcp 或 /proc/net/tcp6 每一行内容的相关域。* 文件 /proc/net/tcp 或 /proc/net/tcp6 的 每一行记录了一个 套接字 的信息。*/num = sscanf(line,"%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %lu %*s\n",&d, local_addr, &local_port, rem_addr, &rem_port, &state,&txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode);.../** 打印一个 套接字 的信息。* 输出内容参考 2.1.1 小节中 netstat -ant 的输出。*/printf("%-4s  %6ld %6ld %-*s %-*s %-11s",prot, rxq, txq, (int)netmax(23,strlen(local_addr)), local_addr, (int)netmax(23,strlen(rem_addr)), rem_addr, _(tcp_state[state]));...
}

到此,对 netstat -ant 命令用户空间部分源码分析到此结束。

2.2.1.2.2 内核部分

上接 2.2.2.1 小节对 netstat -ant 源码的分析,这里对相关的内核部分做出简要分析。这里仅分析 IPv4 协议相关部分,对 IPv6 部分感兴趣的读者请自行阅读相关源码(tcp6_seq_show())。

/* net/ipv4/tcp_ipv4.c */
static struct pernet_operations tcp4_net_ops = {.init = tcp4_proc_init_net,.exit = tcp4_proc_exit_net,
};/* net/ipv4/af_inet.c */
inet_init()ipv4_proc_init()tcp4_proc_init()register_pernet_subsys(&tcp4_net_ops); /* 触发 tcp4_proc_init_net() 回调 */fs_initcall(inet_init);/* net/ipv4/tcp_ipv4.c */
static struct tcp_seq_afinfo tcp4_seq_afinfo = {.name  = "tcp",.family  = AF_INET,.seq_fops = &tcp_afinfo_seq_fops,.seq_ops = {.show  = tcp4_seq_show,},
};static int __net_init tcp4_proc_init_net(struct net *net)
{return tcp_proc_register(net, &tcp4_seq_afinfo); /* 创建 /proc/net/tcp 文件 */
}/** 读取 /proc/net/tcp 内容(如 netstat -ant 和 cat /proc/net/tcp),* 触发 tcp4_seq_show() 调用。*/
static int tcp4_seq_show(struct seq_file *seq, void *v)
{...if (sk->sk_state == TCP_TIME_WAIT) /* TIME-WAIT 态套接字信息 */get_timewait4_sock(v, seq, st->num);else if (sk->sk_state == TCP_NEW_SYN_RECV) /* 服务端 SYN-RECEIVED 状态套接字信息 */get_openreq4(v, seq, st->num);elseget_tcp4_sock(v, seq, st->num);
out:seq_pad(seq, '\n');...
}/* 本文只关注调用 get_tcp4_sock() 情形,对其它情形感兴趣的读者请自行阅读源码 */
static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
{.../* 套接字上 定时器 相关数据信息,如 超时重传定时器 信息 */if (icsk->icsk_pending == ICSK_TIME_RETRANS ||icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT ||icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {/* 重传定时器 处于激活状态 */timer_active = 1;timer_expires = icsk->icsk_timeout;} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {/* 零窗口探测 定时器 处于激活状态 */timer_active = 4;timer_expires = icsk->icsk_timeout;} else if (timer_pending(&sk->sk_timer)) {/* 延迟 ACK 或 keepallive 定时器 处于激活状态 */timer_active = 2;timer_expires = sk->sk_timer.expires;} else {/* 套接字上没有激活的定时器 */timer_active = 0;timer_expires = jiffies;}state = sk_state_load(sk);if (state == TCP_LISTEN) /* 如果是 server 监听套接字, */rx_queue = sk->sk_ack_backlog; /* @rx_queue 赋值为 SYN 队列(半连接队列) 长度 */else/* Because we don't lock the socket,* we might find a transient negative value.*/rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX ""%08X %5u %8d %lu %d %pK %lu %lu %u %u %d",i, src, srcp, dest, destp, state,tp->write_seq - tp->snd_una, /* 发送缓冲 */rx_queue,timer_active,jiffies_delta_to_clock_t(timer_expires - jiffies),icsk->icsk_retransmits, /* 重传次数 */from_kuid_munged(seq_user_ns(f), sock_i_uid(sk)),icsk->icsk_probes_out,sock_i_ino(sk), /* socket inode */refcount_read(&sk->sk_refcnt), sk,jiffies_to_clock_t(icsk->icsk_rto),jiffies_to_clock_t(icsk->icsk_ack.ato),(icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,tp->snd_cwnd, /* 发送窗口大小 */state == TCP_LISTEN ?fastopenq->max_qlen :(tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh));...
}

3. 后记

本文将持续更新。

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

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

相关文章

Sora平替?5款AI视频生成工具,真的好用吗?

最近Sora真的太火了&#xff0c;这不&#xff0c;人家接口都还没公开&#xff0c;各种Sora培训课就已经上线了&#xff0c;就离谱&#xff01; 不过呀&#xff0c;话说回来&#xff0c;AI视频生成的确是未来的趋势&#xff0c;如果AI视频真的能达到Sora演示的效果&#xff0c;…

web项目抢购模块测试

web项目抢购模块测试 抢购模块(先测后台,再测前台)流程抢购用例编写测试点--后台抢购用例编写测试点--前台用例设计 面试题1: 当你发现研发实现的结果与需求不一致时怎么办? 需求评审的时候:需要确认所有输入类型的校验是针对单独的输入框做的还是在最终提交时校验 抢购模块 需…

移速u盘怎么恢复彻底删除的文件,移速u盘彻底删除的文件如何恢复

移速u盘怎么恢复彻底删除的文件&#xff1f;在日常使用中&#xff0c;我们经常会遇到需要删除U盘中的文件的情况。然而&#xff0c;有时候我们可能会不小心将重要的文件彻底删除&#xff0c;导致数据的丢失。对于移速U盘用户来说&#xff0c;如何恢复彻底删除的文件成为了一个备…

注意:腾讯云轻量应用服务器地域选择攻略,选错很麻烦!

腾讯云轻量应用服务器地域如何选择&#xff1f;地域就近选择&#xff0c;北方选北京地域、南方选广州地域&#xff0c;华东地区选上海地域。广州上海北京地域有什么区别&#xff1f;哪个好&#xff1f;区别就是城市地理位置不同&#xff0c;其他的差不多&#xff0c;不区分好坏…

AI壁纸号一周增加上千粉丝,轻松变现的成功案例分享

前言 随着AI绘画技术的发展&#xff0c;传统的互联网副业壁纸号在新的技术加持下迎来了第二春。本文将分享一位壁纸号创作者的成功案例&#xff0c;并为大家提供创作门槛和硬件要求等相关信息。 该项目的创作门槛极低&#xff0c;基本上可以由AI完成内容创作。不过&#xff0…

界面开发框架DevExpress XAF v24.1新版预告 - 跨平台应用UI(二)

DevExpress XAF是一款强大的现代应用程序框架&#xff0c;允许同时开发ASP.NET和WinForms。XAF采用模块化设计&#xff0c;开发人员可以选择内建模块&#xff0c;也可以自行创建&#xff0c;从而以更快的速度和比开发人员当前更强有力的方式创建应用程序。 本文中的内容概述了…

【算法积累】辗转相除法

【算法积累】辗转相除法&#xff0c;python实现两种 辗转相除法&#xff08;又称欧几里得算法&#xff09;减法&#xff08;不常用&#xff09;代码实现执行结果 辗转相除法代码实现执行结果 辗转相除法&#xff08;又称欧几里得算法&#xff09; 又称欧几里得算法&#xff0c…

使用helm部署clickhouse

&#xff08;作者&#xff1a;陈玓玏&#xff09; 前置条件 已安装 Kubernetes 集群&#xff1b; 已安装 Helm 包管理工具。 部署 1 添加 RadonDB ClickHouse 的 Helm 仓库 helm repo add ck https://radondb.github.io/radondb-clickhouse-kubernetes/ helm repo upd…

【算法设计】实验四回溯算法(附源代码)

这里写目录标题 一、上机目的二、上机内容与要求三、上机步骤四、上机结果1、将课本5.2节算法改为程序&#xff0c;并输入数据及进行测试&#xff1b;2、自学5.4节&#xff0c;并完成符号三角形问题。 一、上机目的 1、通过回溯法的示例程序理解回溯法的基本思想&#xff1b; …

信号处理-探索相邻数据点之间的变化和关联性的操作方法

当前值减去前一个值&#xff0c;乘上当前值与前一个值差值的绝对值 当前值减去后一个值&#xff0c;乘上当前值与后一个值差值的绝对值。 意义何在&#xff1f; 当前值减去前一个值&#xff1a;表示当前数据点与前一个数据点之间的变化量。当前值与前一个值差值的绝对值&…

Pycharm的Project Structure (项目结构)

文章目录 一、Sources二、Tests三、Exeluded四、Namespace packages五、Templates六、Resources 一、Sources 源代码根目录&#xff1a;包含项目的主要源代码&#xff0c;它会在这个目录下搜索代码&#xff0c;然后自动补全和只能提示都通过这里的代码提供。若项目运行自定义代…

luatos框架中LVGL如何使用中文字体〈二〉编写脚本设置中文字体

本节内容&#xff0c;将和大家一同学习&#xff0c;在luatos环境中&#xff0c;使用lvgl库&#xff0c;一步步的编译固件、编写脚本&#xff0c;最终实现中文字体的显示。 芯片&#xff1a;AIR101 LCD屏&#xff1a;ST7789 上一节&#xff0c;我们一同学习了&#xff0c;硬件引…

使用vue动态在列表中添加或者删除某一行

** 使用vue动态在列表中添加或者删除某一行 ** 先看一下展示的效果&#xff1a; 好了上代码&#xff1a; 样式界面&#xff1a; <template><div class"container"><h4 style"margin-left: 20px;">线路停靠站站点</h4><el-b…

无缝集成 MongoDB Relational Migrator,Tapdata 提供关系型到 MongoDB 实时迁移优化方案

在去年的 MongoDB 用户大会纽约站上&#xff0c;MongoDB 正式宣布全面推出新工具 MongoDB Relational Migrator&#xff08;MongoDB RM&#xff09;&#xff0c;用以简化应用程序迁移和转换——即从传统关系型数据模型到现代的文档数据模型&#xff0c;助力组织快速提升运营效率…

数字图像处理 使用C#进行图像处理九 实现傅里叶变换

一、简述 傅立叶变换将图像分解为其正弦和余弦分量。换句话说,它将图像从空间域变换到频率域。这个想法是任何函数都可以用无限正弦函数和余弦函数之和来精确近似。傅里叶变换是实现此目的的一种方法。 网上有很多关于傅里叶变换的文章,这里就不进行赘述了,这里主要结合代码…

java中xml概述

1.xml 1.1概述【理解】 万维网联盟(W3C) 万维网联盟(W3C)创建于1994年&#xff0c;又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。 建立者&#xff1a; Tim Berners-Lee (蒂姆伯纳斯李)。 是Web技术领域最具权威和影响力的国际中立性技术标准机构。 到目前为…

element ui 中文离线文档(百度云盘下载)

一般内网开发上不了网&#xff0c;用离线版本比较方便&#xff0c;下载地址&#xff1a; https://download.csdn.net/download/li836779537/88355878?spm1001.2014.3001.5503 下载后里面有个 index.hrml 双击打开就可以用 效果如下&#xff1a;

Python程序设计基础——代码习题

1 __name__属性 import demodef main():if __name__ __main__:print(这个程序被直接运行。)elif __name__demo:print(这个程序作为模块被使用。) main()3.3 编写程序&#xff0c;生成包含1000个0~100之间的随机整数&#xff0c;并统计每个元素出现的次数。 import randomx[r…

助贷系统crm:帮助助贷机构实现高效的客户关系管理

助贷系统CRM&#xff08;客户关系管理系统&#xff09;是一种能够帮助助贷企业实现高效客户关系管理的工具&#xff0c;通过助贷系统CRM&#xff0c;助贷企业可以更好地管理企业客户信息&#xff0c;跟踪客户互动、提高客户满意度&#xff0c;从而促进业务增长。 1. 客户信息集…

vMware WorkStation创建虚拟机安装CentOS7,NAT模式配置网络

一、安装虚拟机 1、选择典型&#xff08;推荐&#xff09;配置 2、选择稍后安装操作系统 3、操作系统选择CentOS7 64位 4、虚拟机命名、选择安装位置 6、指定磁盘容量 7、指定磁盘容量 步骤&#xff1a; &#xff08;1&#xff09;、系统内存2GB &#xff08;2&#xff09;、…