关于2023年嘛,还算是比较顺利的一年,电吉他也玩的比较多(虽然几乎是每天都在练习),还顺势上了两次舞台。
工作上的话就是接触比较多IB和RoCE相关的工作吧,学习了挺多,后续尝试更新下IB和RoCE的内容。
希望电吉他能更精进一层,工作上能再做一些更加自动化的东西吧,优化老屎山。
在写的时候突然发现2024已经过去了3个月,好可怕!
]]>当链路初始启动时,将会把 拥塞控制窗口(cwnd) 设置为1~10个 最大报文段(MSS) ,具体则根据当前机器的TCP协议实现而有所变化。
发送端将根据cwnd的大小来发送数据,在每一个收到有效回复的RTT内,cwnd都会增加1个MSS的大小。考虑到接收端有延迟,大体上是在呈指数级增长。
如果带宽为W,所耗时间如所示计算方法,经过一定的时间之后就可以占满带宽。
拥塞避免的主要思想是 线性增加,乘积减少(AIMD)。
即 Additive Increase, Multiplicative Decrease.
TCP协议中设置了名为 慢启动阈值(ssthresh) 的变量,当cwnd达到ssthresh时,cwnd将会转换为线性增长的拥塞避免阶段。
当数据包在一定时间内没有收到确认时,将会假设未确认是由于网络发生了拥塞,但事实上有些网络环境只是链路质量过于差。
这时候将会进行减半的操作,进而避免产生更多的拥塞。
例如发送了15个包,但只收到了前5个包的确认。后10个包的数据量的一半,设置为新的ssthresh值。
接着把cwnd的值重新设置为1个MSS的大小,再次回到慢启动阶段。
有两个事件会导致快速重传的发生,超时重传和重复确认。
超时重传是TCP协议在发送端每发送一个报文段就会设置一个计时器,如果接收端在发送确认回来的时间超过了计时器的等待时间,将会重新发送该报文段。
重复确认是接收端收到未按正确顺序的包时,接收端立刻将最后一次确认过的包再次发送ACK给发送端,该重复阈值一般为3。
即接收端收到同一包的ACK四次之后,便会从ACK中最后一次确认过的包开始继续发包。
一旦发生这两种情况,ssthresh的值和cwnd的值将会与拥塞避免阶段的设置相同,再次回到拥塞避免阶段。
在上述的快速重传阶段之后,又添加了后来的快速恢复阶段。
当发生重复确认时,将进入快速恢复阶段。
这时候将会先按照快速重传阶段的设置把cwnd和ssthresh设置好,接着把cwnd设置为ssthresh加上三个MSS的大小,添加三个的原因一般是重传三次ACK,随后重传丢失了的数据包。
当再次收到重复的ACK时,cwnd将会递增1,并尝试发送间下一个报文段。
这时如果收到新的ACK时,cwnd的值将更改现在的ssthresh的值。
即表示重传已经结束,可以按照顺序继续发送数据,再次回到拥塞避免阶段。
TCP/IP协议簇作为当前网络的基本,在保持可靠传输的前提下,承担着流量控制、拥塞控制等任务,以获取高性能数据传输。
TCP协议基本的拥塞控制算法有四个阶段,分别是慢启动阶段、拥塞避免阶段、快速重传阶段、快速恢复阶段。
纵观来看,拥塞控制算法的基本运作流程都可以简化为修改最终数据流,而在这一步不可避免地就要修改 拥塞控制窗口(cwnd)。
一般的拥塞控制算法通过接收端和发送端发包的确认情况和丢失情况,来估计网络是否产生了拥塞,进而避免网络崩溃。
但在实际应用中,这种预估的方法会比真正的拥塞发生要慢上许多。
为解决这一问题,计算机科学家们想过很多办法,比如及早将路由器缓存的情况报告给接收端。
即使用TCP协议的 拓展显式拥塞通知ECN ,当路由器或链路发生拥塞时,把请求发送数据速率降低的信号发给发送端。
仅当配合 活动队列管理(AQM) 策略的时候,ECN才能有效地使用,它依赖于AQM的精确度,而且在高度拥塞的网络环境中性能急剧降低。
另一种方法是测量 RTT(数据包往返时延) ,当RTT增大时,就降低发送数据的速率。
其中的代表是BBR算法,与以往大部分拥塞算法基于丢包来作为拥塞信号不同,BBR算法会主动检测RTT。
普通的拥塞控制算法是由事件驱动的。
不管是丢包还是RTT的增加,都被视为一种严重的拥塞事件,这些严重的拥塞事件导致TCP拥塞控制系统在寻找当前带宽、避免拥塞发生、探测在当前状态下还能使用的带宽这三种状态之间切换,最终的结果也就是导致促使拥塞算法调整拥塞控制窗口的大小。
只要拥塞控制状态机检测到拥塞事件的发生,它就可以直接控制这些状态的切换,而不管它是否实际发生,结果都会导致降低拥塞控制窗口的值。
所以在这点上,Reno算法和Cubic算法的拥塞控制窗口调整是被动的。
BBR算法具有内置的状态机制,不受TCP拥塞控制状态机的控制。
它可以完成所有的状态检测和切换,而不会受到外部干扰。
BBR算法将定期尝试检测是否有更多带宽并尽可能多地使用它,如果没有,它将返回到先前的状态。
BBR状态机的维持。
BBR算法的主体由一个状态机构成,共有四个状态,STARTUP,DRAIN,PROBE_BW,PROBE_RTT。
将 bw(瞬时带宽值) 乘以一个内置的 增益系数(pacing gain) 得到 pacing rate(传输速率) ,以及对RTT值的持续跟踪,BBR算法会根据上述计算的结果在这四个状态之间自由地来回切换。
其目的也是抛弃原有的丢包反馈,使得充分利用带宽。
瞬时带宽的计算。
计算bw(瞬时带宽值),这是BBR算法中所有计算的基础和规则。
BBR算法将会根据当前获得地瞬时带宽值以及其所处的状态来计算 pacing rate(传输速率) 以及 cwnd(拥塞控制窗口) ,BBR算法的简单高效正是源于此。
系统将会自动跟踪迄今为止最大的瞬时带宽。
实时跟踪RTT。
BBR算法之所以能够获取很高的带宽利用率,是因为它使用了状态机内的两个状态交替测量最大带宽值和最小RTT,得出吞吐率与 RTT的乘积(BDP) 的结果如公式所示。
能利用到这个最大的带宽容量,是BBR算法的最终目标,也正是这个目标最终驱动了cwnd的计算。系统会自动跟踪当前为止最小RTT。
BBR算法最主要维持两个核心的控制参数,pacing rate(传输速率) 和 cwnd(拥塞控制窗口)。
传输速率的计算
拥塞窗口的计算
当然,事物存在它的两面性,代价是什么?
BBR自出世以来已经收到了一些批评,首先,因为它倾向于抢占Cubic算法的带宽,在网络公平性上明显存在不足;
其次BBR的机制会导致高重传率;
第三点是在Wi-Fi环境下用户的网速明显变慢。
综合来看,BBR与Cubic相比只能说互有优劣,各有其擅长的领域。
针对BBR的问题,BBRv2的目标就是解决第一版中存在的一些主要缺点,其改进包括了还使用聚合/运行中的参数增强了网络建模,并增加了对 ECN(显式拥塞通知) 的支持等。
为便于理解,我们可以通过这样一张表来了解这几个拥塞算法的异同。
CUBIC | BBR v1 | BBR v2 | |
---|---|---|---|
Model parameters for the state machine | N/A | Troughput, RTT | Throughput, RTT, max aggregation, max inflight |
Loss | Reduce cwnd by 30% on window by any loss | N/A | Explicit loss rate target |
ECN | RFC3168 (Classic ECN) | N/A | DCTCP-inspired ECN |
Startup | Slow-start until RTT rises (Hystart) or any loss | Slow-start until throughput plateaus | Slow-start until throughput plateaus or ECN/Loss rate > target |
早在2019年,Dropbox 就已经在自家的数据中心边缘网络进行尝试使用BBRv2算法,并在下面的博客记载了详细的过程。
Evaluating BBRv2 on the Dropbox Edge Network
“在我们的测试中,BBRv2显示了以下特性:
总的来说,BBRv2在BBRv1基础上有了很大的改进,而且在需要更高带宽的情况下,它更接近于成为Reno/CUBIC的完全替代品。
添加实验性的ECN支持,我们甚至可以看到他可以成为 Datacenter TCP (DCTCP) 的完全替代者。”
1 | package_name set to manually installed |
你有没有想过这条消息是什么意思,为什么你没有在所有包上看到它?让我在本篇中分享一些细节。
那么从最常见的场景开始:复制文件和复制目录。
当你尝试安装已安装的库或开发包时,你会看到这个输出。此依赖包是与另一个包一起自动安装的。如果删除了主包,则使用 apt autoremove 命令删除依赖包。
但是由于你试图显式安装依赖包,你的 Debian 系统认为你需要这个包独立于主包。因此,该软件包被标记为手动安装,不会被自动删除。
不是很清楚,对吧?以 在 Debian 上安装 inxi 为例。
由于主 inxi 包依赖于许多其他包,因此这些包会自动安装。
1 | sudo apt install inxi |
比如我们检查第一个新安装的 bind9-dnsutils,会发现它被标记为“自动”。
这表明这些软件包是跟着 inxi 自动安装的,当 inxi 被卸载时,它们会被 apt autoremove 命令自动删除。
1 | apt list --installed | grep bind9-dnsutils |
假设你出于某种原因考虑安装 bind9-dnsutils,如果运行 apt install 命令,系统会告诉你该软件包已安装。
同时,它将标记从自动更改为手动,因为系统认为在尝试手动安装表明需要此包。
1 | sudo apt install bind9-dnsutils |
现在,删除 inxi 并运行 autoremove 命令。你可以看到 bind9-dnsutils 不在要删除的软件包列表中,并且它仍然在系统上面。
1 | sudo apt remove inxi |
1 | sudo apt-mark auto bind9-dnsutils |
这不是一个需要注意的错误,也不会中断你任何在系统中的工作。
但是,了解这些小事会增加你的知识。
]]>首先展示下如何安装inxi。
inxi也算是一个非常流行的工具,所以在大多数Linux发行版仓库中都可以轻松获取到该工具。
不过还没有流行到各大Linux发行版默认就安装了该软件,所以需要自己安装一下。
博主日常用RHEL系,就只展示下CentOS7的命令。
1 | dnf install epel-release -y |
直接运行inxi的效果如下。
1 | inxi |
可以简要浏览这些信息:
带上-b
可以获得更为详细的信息,博主随便拉了个常用的服务器跑了一次。
1 | System: Host: normal-server Kernel: 5.10.6-1.el7.elrepo.x86_64 x86_64 bits: 64 |
信息还挺多的,不过没有显卡。
使用-A
获得Audio信息,即音频输入输出设备。
使用-B
是Battery信息。
使用-G
是图形显示相关信息。
以上三个在服务器上都没有……
使用-F
则是获得比-b
更为详尽的信息。
1 | System: Host: normal-server Kernel: 5.10.6-1.el7.elrepo.x86_64 x86_64 bits: 64 |
甚至能看到虚拟网卡和RAID卡的信息。
使用-I
显示当前进程数、开机时间、shell、内存使用情况和inxi的版本号,差不多就是无参数的inxi弱化版。
1 | Info: Processes: 856 Uptime: 357d 5h 39m Memory: 62.56 GiB used: 8.7 GiB (13.9%) Init: systemd runlevel: 3 Shell: Zsh |
使用-m
就是内存信息了。
1 | Memory: RAM: total: 62.56 GiB used: 8.88 GiB (14.2%) |
不愧是服务器,插槽真多(。
使用--memory-shrot
则能看简易版的内存信息。
1 | Memory: RAM: total: 62.56 GiB used: 8.47 GiB (13.5%) |
使用-r
查看当前使用的包管理仓库。
1 | Repos: No active dnf repos in: /etc/dnf/dnf.conf |
不过好像对安装了dnf的CentOS不太友好。
-R
则是raid卡设备了。
1 | RAID: Hardware-1: Broadcom / LSI MegaRAID SAS-3 3108 [Invader] driver: megaraid_sas |
甚至可以用-W
查询天气。
1 | inxi -W Tokyo,Japan |
-t
可以查看top5的进程信息。还可以追加-c
和-m
查看cpu和内存使用情况。-t
效果和-t cm5
相同,即top5的信息。
1 | Processes: CPU top: 5 of 838 |
偶尔需要监控资源使用情况时,该工具还算趁手。
如果需要更多的资源监控功能,则推荐使用一些其他的工具。
对于需要诊断计算机问题以及获取那些并不熟悉的软硬件信息的人来说,inxi 工具是十分便利且有用的。
它能识别那些消耗 CPU、内存的进程;可以检查是否安装了合适的图形驱动程序、主板 UEFI/BIOS 是否需要更新等等。
事实上,在inxi开源社区论坛上,会要求那些寻求帮助的成员提供inxi命令输出内容以便判断他们当前正在使用什么样的系统环境。
当然也有其他的工具可以读取Linux上的硬件信息,不过inxi同时能读取硬件和软件信息。
那么从最常见的场景开始:复制文件和复制目录。
对于复制单个文件而言,scp 和 rsync 命令实际上是等价的。比方说,你需要把 foo.txt 传到你在名为 server 的服务器上的主目录下:
1 | scp foo.txt me@server:/home/me/ |
相应的 rsync 命令只需要输入 rsync 取代 scp:
1 | rsync foo.txt me@server:/home/me/ |
对于复制目录,就有了很大的分歧,这也解释了为什么 rsync 会被认为比 scp 更复杂。
如果你想把 bar 目录复制到 server 服务器上,除了指定 ssh 信息外,相应的 scp 和 cp 一模一样。
1 | scp -r bar/ me@server:/home/me/ |
对于 rsync,考虑的因素比较多,因为它是一个比较强大的工具。首先,我们来看一下最简单的形式:
1 | rsync -r bar/ me@server:/home/me/ |
看起来很简单?对于只包含目录和普通文件的简单情况,这就可以了。
然而,rsync 更在意发送与主机系统中一模一样的文件。
让我们来创建一个稍微复杂一些,但并不罕见的例子:
1 | mkdir -p bar/baz |
1 | touch bar/foo.txt |
1 | cd bar/baz |
1 | . |
如果我们尝试上面的命令来复制 bar,我们会注意到非常不同的(并令人惊讶的)结果。
首先,我们来试试 scp:
1 | scp -r bar/ me@server:/home/me/ |
如果你 ssh 进入你的服务器,看看 bar 的目录树,你会发现它有一个微妙的区别:
1 | . |
link.txt 不再是一个符号链接,它现在是一个 foo.txt 的完整副本。
如果你习惯于使用 cp,这可能会是令人惊讶的行为。
如果你尝试使用 cp -r 复制 bar 目录,你会得到一个新的目录,里面的符号链接和 bar 的一样。
现在如果我们尝试使用之前的 rsync 命令,我们会得到一个警告:
1 | rsync -r bar/ me@server:/home/me/ |
rsync 警告我们它发现了一个非常规文件,并正在跳过它。
因为你没有告诉它可以复制符号链接,所以它忽略了它们。
rsync 在手册中有一节“符号链接”,解释了所有可能的行为选项。
在我们的例子中,我们需要添加 -links 标志:
1 | rsync -r --links bar/ me@server:/home/me/ |
在远程服务器上,我们看到这个符号链接是作为一个符号链接复制过来的。请注意,这与 scp 复制符号链接的方式不同。
1 | . |
在复制目录时可以使用归档标志 -archive/-a。
该归档标志将做大多数人所期望的事情,因为它可以实现递归复制、符号链接复制和许多其他选项。
1 | rsync -a bar/ me@server:/home/me/ |
如果你感兴趣的话,rsync 手册页有关于存档标志的深入解释。
不过,使用 rsync 有一个注意事项。
使用 scp 比使用 rsync 更容易指定一个非标准的 ssh 端口。
例如,如果 server 使用 8022 端口的 SSH 连接,那么这些命令就会像这样:
1 | scp -P 8022 foo.txt me@server:/home/me/ |
而在使用 rsync 时,你必须指定要使用的“远程 shell”命令,默认是 ssh。
你可以使用 -e 标志来指定。
1 | rsync -e 'ssh -p 8022' foo.txt me@server:/home/me/ |
rsync 会使用你的 ssh 配置;但是,如果你经常连接到这个服务器,你可以在你的 ~/.ssh/config 文件中添加以下代码。
这样你就不需要再为 rsync 或 ssh 命令指定端口了。
1 | Host server |
另外,如果你连接的每一台服务器都在同一个非标准端口上运行,你还可以配置 RSYNC_RSH 环境变量。
现在我们已经介绍了从 scp 切换到 rsync 的日常使用案例和注意事项,让我们花一些时间来探讨一下为什么你可能想要使用 rsync 的优点。
很多人在很久以前就已经开始使用 rsync 了,就是因为这些优点。
如果你和服务器之间的网络连接速度较慢或有限,rsync 可以花费更多的 CPU 处理能力来节省网络带宽。
它通过在发送数据之前对数据进行即时压缩来实现。压缩可以用 -z 标志来启用。
rsync 也只在目标文件与源文件不同的情况下复制文件。这可以在目录中递归地工作。
例如,如果你拿我们上面的最后一个 bar 的例子,并多次重新运行那个 rsync 命令,那么在最初的传输之后就不会有任何传输。
如果你知道你会重复使用这些命令,例如备份到 U 盘,那么使用 rsync 即使是进行本地复制也是值得的,因为这个功能可以节省处理大型数据集的大量的时间。
顾名思义,rsync 可以做的不仅仅是复制数据。
到目前为止,我们只演示了如何使用 rsync 复制文件。
如果你想让 rsync 把目标目录变成源目录的样子,你可以在 rsync 中添加删除标志 -delete。
这个删除标志使得 rsync 将从源目录中复制不存在于目标目录中的文件,然后它将删除目标目录中不存在于源目录中的文件。结果就是目标目录和源目录完全一样。相比之下,scp 只会在目标目录下添加文件。
对于简单的使用情况,rsync 并不比老牌的 scp 工具复杂多少。
唯一显著的区别是在递归复制目录时使用 -a 而不是 -r。
然而,正如我们看到的,rsync 的 -a 比 scp 的 -r 更像 cp 的 -r。
希望通过这些新命令,可以加快文件传输工作流程。
]]>其实和nornir官方文档没什么大差别,写出来方便自己查阅和使用。
nornir,100%基于python3构成的自动化框架,易于NetDevOps进行二次开发和脚本编写,操作容易。
相比于ansible,操作更为简单容易,且只需使用python3即可完成配置和脚本编写。
加上支持使用netmiko,使得一些较为冷门的交换机如ruijie、h3c等无需二次开发插件即可进行脚本编写。
目前版本为3.1.1,与之前的版本差异较大,看同类文章时需注意版本。
nornir本体和常用的插件。
1 | pip install nornir nornir_utils nornir_netmiko |
nornir分为三类文件:
首先看配置文件yaml,一般使用config.yaml
,可自定义命名。
1 | #config.yaml |
也可以直接在python文件中使用变量指定。
1 | from nornir import InitNornir |
或两类混用指定配置。
1 | from nornir import InitNornir |
接着可以使用python文件进行测试,无异常则配置测试通过。
1 | from nornir import InitNornir |
目前来看,例如host_file
不支持使用多个文件,无法像ansible使用多个文件进行分类管理。
1 | #hosts.yaml |
下面为nornir默认的key/value参数
1 | { |
分组信息文件示例如下。
1 | #groups.yaml |
默认配置文件示例如下
1 | #defaults.yaml |
接下来使用脚本来查看配置文件内的机器参数。
1 | from nornir import InitNornir |
1 | {'cisco_3850': Host: cisco_3850, 'cisco_3548': Host: cisco_3548, 'server_1': Host: server_1} |
输出1可以看到所有机器名称和它的hostname。
输出2可以看到所有组。
输出3可以看到server_1
的data
所有key。
输出4可以看到cisco_3850
的data
所有key。
并且这些输出都是用标准的python数据格式,字典输出而成,无需经过二次处理即可使用。这里面玩法有多少想必大家都很清楚了。
没错,对于inventory,nornir自带了一个过滤器,可以用来区分设备。
1 | print(nr.filter(os_type="nxos").inventory.hosts.keys()) |
1 | dict_keys(['cisco_3548']) |
filter还可以循环使用,多个kv使用。
1 | print(nr.filter(os_type="nxos").filter(role="core_switch").inventory.hosts.keys()) |
也可以作为变量,中转使用。
1 | filter_device=nr.filter(os_type="nxos") |
甚至可以塞函数进去。
1 | def has_long_name(host): |
filter主要使用data里面的key-value进行过滤,如果根据组别拉出来则使用。
1 | print(nr.inventory.children_of_group("access_switch")) |
1 | {Host: cisco_3850} |
nornir提供了过滤器对象F,可以使用它进行更加复杂的组合判断。__contains
可以判断是否符合指定字符串。
1 | from nornir.core.filter import F |
1 | dict_keys(['cisco_3850']) |
F甚至可以使用__
进行data内的字段直接访问,这时候加上__contains
可以判断是否为子字符串。
1 | device = nr.filter(F(test__test_dict__c__contains="def")) |
1 | dict_keys(['cisco_3850', 'cisco_3548']) |
老生长叹,毕竟你不知道这个程序的配置文件是在用户根目录,还是systemd内。
因为一个程序有一百万种方法来记录它的配置文件在哪里(在手册页、网站上、–help等),但只有一种方法可以让它真正打开它(用系统调用!)。
比如我现在找一下apt的配置文件。
1 | strace -tf apt 2>&1|grep stat |
很明显可以看到apt.conf.d
被加入了加载列表,以及apt.conf
。
执行程序,却什么输出都没有,这是怎么回事?
很多时候你只需要运行strace -p PID
,看看当前运行的是什么系统调用。你甚至不需要看几百行的输出。
如果这个程序闪没了咋办?
试试这个!strace <command>
使用 strace 作为一种粗略的剖析工具strace -t <command>
会显示每次系统调用的时间戳,这样你就可以寻找大的漏洞,找到罪魁祸首。
或者用统计工具 strace -c <command>
你可以通过DNS请求来查找域名,或者通过connect
系统调用来查找IP。
一般来说,当tcpdump
因为某些原因不能使用或者只是因为比较熟悉strace
时,就经常会使用strace
调试网络问题。
比如服务器curl ifconfig.me
一直不出结果。
这时候strace curl ifconfig.me
检查了一下。
1 | sendto(3, "GET HTTP://ifconfig.me/ HTTP/1.1"..., 172, MSG_NOSIGNAL, NULL, 0) = 172 |
发现一直超时,这样头绪会清晰一些,至少不是机器有问题。
]]>首先到 http://elrepo.org/tiki/HomePage ,按照步骤导入公钥和7的yum源。
1 | rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org |
换一波tuna源。
1 | vim /etc/yum.repos.d/elrepo.repo |
其次,确认自己需要mainline还是longtime的内核。前者更新快,后者支持时间更久更稳定。
1 | dnf --enablerepo=elrepo-kernel install --allowerasing kernel-lt kernel-lt-headers kernel-lt-tools kernel-lt-devel -y |
首先需要修改一下grub的默认配置。
1 | cat /etc/default/grub |
然后查看自己的grub2启动表。
1 | awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg |
接着查看当前启动项。
1 | grub2-editenv list |
更改为第一个,再看一下当前启动项。
1 | grub2-set-default 0 |
更改成功后reboot,uname看一下内核。
1 | uname -r |
cacti选择起止时间后,绘图并不能正常进行,依旧显示原来的起止时间。
在相对旧版本的cacti内(versions<=1.1),根目录的绘图graph_image.php文件中,其中的时间判断是依据Unix timestamp,即从 1970/1/1 00:00:00 经过的秒数。
1 | /* override: graph start time (unix time) */ |
原判断为小于1600000000,则会在 2020/09/13 20:26:40 后无法通过判断,进而导致绘图没有刷新。
简单粗暴:把判断改大。
永久解决:更换新版本,确认机器的time_t是否为64位。
这个问题说白了就是千年虫的翻版,且旧版本测试时候,有可能是在32位机器上进行测试,故判断写成了1600000000,但是32位机器上,Unix timestamp会在2038/01/19 03:14:07后工作异常。
如果挂载机器是32位,还是尽快更新为妙。
]]>首先,让我们了解一下输出。top会显示系统的很多信息。我们需要理解不同部分输出的意义:默认运行时,top命令会显示如下输出:
前几行水平显示了不同系统参数的概括,接下来是进程和它们在列中的属性。
1 | top - 15:16:42 up 103 days, 2:09, 1 user, load average: 0.05, 0.09, 0.11 |
top - 15:16:52 up 103 days, 2:09, 1 user, load average: 0.06, 0.09, 0.11
top命令的顶部显示与uptime命令相似的输出。
这些字段意思是:
关于load average,我们可以粗略地按照如下计算方式。
load average/CPUs=usage percent
例如上面给出的0.06,这台机器的核心数量可以通过lscpu查看,是32,则利用率为0.1875%,且无进程在等待。
如果达到了65.00,则此时利用率为203.125%,且有33个进程在等待。
Tasks: 1067 total, 2 running, 1065 sleeping, 0 stopped, 0 zombie
这一行说明了进程数量和进程类型。
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
CPU状态,显示了不同模式下的百分比,分别为:
1 | KiB Mem : 65420768 total, 27637080 free, 4135944 used, 33647744 buff/cache |
内存使用率,类似于free,第一行为物理内存,第二行为swap。
1 | PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND |
默认的列有如下:
列 | 意义 |
---|---|
PID | 进程ID,唯一标识符。 |
USER | 进程所有者的实际用户名。 |
PR | 进程的调度优先级。这个字段的一些值是’rt’。这意味这这些进程运行在实时态。 |
NI | 进程的nice值(优先级)。越小的值意味着越高的优先级。 |
VIRT | 进程使用的虚拟内存。 |
RES | 驻留内存大小。驻留内存是任务使用的非交换物理内存大小。 |
SHR | 进程使用的共享内存。 |
S | 这个是进程的状态。它有D/R/S/T/Z/I |
%CPU | 自从上一次更新时到现在任务所使用的CPU时间百分比。 |
%MEM | 进程使用的可用物理内存百分比。 |
TIME+ | 任务启动后到现在所使用的全部CPU时间,精确到百分之一秒。 |
COMMAND | 运行进程所使用的命令。 |
关于S,有如下状态:
类型 | 意义 |
---|---|
D | Disk Sleep,不可中断睡眠,一般为进程正在与硬件交互。 |
R | Running,运行中。 |
S | Interruptible Sleep,可中断睡眠,表示进程因为等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入R。 |
T | Traced/Stopped,调试器会触发Traced,发送SIGSTOP信号会触发Stopped。 |
Z | Zombie,僵尸进程,父进程结束但未回收资源。 |
I | Idle,空闲状态,一般不会消耗资源。 |
输入h,出现帮助手册。
1 | Help for Interactive Commands - procps-ng version 3.3.10 |
手动刷新,That’s all。
这个命令在全屏和交替模式间切换。在交替模式下会显示4个窗口:
这四组字段共有一个独立的可配置的概括区域和它自己的可配置任务区域。4个窗口中只有一个窗口是当前窗口。当前窗口的名称显示在左上方,交互命令只有在当前窗口有效。
可以使用a/w进行窗口切换。
1 | 1:Def - 15:55:22 up 103 days, 2:48, 1 user, load average: 0.27, 0.16, 0.15 |
Bold,会让top部分关键字加粗。
切换是否显示进程启动时的完整路径和程序名。
时间间隔,如下,我输入了10,即10秒后刷新输出一次top信息。
1 | 1:Def - 15:58:10 up 103 days, 2:51, 1 user, load average: 0.16, 0.15, 0.14 |
选择字段,*为已选择的字段。
1 | Fields Management for window 1:Def, whose current sort field is %CPU |
空闲任务列表。
输入PID,结束任务。
l-不显示load average和uptime
t-进度条显示cpu信息/不显示cpu信息
m-进度条显示内存信息/不显示内存信息
切换进程的默认排序/PID顺序排序。
设置任务优先级。
将进程切换显示到树形图。
1 | PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND |
高亮当前任务所在行列,开启彩色输出后会更明显。
![高亮后的效果]](/images/Snipaste_2021-01-25_16-12-57.png)
特定用户进程显示,输入用户名后只显示该用户进程,空白则显示全部用户。
1 | top - 16:14:37 up 103 days, 3:07, 1 user, load average: 0.25, 0.21, 0.16 |
可为不同的任务区改变配色/粗体等设置。
切换彩色/单色输出。
设置最大显示任务数量。
批处理模式启动top。
这个选项会以上次记住的程序/命令显示的状态显示(是否显示完整路径)。
时间间隔,后面带整数,以秒计算。
显示空闲进程。
刷新次数,带整数,输入10则刷新10次后自动退出。
监控特定PID。
监控特定用户/UID。
]]>1 | hex 100 |
注:hex 和 dec 均为隐藏命令。
1 | show clock ; show switchname ; show ip int brie |
注:命令可简写/后续命令无法Tab或?
1 | conf t ; hostname NXOSSW ; end |
1 | show clock ; show run | ex .* ; show clock |
1 | tac-pac |
注:使用传统 show tech-support 方式获取该文件有时会非常大而且需要非常长的时间去生成,并且会弹到终端内刷新页面。
nexus设备可以使用tac-pac命令替代,输入命令回车后等待一段时间后,自动生成show_tech_out.gz文件,默认保存在volatile:中。
推荐使用tac-pac bootflash:,保存在bootflash:里,之后可以通过FTP等方式导出。
1 | show cli variables |
1 | alias |
1 | cli alias name shiib show ip int brief |
1 | terminal alias shiib show ip int brief |
1 | terminal alias shall show clock ; show switchname ; show ip int brief |
注:全局或会话alias,show 或 config命令都可以组合尝试。
1 | show terminal | in Timeout |
1 | show terminal | in Length |
1 | show clock ; sleep 2 ; show clock |
1 | routing-context vrf management |
这个没什么好说的,就是一系列的常规操作。
轮流升级,先备后主。
确认bootflash大小,备份旧版本bin和kickstart,license。
通过厂商给的过渡版本和kickstart文件,以及最新版本的文件。核对md5后上传至交换机。
这一步发现新版本的NXOS直接上传会无法直接升级,需要升级到过渡版本后出现新命令compact,通过该命令上传文件至交换机。
install过渡版本6,安装前会确认与现网配置是否会冲突和破坏。
升级完毕后返回终端,提示10s后重启。
重启确认version无误,继续升级新版本7。
install过渡版本,安装前会确认与现网配置是否会冲突和破坏。
升级完毕后返回终端,提示10s后重启。
环境是主备两台Nexus3000,做了vpc,没有注意到这点。导致备机升级到新版本7后,版本跨度过大导致主机的vpc产生了未定义的错误,使得主机所有端口errdisable和suspend by vpc,端口都掉了。
解决办法是紧急下线主机,备机先单台撑住,把peer-link端口关闭。隔离升级主机版本。
升级完后打开端口,好了……
其实这个问题在cisco的官方guide里面有说明,不支持跨版本的vpc。然而当时厂商给的升级方案也没有注意到这点,结果就崩了……
其次没有做下线隔离,因为之前都是让厂商来做。为什么这次没让厂商来做,因为这两台设备已经接近EOL了所以没有续保……
当服务器的socket连接数量变得非常大时,无论是使用netstat命令还是直接cat /proc/net/tcp,执行速度都会很慢。可能你不会有切身的感受,但请相信我,当服务器维持的连接达到上万个的时候,使用netstat等于浪费生命,而用ss才是节省时间。
ss快的原因,在于用到了TCP协议栈中tcp_diag。tcp_diag是一个用于分析统计的模块,可以获得Linux内核中第一手的信息,这就确保了ss的快捷高效。当然,如果你的系统中没有tcp_diag,ss也可以正常运行,只是效率会变得稍慢。(但仍然比netstat要快。)
为了加速让你去用ss,下面给了一些具体数据。当服务器维持9万个socket连接,Admin需要计算具体的连接数量时,不同情况的耗时如下:
1 | netstat -at | wc |
几乎所有的Linux系统都会默认包含netstat命令,但并非所有系统都会默认包含ss命令。
因为netstat命令是net-tools工具集中的一员:
1 | rpm -q net-tools |
而ss命令是iproute工具集中的一员:
1 | rpm -q iproute |
如果你无法使用ss命令,那么可能是缺少了iproute,需要安装一下:
1 | yum install iproute iproute-doc |
net-tools是一套标准的Unix网络工具,用于配置网络接口、设置路由表信息、管理ARP表、显示和统计各类网络信息等等,但是遗憾的是,这个工具自2001年起便不再更新和维护了。
接替net-tools任务的便是iproute,这是一套可以支持IPv4/IPv6网络的用于管理TCP/UDP/IP网络的工具集,这套工具由Stephen Hemminger负责维护和升级,目前的大版本号是2。
从某种意义上说,iproute工具集几乎可以替代掉net-tools工具集,具体的替代方案是这样的:
用途 | net-tool | iproute2 |
---|---|---|
地址和链路配置 | ifconfig | ip addr/ip link |
路由表 | route | ip route |
邻居 | arp | ip neigh |
VLAN | vconfig | ip link |
隧道 | iptunnel | ip tunnel |
组播 | ipmaddr | ip maddr |
统计 | netstat | ss |
想获得ss命令的帮助信息和版本信息,都非常简单,随便敲都能猜到。
1 | ss -h |
ss的选项既不多也不复杂,除去非功能性选项(-h/-V)外,ss共有22个选项。
实操一波来看看ss有多香。
1 | ss -s |
1 | ss -l |
如果使用-pl参数的话,则会列出具体的程序名称。你会在输出中看到类似于这样的内容:
1 | LISTEN 0 128 :::http :::* users:(("httpd",1188,4),("httpd",2151,4),("httpd",10213,4),("httpd",10214,4),("httpd",10215,4),("httpd",10216,4),("httpd",10217,4),("httpd",10218,4),("httpd",10219,4),("httpd",10220,4),("httpd",11724,4),("httpd",18632,4)) |
从中可以知道,某个socket连接是属于httpd的,PID是1188/2151/10213等等。
很简单,直接使用-a选项即可列出所有网络连接。
1 | ss -a |
可以配合使用tuwx等参数,例如只查看udp。
1 | ss -au |
直接在curl命令后加上网址,就可以看到网页源码。这里就用我的博客首页来做示范,因为挺短的。
1 | curl laplacence.github.io |
加上 -o 保存则是进行保存,同等与wget。
1 | curl -o <filename> laplacence.github.io |
有的网址是自动跳转的,比如我的首页。使用 -L 参数,curl就会跳转到新的网址。
1 | curl -L laplacence.github.io |
-i 可以显示response header的信息,-I 则只显示header。
1 | curl -i laplacence.github.io |
-v 可以显示完整的http通信过程。
1 | curl -v laplacence.github.io |
curl默认使用GET,-X 后面可以指定其他方法。
1 | curl -X POST laplacence.github.io |
假设网页的上传表单如下
1 | <form method="POST" enctype='multipart/form-data' action="upload.cgi"> |
则可以使用这种方法。
1 | curl --form upload=@<filename> --form press=OK [URL] |
1 | curl --user-agent "UA" [URL] |
你一定需要Windows Terminal,如果不使用它,就好比你的咖喱没有放咖喱粉,而只有各类香料。
安装只需要在微软商城store内搜索它即可下载。
然后你还要确认自己的version是高于1903的版本,否则装不了WSL2。
接着,你需要升级WSL2。
安装前,你需要启用WSL。
打开admin权限的powershell,输入
1 | dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart |
下载升级内核,源地址在这。
安装完成后,继续用powershell运行。
1 | wsl --set-default-version 2 |
最后,去市场安装你需要的发行版,即可享用WSL2。
目前来说,最好用的应该是网络文件夹,访问\\wsl$,选择对应的发行版即可访问WSL内所有文件。
相信大家对DNS的工作方式都有一定的了解,简而言之就是正确连接到互联网的机器都能通过DNS来查询到正确注册的服务器IP地址。
Linux系统上一般都默认安装了dig工具。尝试查询谷歌看看:
1 | dig www.google.com |
如果你得到了一个这样的应答,是好消息吗?是。你得到了及时的回复。状态字段(status: NOERROR)显示没有问题。你正在连接到一个能够提供所要求的信息的名称服务器,并得到一个回复,告诉你一些关于你所查询域名的重要细节。
简而言之,你已经验证了你的系统和DNS服务器相处得很好。
其他可能的状态指标包括:
接下来试试一个不存在的域名:
1 | dig wtfaboutthis.org |
如果你使用ping,可能终端只会返回一句Ping request could not find host www.wtfaboutthis.org. 使用dig可以比ping获取到的信息更多。比起nslookup也更加详细,nslookup只返回一个状态,且与dig使用的系统库不同,nslookup使用了自己的一套库,某些协会也在呼吁人们停止使用nslookup,因为可能产生意外错误。
查询Google时,只有一条A记录,其实还有CNAME记录。CNAME(规范名称canonical name)就像一个别名,把一个域名指向另一个域名。你查询的大多数域名不会有CNAME记录,而只有A记录。如果你运行dig localhost命令,你会看到一个A记录,它就指向127.0.0.1。A记录用于将一个名字映射到一个IP地址。
DNS记录类型包括:
1 | 1 1 1 1 1 1 |
RFC中描述的标志包括:
如果你好奇dig命令执行查询时都经历了哪些过程,你可以尝试使用+trace选项。它会输出从根域到最终结果的所有信息:
1 | dig +trace github.io |
使用@符号指定服务器查询,这里我们使用经典4个8。
1 | dig @8.8.8.8 github.io |
接着使用-x参数,查看8.8.8.8的CNAME。
1 | dig -x 8.8.8.8 |
在上面的结果中,IN前面的数字是TTL,DNS解析中相对重要的指标之一,表示了DNS记录在DNS服务器上的缓存时间。
]]>Python默认采用的垃圾收集机制是『引用计数法 Reference Counting』,该算法最早由George E. Collins在1960的时候首次提出,直到今天,该算法依然被很多编程语言使用。
『引用计数法』的原理是:每个对象维护一个ob_ref字段,用来记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数ob_ref加1,每当该对象的引用失效时计数ob_ref减1,一旦对象的引用计数为0,该对象立即被回收,对象占用的内存空间将被释放。
它的缺点是需要额外的空间维护引用计数,这个问题是其次的,不过最主要的问题是它不能解决对象的“循环引用”,因此,也有很多语言比如Java并没有采用该算法做来垃圾的收集机制。
接下来我们看个例子。
1 | import sys |
结果如下
1 | object id:0x15a685eaf40 |
1 | def f2(): |
结果如下
1 | object id:0x15ad4ca7610 |
创建c1、c2后,计数均为1,执行引用后都变成2,del执行后计数器都降为1,不为0的情况则导致c1、c2不会被销毁。加上while循环,结果就导致无限创建新的对象,新的对象又产生引用,统统无法收回的结果就是内存泄漏。
分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为0、1、2代,他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。
新创建的对象都会分配在0代,0代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到1代去,依此类推,2代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
同时,分代回收是建立在标记清除技术基础之上。分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象。
有三种情况会触发gc模块垃圾回收:
gc模块提供一个接口给开发者设置垃圾回收的选项。上面说到,采用引用计数的方法管理内存的一个缺陷是循环引用,而gc模块的一个主要功能就是解决循环引用的问题。
下面是一些常用的函数。
函数 | 作用 |
---|---|
gc.enable() | 开启自动垃圾收集 |
gc.disable() | 禁用自动垃圾收集 |
gc.isenabled() | 如果启用则返回true |
gc.set_debug(flags) | 设置gc的debug日志,一般设置为gc.DEBUG_LEAK |
gc.collect([generation]) | 显式进行垃圾回收,可以输入参数,0代表只检查0代的对象,1代表检查0、1代的对象,2代表检查所有代的对象。如果不传参数,则默认传入2。返回不可达(unreachable objects)对象的数目。 |
gc.set_threshold(threshold0[, threshold1[, threshold2]) | 设置自动执行垃圾回收的频率。 |
gc.get_count() | 获取当前自动执行垃圾回收的计数器,返回返回当前集合计数为元组(count0, count1, count2)。 |
其他函数可以在官方文档内查看,接下来我们使用看看。
1 | def f3(): |
结果如下
1 | gc: collectable <A 0x00000222A733AF40> |
可以看到gc很快就捕捉到两个A和c1、c2,并判定为可回收的,执行回收之后,garbage列表内多了4个对象。
gc本身采用了分代收集的方法,把对象分为3代,一开始,对象在创建的时候,放在0代中,如果在一次0代的垃圾检查中,该对象存活下来,就会被放到1代中,同理在一次1代的垃圾检查中,该对象存活下来,就会被放到2代中。
用例如下
1 | def f4(): |
结果如下
1 | (427, 9, 0) |
427指距离上一次0代垃圾检查,Python分配内存的数目减去释放内存的数目。
9指距离上一次0、1代垃圾检查的次数。
0是指距离上一次0、1、2代垃圾检查的次数。
配合阈值来看,一般默认阈值为(700, 10, 10),如果当其中任何一个数字达到阈值,则会对应代的执行垃圾回收,即gc.collect([generation]),并将计数器清零。
所谓标记-清除,就是先标记,再清除。标记的是活动对象,清除的是非活动对象。
首先,为了追踪容器对象的引用情况,每个容器对象需要维护两个额外的指针,指针分别指向前后两个容器对象,所有容器对象便组成了一个双向链表,或者,我们也可以将其视为一个有向图。其中,容器对象是有向图的节点,而引用关系是有向图的边。从根对象(root objects)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象(unreachable objects)就是要被清除的非活动对象。
标记-清除作为Python的辅助垃圾收集技术主要处理的是一些容器对象,比如list、dict、tuple,instance等,因为对于字符串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。不过,这种简单粗暴的标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。
最近在做一些小程序,想着分享给一些朋友用用,就想到了打包程序。结果不看不知道,一看原来这东西是个深渊。
使用的是Pyinstaller 3.6/Python 3.8.3环境进行的编译打包。
新版本的Pyinstaller虽然支持了Python3,但是依旧有遗留下来的问题。打包后程序运行后会提示未找到模块。
1 | ModuleNotFoundError: No module named 'pkg_resources.py2_warn' |
这个模块照着pkg_resources源码一搜发现,在它的__init__.py
里面import了这个模块,而且这个模块本身的内容就是一个逻辑判断,看你是不是使用了Python2,如果是就报错。
1 | import sys |
不知道是哪里的咖喱代码导致这个模块无法导入,总之其中一个解决方法就是直接在__init__.py
里面注释掉py2_warn这个import。
另一个方法是在打包的根目录中找到.spec,里面的hiddenimports加上’pkg_resources.py2_warn’,即手动import模块,之后再对.spec进行打包操作即可。
Fedora 28 重大更新,使用GNOME3后,开发团队移除了桌面图标的相关代码。
1 | dnf install gnome-tweak-tool arc-theme numix-icon-theme-circle nautilus-open-terminal -y |
之后在开始可以找到tweaks的图标,里面有详细设置。
各种插件可以去gnome的插件官网下载chrome的管理器后下载安装各类插件。
找到设置里面的keyboard,添加快捷键,名字随意,command用gnome-terminal。
配置文件 ~/.vimrc 里面写入
1 | "basic" |
官网下载对应的rpm包,双击文件/命令行安装完后所有程序即出现VSC。
到MarketPlace下载官方的Python拓展,内置了格式化/自动补全等功能,可在File->Preference->Settings内搜索到相应设置。
格式化快捷键:Ctrl+Shift+I
C配置与Windows下一致,同样下载官方的C/C拓展,生成.vscode文件夹,配置launch.json/task.json。
需要另外安装gcc-c++,Fedora并没有自带。
只需要更改/etc/dnf/dnf.conf里面的内容即可,在里面多加上一句。
1 | fastestmirror=true |
以下是在Fedora 32碰到的问题。
目前Fedora 32已经内置了Python3.8,所以只需通过pip安装selenium即可。
chromedriver放入PATH中,或者在代码中指定配置路径。
chromedriver的版本要与已安装的chrome/chromium一致,否则会导致错误,如
1 | SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 81 |
还有一种错误也是经常见到的
1 | unknown error: DevToolsActivePort file doesn't exist |
这时候要在options内添加一些参数
1 | options = ChromeOptions() |