我们一起学习了文件系统和磁盘 I/O 的工作原理,以及相应的性能分析和优化方法。接下来,我们将进入下一个重要模块—— Linux 的网络子系统。

由于网络处理的流程最复杂,跟我们前面讲到的进程调度、中断处理、内存管理以及 I/O 等都密不可分,所以,我把网络模块作为最后一个资源模块来讲解。

同 CPU、内存以及 I/O 一样,网络也是 Linux 系统最核心的功能。网络是一种把不同计算机或网络设备连接到一起的技术,它本质上是一种进程间通信方式,特别是跨系统的进程间通信,必须要通过网络才能进行。随着高并发、分布式、云计算、微服务等技术的普及,网络的性能也变得越来越重要。

网络模型

说到网络,我想你肯定经常提起七层负载均衡、四层负载均衡,或者三层设备、二层设备等等。那么,这里说的二层、三层、四层、七层又都是什么意思呢?

实际上,这些层都来自国际标准化组织制定的开放式系统互联通信参考模型(Open System Interconnection Reference Model),简称为 OSI 网络模型。

应用层,负责为应用程序提供统一的接口

表示层,负责把数据转换成兼容接收系统的格式

会话层,负责维护计算机之间的通信连接

传输层,负责为数据加上传输表头,形成数据包

网络层,负责数据的路由和转发

数据链路层,负责 MAC 寻址、错误侦测和改错

物理层,负责在物理网络中传输数据帧

但是 OSI 模型还是太复杂了,也没能提供一个可实现的方法。所以,在 Linux 中,我们实际上使用的是另一个更实用的四层模型,即 TCP/IP 网络模型。

TCP/IP 模型,把网络互联的框架分为应用层、传输层、网络层、网络接口层等四层,其中,

应用层,负责向用户提供一组应用程序,比如 HTTP、FTP、DNS 等。

传输层,负责端到端的通信,比如 TCP、UDP 等。

网络层,负责网络包的封装、寻址和路由,比如 IP、ICMP 等。

网络接口层,负责网络包在物理网络中的传输,比如 MAC 寻址、错误侦测以及通过网卡传输网络帧等。

为了帮你更形象理解 TCP/IP 与 OSI 模型的关系,我画了一张图,如下所示:

image.png

当然了,虽说 Linux 实际按照 TCP/IP 模型,实现了网络协议栈,但在平时的学习交流中,我们习惯上还是用 OSI 七层模型来描述。比如,说到七层和四层负载均衡,对应的分别是 OSI 模型中的应用层和传输层(而它们对应到 TCP/IP 模型中,实际上是四层和三层)。

OSI七层和TCP/IP四层的关系

OSI引入了服务、接口、协议、分层的概念,TCP/IP借鉴了OSI的这些概念建立TCP/IP模型。

OSI先有模型,后有协议,先有标准,后进行实践;而TCP/IP则相反,先有协议和应用再提出了模型,且是参照的OSI模型。

OSI是一种理论下的模型,而TCP/IP已被广泛使用,成为网络互联事实上的标准。

Linux 网络栈

有了 TCP/IP 模型后,在进行网络传输时,数据包就会按照协议栈,对上一层发来的数据进行逐层处理;然后封装上该层的协议头,再发送给下一层。

当然,网络包在每一层的处理逻辑,都取决于各层采用的网络协议。比如在应用层,一个提供 REST API 的应用,可以使用 HTTP 协议,把它需要传输的 JSON 数据封装到 HTTP 协议中,然后向下传递给 TCP 层。

而封装做的事情就很简单了,只是在原来的负载前后,增加固定格式的元数据,原始的负载数据并不会被修改。

比如,以通过 TCP 协议通信的网络包为例,通过下面这张图,我们可以看到,应用程序数据在每个层的封装格式。

image.png

这些新增的头部和尾部,增加了网络包的大小,但我们都知道,物理链路中并不能传输任意大小的数据包。网络接口配置的最大传输单元(MTU),就规定了最大的 IP 包大小。在我们最常用的以太网中,MTU 默认值是 1500(这也是 Linux 的默认值)。

一旦网络包超过 MTU 的大小,就会在网络层分片,以保证分片后的 IP 包不大于 MTU 值。显然,MTU 越大,需要的分包也就越少,自然,网络吞吐能力就越好。

理解了 TCP/IP 网络模型和网络包的封装原理后,你很容易能想到,Linux 内核中的网络栈,其实也类似于 TCP/IP 的四层结构。如下图所示,就是 Linux 通用 IP 网络栈的示意图:

image.png

我们从上到下来看这个网络栈,你可以发现,

最上层的应用程序,需要通过系统调用,来跟套接字接口进行交互;

套接字的下面,就是我们前面提到的传输层、网络层和网络接口层;

最底层,则是网卡驱动程序以及物理网卡设备。

这里我简单说一下网卡。网卡是发送和接收网络包的基本设备。在系统启动过程中,网卡通过内核中的网卡驱动程序注册到系统中。而在网络收发过程中,内核通过中断跟网卡进行交互。

再结合前面提到的 Linux 网络栈,可以看出,网络包的处理非常复杂。所以,网卡硬中断只处理最核心的网卡数据读取或发送,而协议栈中的大部分逻辑,都会放到软中断中处理。

Linux 网络收发流程

我们先来看网络包的接收流程。

当一个网络帧到达网卡后,网卡会通过 DMA 方式,把这个网络包放到收包队列中;然后通过硬中断,告诉中断处理程序已经收到了网络包。

接着,网卡中断处理程序会为网络帧分配内核数据结构(sk_buff),并将其拷贝到 sk_buff 缓冲区中;然后再通过软中断,通知内核收到了新的网络帧。

接下来,内核协议栈从缓冲区中取出网络帧,并通过网络协议栈,从下到上逐层处理这个网络帧。比如,

在链路层检查报文的合法性,找出上层协议的类型(比如 IPv4 还是 IPv6),再去掉帧头、帧尾,然后交给网络层。

网络层取出 IP 头,判断网络包下一步的走向,比如是交给上层处理还是转发。当网络层确认这个包是要发送到本机后,就会取出上层协议的类型(比如 TCP 还是 UDP),去掉 IP 头,再交给传输层处理。

传输层取出 TCP 头或者 UDP 头后,根据 < 源 IP、源端口、目的 IP、目的端口 > 四元组作为标识,找出对应的 Socket,并把数据拷贝到 Socket 的接收缓存中。

最后,应用程序就可以使用 Socket 接口,读取到新接收到的数据了。

为了更清晰表示这个流程,我画了一张图,这张图的左半部分表示接收流程,而图中的粉色箭头则表示网络包的处理路径。

image.png

网络包的发送流程

了解网络包的接收流程后,就很容易理解网络包的发送流程。网络包的发送流程就是上图的右半部分,很容易发现,网络包的发送方向,正好跟接收方向相反。

首先,应用程序调用 Socket API(比如 sendmsg)发送网络包。

由于这是一个系统调用,所以会陷入到内核态的套接字层中。套接字层会把数据包放到 Socket 发送缓冲区中。

接下来,网络协议栈从 Socket 发送缓冲区中,取出数据包;再按照 TCP/IP 栈,从上到下逐层处理。比如,传输层和网络层,分别为其增加 TCP 头和 IP 头,执行路由查找确认下一跳的 IP,并按照 MTU 大小进行分片。

分片后的网络包,再送到网络接口层,进行物理地址寻址,以找到下一跳的 MAC 地址。然后添加帧头和帧尾,放到发包队列中。这一切完成后,会有软中断通知驱动程序:发包队列中有新的网络帧需要发送。

最后,驱动程序通过 DMA ,从发包队列中读出网络帧,并通过物理网卡把它发送出去。

小结

多台服务器通过网卡、交换机、路由器等网络设备连接到一起,构成了相互连接的网络。由于网络设备的异构性和网络协议的复杂性,国际标准化组织定义了一个七层的 OSI 网络模型,但是这个模型过于复杂,实际工作中的事实标准,是更为实用的 TCP/IP 模型。

TCP/IP 模型,把网络互联的框架,分为应用层、传输层、网络层、网络接口层等四层,这也是 Linux 网络栈最核心的构成部分。

应用程序通过套接字接口发送数据包,先要在网络协议栈中从上到下进行逐层处理,最终再送到网卡发送出去。

而接收时,同样先经过网络栈从下到上的逐层处理,最终才会送到应用程序。

FAQ

我结合网络上查阅的资料和文章中的内容,总结了下网卡收发报文的过程,不知道是否正确:

内核分配一个主内存地址段(DMA缓冲区),网卡设备可以在DMA缓冲区中读写数据(应该是网络包到了之后,才分配DMA缓冲区

)

当来了一个网络包,网卡将网络包写入DMA缓冲区,写完后通知CPU产生硬中断

硬中断处理程序锁定当前DMA缓冲区,然后将网络包拷贝到另一块内存区,清空并解锁当前DMA缓冲区,然后通知软中断去处理网络包。

当发送数据包时,与上述相反。链路层将数据包封装完毕后,放入网卡的DMA缓冲区,并调用系统硬中断,通知网卡从缓冲区读取并发送数据。

了解 Linux 网络的基本原理和收发流程后,你肯定迫不及待想知道,如何去观察网络的性能情况。具体而言,哪些指标可以用来衡量 Linux 的网络性能呢?

性能指标

实际上,我们通常用带宽、吞吐量、延时、PPS(Packet Per Second)等指标衡量网络的性能。

带宽,表示链路的最大传输速率,单位通常为 b/s (比特 / 秒)

吞吐量,表示单位时间内成功传输的数据量,单位通常为 b/s(比特 / 秒)或者 B/s(字节 / 秒)。吞吐量受带宽限制,而吞吐量 / 带宽,也就是该网络的使用率。

延时,表示从网络请求发出后,一直到收到远端响应,所需要的时间延迟。在不同场景中,这一指标可能会有不同含义。比如,它可以表示,建立连接需要的时间(比如 TCP 握手延时),或一个数据包往返所需的时间(比如 RTT)。

PPS,是 Packet Per Second(包 / 秒)的缩写,表示以网络包为单位的传输速率。PPS 通常用来评估网络的转发能力,比如硬件交换机,通常可以达到线性转发(即 PPS 可以达到或者接近理论最大值)。而基于 Linux 服务器的转发,则容易受网络包大小的影响。

除了这些指标,网络的可用性(网络能否正常通信)、并发连接数(TCP 连接数量)、丢包率(丢包百分比)、重传率(重新传输的网络包比例)等也是常用的性能指标。

网络配置

分析网络问题的第一步,通常是查看网络接口的配置和状态。你可以使用 ifconfig 或者 ip 命令,来查看网络的配置。我个人更推荐使用 ip 工具,因为它提供了更丰富的功能和更易用的接口。

ifconfig 和 ip 分别属于软件包 net-tools 和 iproute2,iproute2 是 net-tools 的下一代。通常情况下它们会在发行版中默认安装。但如果你找不到 ifconfig 或者 ip 命令,可以安装这两个软件包。

以网络接口 eth0 为例,你可以运行下面的两个命令,查看它的配置和状态:

$ ifconfig eth0

eth0: flags=4163 mtu 1500

inet 10.240.0.30 netmask 255.240.0.0 broadcast 10.255.255.255

inet6 fe80::20d:3aff:fe07:cf2a prefixlen 64 scopeid 0x20

ether 78:0d:3a:07:cf:3a txqueuelen 1000 (Ethernet)

RX packets 40809142 bytes 9542369803 (9.5 GB)

RX errors 0 dropped 0 overruns 0 frame 0

TX packets 32637401 bytes 4815573306 (4.8 GB)

TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

$ ip -s addr show dev eth0

2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000

link/ether 78:0d:3a:07:cf:3a brd ff:ff:ff:ff:ff:ff

inet 10.240.0.30/12 brd 10.255.255.255 scope global eth0

valid_lft forever preferred_lft forever

inet6 fe80::20d:3aff:fe07:cf2a/64 scope link

valid_lft forever preferred_lft forever

RX: bytes packets errors dropped overrun mcast

9542432350 40809397 0 0 0 193

TX: bytes packets errors dropped carrier collsns

4815625265 32637658 0 0 0 0

你可以看到,ifconfig 和 ip 命令输出的指标基本相同,只是显示格式略微不同。比如,它们都包括了网络接口的状态标志、MTU 大小、IP、子网、MAC 地址以及网络包收发的统计信息。

第一,网络接口的状态标志。ifconfig 输出中的 RUNNING ,或 ip 输出中的 LOWER_UP ,都表示物理网络是连通的,即网卡已经连接到了交换机或者路由器中。如果你看不到它们,通常表示网线被拔掉了。

第二,MTU 的大小。MTU 默认大小是 1500,根据网络架构的不同(比如是否使用了 VXLAN 等叠加网络),你可能需要调大或者调小 MTU 的数值。

第三,网络接口的 IP 地址、子网以及 MAC 地址。这些都是保障网络功能正常工作所必需的,你需要确保配置正确。

第四,网络收发的字节数、包数、错误数以及丢包情况,特别是 TX 和 RX 部分的 errors、dropped、overruns、carrier 以及 collisions 等指标不为 0 时,通常表示出现了网络 I/O 问题。其中:

errors 表示发生错误的数据包数,比如校验错误、帧同步错误等;

dropped 表示丢弃的数据包数,即数据包已经收到了 Ring Buffer,但因为内存不足等原因丢包;

overruns 表示超限数据包数,即网络 I/O 速度过快,导致 Ring Buffer 中的数据包来不及处理(队列满)而导致的丢包;

carrier 表示发生 carrirer 错误的数据包数,比如双工模式不匹配、物理电缆出现问题等;

collisions 表示碰撞数据包数。

套接字信息

ifconfig 和 ip 只显示了网络接口收发数据包的统计信息,但在实际的性能问题中,网络协议栈中的统计信息,我们也必须关注。你可以用 netstat 或者 ss ,来查看套接字、网络栈、网络接口以及路由表的信息。

我个人更推荐,使用 ss 来查询网络的连接信息,因为它比 netstat 提供了更好的性能(速度更快)。

比如,你可以执行下面的命令,查询套接字信息:

# head -n 3 表示只显示前面3行

# -l 表示只显示监听套接字

# -n 表示显示数字地址和端口(而不是名字)

# -p 表示显示进程信息

$ netstat -nlp | head -n 3

Active Internet connections (only servers)

Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name

tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN 840/systemd-resolve

# -l 表示只显示监听套接字

# -t 表示只显示 TCP 套接字

# -n 表示显示数字地址和端口(而不是名字)

# -p 表示显示进程信息

$ ss -ltnp | head -n 3

State Recv-Q Send-Q Local Address:Port Peer Address:Port

LISTEN 0 128 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=840,fd=13))

LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1459,fd=3))

netstat 和 ss 的输出也是类似的,都展示了套接字的状态、接收队列、发送队列、本地地址、远端地址、进程 PID 和进程名称等。

其中,接收队列(Recv-Q)和发送队列(Send-Q)需要你特别关注,它们通常应该是 0。当你发现它们不是 0 时,说明有网络包的堆积发生。当然还要注意,在不同套接字状态下,它们的含义不同。

当套接字处于连接状态(Established)时,

Recv-Q 表示套接字缓冲还没有被应用程序取走的字节数(即接收队列长度)。

而 Send-Q 表示还没有被远端主机确认的字节数(即发送队列长度)。

当套接字处于监听状态(Listening)时,

Recv-Q 表示全连接队列的长度。

而 Send-Q 表示全连接队列的最大长度。

所谓全连接,是指服务器收到了客户端的 ACK,完成了 TCP 三次握手,然后就会把这个连接挪到全连接队列中。这些全连接中的套接字,还需要被 accept() 系统调用取走,服务器才可以开始真正处理客户端的请求。

与全连接队列相对应的,还有一个半连接队列。所谓半连接是指还没有完成 TCP 三次握手的连接,连接只进行了一半。服务器收到了客户端的 SYN 包后,就会把这个连接放到半连接队列中,然后再向客户端发送 SYN+ACK 包。

协议栈统计信息

类似的,使用 netstat 或 ss ,也可以查看协议栈的信息:

$ netstat -s

...

Tcp:

3244906 active connection openings

23143 passive connection openings

115732 failed connection attempts

2964 connection resets received

1 connections established

13025010 segments received

17606946 segments sent out

44438 segments retransmitted

42 bad segments received

5315 resets sent

InCsumErrors: 42

...

$ ss -s

Total: 186 (kernel 1446)

TCP: 4 (estab 1, closed 0, orphaned 0, synrecv 0, timewait 0/0), ports 0

Transport Total IP IPv6

* 1446 - -

RAW 2 1 1

UDP 2 2 0

TCP 4 3 1

...

这些协议栈的统计信息都很直观。ss 只显示已经连接、关闭、孤儿套接字等简要统计,而 netstat 则提供的是更详细的网络协议栈信息。

比如,上面 netstat 的输出示例,就展示了 TCP 协议的主动连接、被动连接、失败重试、发送和接收的分段数量等各种信息。

网络吞吐和 PPS

接下来,我们再来看看,如何查看系统当前的网络吞吐量和 PPS。在这里,我推荐使用我们的老朋友 sar,在前面的 CPU、内存和 I/O 模块中,我们已经多次用到它。

给 sar 增加 -n 参数就可以查看网络的统计信息,比如网络接口(DEV)、网络接口错误(EDEV)、TCP、UDP、ICMP 等等。执行下面的命令,你就可以得到网络接口统计信息:

# 数字1表示每隔1秒输出一组数据

$ sar -n DEV 1

Linux 4.15.0-1035-azure (ubuntu) 01/06/19 _x86_64_ (2 CPU)

13:21:40 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil

13:21:41 eth0 18.00 20.00 5.79 4.25 0.00 0.00 0.00 0.00

13:21:41 docker0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

13:21:41 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

这儿输出的指标比较多,我来简单解释下它们的含义。

rxpck/s 和 txpck/s 分别是接收和发送的 PPS,单位为包 / 秒

rxkB/s 和 txkB/s 分别是接收和发送的吞吐量,单位是 KB/ 秒

rxcmp/s 和 txcmp/s 分别是接收和发送的压缩数据包数,单位是包 / 秒

%ifutil 是网络接口的使用率,即半双工模式下为 (rxkB/s+txkB/s)/Bandwidth,而全双工模式下为 max(rxkB/s, txkB/s)/Bandwidth

其中,Bandwidth 可以用 ethtool 来查询,它的单位通常是 Gb/s 或者 Mb/s,不过注意这里小写字母 b ,表示比特而不是字节。我们通常提到的千兆网卡、万兆网卡等,单位也都是比特。如下你可以看到,我的 eth0 网卡就是一个千兆网卡:

其中,Bandwidth 可以用 ethtool 来查询,它的单位通常是 Gb/s 或者 Mb/s,不过注意这里小写字母 b ,表示比特而不是字节。我们通常提到的千兆网卡、万兆网卡等,单位也都是比特。如下你可以看到,我的 eth0 网卡就是一个千兆网卡:

$ ethtool eth0 | grep Speed

Speed: 1000Mb/s

小结

我们通常使用带宽、吞吐量、延时等指标,来衡量网络的性能;相应的,你可以用 ifconfig、netstat、ss、sar、ping 等工具,来查看这些网络的性能指标。

小狗同学问到: 老师,您好 ss —lntp 这个 当session处于listening中 rec-q 确定是 syn的backlog吗?

A: Recv-Q为全连接队列当前使用了多少。 中文资料里这个问题讲得最明白的文章:https://mp.weixin.qq.com/s/yH3PzGEFopbpA-jw4MythQ

看了源码发现,这个地方讲的有问题.关于ss输出中listen状态套接字的Recv-Q表示全连接队列当前使用了多少,也就是全连接队列的当前长度,而Send-Q表示全连接队列的最大长度

linux 设置固定网络转发_关于 Linux 网络,你必须知道这些相关推荐

  1. linux设置nexus开机自启动_在linux中使用nexus搭建maven私服

    首先介绍下为什么要搭建maven私服,简单点说就是就是把项目工程中的Jar包放在一个服务器上,每次Jar包的修改都能去私服上面Down到本地.可以对整个项目组的人形成一个统一的管理. 2.下载完之后就 ...

  2. 银河麒麟服务器 linux 设置 固定IP地址

    银河麒麟服务器 linux 设置 固定IP地址 /etc/network/interfaces # interfaces(5) file used by ifup(8) and ifdown(8) # ...

  3. VMware虚拟机Linux设置固定ip和自定义域名

    ​ 在使用VMware时,有时候需要用外部终端ssh连接自己的虚拟机,但是通常虚拟机默认的是DHCP协议,ip可能动态变化,不方便连接.于是我们可以设置固定ip,并且设置hosts文件实现自定义域名. ...

  4. linux设置静态ip地址_什么是静态IP地址,与动态IP地址比较以及如何为Windows和Linux设置?...

    linux设置静态ip地址 IP addresses are the core mechanism of Computer networks. The IP address is used to sp ...

  5. linux与固定ip校时命令,linux通过ntpdate网络校时

    linux通过ntpdate网络校时 目前 Linux 系统上面有两个时间喔,一个是 Linux 系统,另一个则是 BIOS 时间(真正的硬件记录的时间)! 我们可以使用 date 这个指令来手动修正 ...

  6. 如何给linux设置固定ip地址,设置Linux系统的固定IP地址

    用ifconfig配置的ip地址是临时的,重启会无效,设置固定IP的方法如下: #vi etc/sysconfig/network-scrips/ifcfg-eth0 把 BOOTPROTO=dhcp ...

  7. LINUX设置固定IP上网方法

    由于开发,需要频繁连接开发机,但是如果不设置静态ip地址,每次重启后或断开网络连接口,ip地址会改变,需要设置固定的ip地址,下来就是怎么设置固定ip的方法 默认配置 输入命令:vim /etc/sy ...

  8. Linux设置固定IP连接wifi

    环境:TPU上的Debian系统 设置固定IP 参考:https://blog.csdn.net/nzjdsds/article/details/77197246 例如想要设置网络的信息如下 IP地址 ...

  9. linux:使用yum安装_首次使用Linux:30个安装案例

    linux:使用yum安装 Linux内核在8月25日(星期六)再大一岁.26年前,创建者和BDFL Linus Torvalds可能已经感觉到Linux只能满足一个人的需求. 但是今天我们知道,它改 ...

  10. linux运维工程师培训课程_《Linux运维工程师必学技能》完整版视频课程专题(1.0)...

    高性能负载均衡集群HAProxy实战视频课程 13节 4小时7分钟 课程目标: 欢迎大家加入 51CTO学院Linux交流群575837909,与喜欢学习Linux小伙伴们做朋友,一起为梦想增值.高性 ...

最新文章

  1. 令人拍案叫绝的Wasserstein GAN
  2. 魅族的android m l,Android M 外部存储剖析
  3. # 中小型网络构建-ACL
  4. 关联查询取更新时间_数据分析之sql复杂查询
  5. 对于变态数据搜索的心得
  6. 10个凭证类型的速记
  7. java中椭圆类_Java中的Graphics2D类基本使用的教程
  8. 多租户saas 架构_[译/注] Force.com 多租户互联网应用开发平台的设计
  9. android http 慢,android httpurlconnection數據連接速度慢
  10. NYOJ 3(多边形重心)
  11. 国内稳定的暗黑2服务器,国内暗黑2战网的基本概念介绍
  12. coreseek mysql_centos+php+coreseek+sphinx+mysql之一coreseek安装篇
  13. python滤波器处理数据的优点_使用Python对原始信号应用合适的butterworth滤波器
  14. 上线长辈模式,饿了么能拿下银发市场吗?
  15. 后台页面-制作铃铛带数字消息提示样式
  16. 坐标系、欧拉角、旋转矩阵、四元数
  17. 【云栖大会】一场7年后的狂欢,云栖大会除了“飞天”还有什么
  18. 差速驱动机器人轮间距校准
  19. Human Parsing 数据预处理使用指南
  20. bim计算机考试,2020年BIM等级考试练习题(十四)

热门文章

  1. 15种排序算法可视化展示
  2. cout和printf的区别
  3. 拓端tecdat|R语言GJR-GARCH和GARCH波动率预测普尔指数时间序列和Mincer Zarnowitz回归、DM检验、JB检验
  4. 图像处理-图像尺寸变换
  5. (2)机器学习_train_test_split
  6. springboot学习笔记1
  7. 江苏省计算机二级C操作题汇编
  8. 机器学习八大优质数据库
  9. R-CNN学习笔记5:Faster R-CNN
  10. python数字图像处理(13):基本形态学滤波