运维知识体系之操作系统层
到目前为止,也工作了好几年了吧。曾经某次听赵班长的公开课,听着听着发现,越听越懵…有种啥也不会的感觉..-_-!!!
但是呢,这些东西也确实是在工作中用到过,只是比较零碎。某些名词也确实很熟悉,但是呢,比如你现在问我里面具体的细节,我会用一脸懵逼来回应你,哈哈。
这些天抽空对着赵班长总结的表格,尝试在网上收集了些资料,整理出下面的文章。如果有不对的地方,欢迎大家指点。在整理这些东西的过程中,也是对我自己所掌握知识的一种回顾吧。让我们为了自己心中所想,一起努力吧!
整理运维知识体系之操作系统层
参考:https://www.unixhot.com/page/ops
运维架构层级 | 运维角度 | 内容描述/主要技术关键词 | 监控体系 | 自动化/DevOps | 云计算 |
操作系统层 | CPU | CPU运行级别、CPU管理(进程管理、taskset、intel VT-X)、使用率、上下文切换、运行队列、进程调度、系统调用 | mpstat、strace | 虚拟化 | 公有云弹性计算产品 |
内存 | 虚拟内存、SWAP换入换出、内存寻址、内存管理(Buffer Cache、HugePages、ksmd、EPT) | vmstat、free | |||
I/O(磁盘) | 缺页中断、IOPS(顺序IO、随机IO)、IO管理(IO调度算法、virtio)、VFS | iostat、iotop | |||
I/O(网络) | TCP/IP(三次握手、四次挥手、状态转换、TCP队列)、IO模型、Bonding、Bridge、网络管理(iftop、tcpdump) | iftop | |||
内核/Shell | 内核定制、内存参数优化、脚本编程(AWK、Sed、Shell、Python、PHP、Perl、Ruby、Lua) | 系统监控 |
CPU
- CPU运行级别
- Intel的 x86处理器是通过Ring级别来进行访问控制的,级别共分4层,RING0,RING1,RING2,RING3。Windows只使用其中的两个级别RING0和RING3。RING0层拥有最高的权限,RING3层拥有最低的权限。
- 按照Intel原有的构想,应用程序工作在RING3层,只能访问RING3层的数据,操作系统工作在RING0层,可以访问所有层的数据,而其他驱动程序位于RING1、RING2层,每一层只能访问本层以及权限更低层的数据。RING设计的初衷是将系统权限与程序分离出来,使之能够让OS更好的管理当前系统资源,也使得系统更加稳定。
- 应用程序的代码运行在最低运行级别上ring3上,不能做受控操作。如果要做,比如要访问磁盘,写文件,那就要通过执行系统调用(函数),执行系统调用的时候,CPU的运行级别会发生从ring3到ring0的切换,并跳转到系统调用对应的内核代码位置执行,这样内核就为你完成了设备访问,完成之后再从ring0返回ring3。这个过程也称作用户态和内核态的切换。
- 驱动程序都是工作在ring0上,否则驱动不了设备。
- intel VT-X
- 虚拟化在这里就遇到了一个难题,因为宿主操作系统是工作在ring0的,客户操作系统就不能也在ring0了。但执行的指令还是一样的,这样肯定不行,因为没权限,跑不起来。一般客户操作系统执行特权指令时,会触发异常(CPU机制,没权限的指令,触发异常),然后VMM捕获这个异常,在异常里面做翻译,模拟,最后返回到客户操作系统内,客户操作系统认为自己的特权指令工作正常,继续运行。但是这个性能损耗非常的大。以前只是简单的执行一条指令,现在却需要复杂的异常处理。
- 后来,CPU厂商开始支持虚拟化,支持Intel-VT 的CPU,有VMX root operation 和 VMX non-root operation两种模式,两种模式都支持Ring 0 ~ Ring 3 这 4 个运行级别。这时VMM可以运行在VMX root operation模式下,客户OS运行在VMX non-root operation模式下。即硬件这层做了些区分,在全虚拟化下,有些靠“捕获异常-翻译-模拟”的实现就不需要了。
- 进程管理
- 进程是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,如CPU,IO,内存,网络资源等。进程和程序的区别在于:程序是指令的集合,是程序的静态描述,而进程是动态的一次活动的执行。参考
taskset
- LINUX提供的一个命令,可以让某个程序运行在某个(或)某些CPU上。参考性能调优攻略 123# taskset -p 31100pid 31100's current affinity mask: f# 显示结果的 f 实际上是二进制4个低位均为1的bitmask,每一个1对应于1个CPU,表示该进程在4个CPU上运行
1234# taskset -pc 2 31100pid 31100's current affinity list: 0-3pid 31100's new affinity list: 2# 2表示CPU将只会运行在第3个CPU上(从0开始计数)- LINUX提供的一个命令,可以让某个程序运行在某个(或)某些CPU上。参考性能调优攻略
CPU使用率
- 反映的是当前cpu的繁忙程度,忽高忽低的原因在于占用cpu处理时间的进程可能处于io等待状态但却还未释放进入wait。
- 平均负载(load average)是指某段时间内占用cpu时间的进程和等待cpu时间的进程数,这里等待cpu时间的进程是指等待被唤醒的进程,不包括处于wait状态进程。
- 对于每一个CPU来说运行队列最好不要超过3。如果是双核CPU就不要超过6。如果队列长期保持在3以上,说明任何一个进程运行时都不能马上得到cpu的响应,这时可能需要考虑升级cpu。另外满负荷运行cpu的使用率最好是user空间保持在
65%~70%
,system空间保持在30%
,空闲保持在0%~5%
。 - top
参数 | 描述 |
---|---|
%us | 用户空间程序的cpu使用率(没有通过nice调度) |
%sy | 系统空间的cpu使用率,主要是内核程序 |
%ni | 用户空间且通过nice调度过的程序的cpu使用率 |
%id | 空闲cpu |
%wa | cpu运行时在等待io的时间 |
%hi | cpu处理硬中断的数量 |
%si | cpu处理软中断的数量 |
%st | 被虚拟机偷走的cpu |
- vmstat
参数 | 描述 |
---|---|
r | 运行队列(多少个进程真的分配到CPU)。当值超过CPU数目,就会出现CPU瓶颈。和CPU的负载有关系,一般负载超过3就比较高,超过5就很高,超过10就不正常,服务器状态很危险。top的负载类似每秒的运行队列,如果队列过大,表示CPU很繁忙,一般会造成CPU使用率很高。 |
b | 阻塞的进程 |
swpd | 虚拟内存使用的大小。如果大于0表示机器的物理内存不足 |
free | 空闲的物理内存大小 |
buff | 缓存(主要用于块设备) |
cache | 缓存(缓存文件) |
si | 每秒从磁盘读入虚拟内存的大小。如果大于0表示物理内存不够或内存泄漏 |
so | 每秒虚拟内存写入磁盘的大小。如果大于0表示物理内存不够或内存泄漏 |
bi | 块设备每秒写入的块数量,块设备指系统上所有的磁盘和其他设备,默认为1024byte |
bo | 块设备每秒读取的块数量,如果读取文件,bo会大于0。bi和bo一般都接近0,不然就是IO过于频繁 |
in | 每秒CPU的中断次数,包括时间中断 |
cs | 每秒上下文切换次数,在调用系统函数,就要进行上下文切换,线程的切换,也要进程上下文切换,这个值要越小越好,太大了,要考虑调低线程或者进程的数目。每次调用系统函数,代码就会进入内核空间,导致上下文切换,这个很耗资源。 |
us | 用户CPU使用率 |
sy | 系统CPU使用率,如果太高,表示系统调用时间长 |
id | 空闲CPU时间,一般来说,id + us + sy = 100 |
wt | 等待IO CPU时间 |
sar
sar命令语法和vmstat一样命令 参数 描述 sar -q 1 5 runq-sz 运行队列的长度(等待运行的进程数) plist-sz 进程列表中进程(processes)和线程(threads)的数量 ldavg-1 最后1分钟的系统平均负载(System load average) ldavg-5 过去5分钟的系统平均负载 ldavg-15 过去15分钟的系统平均负载 mpstat
命令 参数 描述 mpstat 1 5 %user 处理用户进程所使用 CPU 的百分比 %nice 使用 nice 命令对进程进行降级时 CPU 的百分比 %system 内核进程使用的 CPU 百分比 %iowait 等待进行 I/O 所使用的 CPU 时间百分比 %irq 用于处理系统中断的 CPU 百分比 %soft 软件中断的 CPU 百分比 %idle 显示 CPU 的空闲百分比 %intr/s 显示每秒 CPU 接收的中断总数 pidstat
命令 参数 描述 pidstat %user 处理用户进程所使用 CPU 的百分比 %system 内核进程使用的 CPU 百分比 %guest 进程在虚拟机占用cpu的百分比 %CPU 进程占用cpu的百分比 CPU 处理进程的cpu编号 Command 当前进程对应的命令 pidstat -r PID 进程标识符 Minflt/s 任务每秒发生的次要错误,不需要从磁盘中加载页 Majflt/s 任务每秒发生的主要错误,需要从磁盘中加载页 VSZ 虚拟地址大小,虚拟内存的使用KB RSS 常驻集合大小,非交换区五里内存使用KB Command 当前进程对应的命令 pidstat -d PID 进程id kB_rd/s 每秒从磁盘读取的KB kB_wr/s 每秒写入磁盘KB kB_ccwr/s 任务取消的写入磁盘的KB。当任务截断脏的pagecache的时候会发生 Command 当前进程对应的命令 pidstat -w -p PID PID 进程id Cswch/s 每秒主动任务上下文切换数量 Nvcswch/s 每秒被动任务上下文切换数量 Command 当前进程对应的命令 pidstat -t -p PID TGID 主线程号 TID 线程id %user 处理用户进程所使用 CPU 的百分比 %system 内核进程使用的 CPU 百分比 %guest 进程在虚拟机占用cpu的百分比 %CPU 进程占用cpu的百分比 CPU 处理进程的cpu编号 Command 当前进程对应的命令 pidstat -T ALL -p PID PID 进程标识符 Usr-ms 任务和子线程在用户级别使用的毫秒数 System-ms 任务和子线程在系统级别使用的毫秒数 Guest-ms 任务和子线程在虚拟机(running a virtual processor)使用的毫秒数 Command 当前进程对应的命令
上下文切换
文档参考
现在linux是大多基于抢占式,CPU给每个任务一定的服务时间,当时间片轮转的时候,需要把当前状态保存下来,同时加载下一个任务,这个过程叫做上下文切换。时间片轮转的方式,使得多个任务利用一个CPU执行成为可能,但是保存现场和加载现场,也带来了性能消耗。
对于抢占式操作系统,引起上下文切换的原因大致有几下几种:- 当前任务的时间片用完之后,系统CPU正常调度下一个任务
- 当前任务碰到IO阻塞,调度线程将挂起此任务,继续下一个任务
- 多个任务抢占锁资源,当前任务没有抢到,被调度器挂起,继续下一个任务
- 用户代码挂起当前任务,让出CPU时间
- 硬件中断
监测Linux的应用的时候,当CPU的利用率非常高,但是系统的性能却上不去的时候,不妨监控一下线程/进程的切换,看看是不是context switching导致的overhead过高。常用命令:
pidstat
,vmstat
运行队列
文档参考- run-queue:活动(正在运行)和排队的进程数。每个CPU都会维持一个运行队列,理想情况下,调度器会不断让队列中的进程运行。进程不是处在sleep状态就是runable状态。如果CPU过载,就会出现调度器跟不上系统的要求,导致可运行的进程会填满队列。队列愈大,程序执行时间就愈长。
- 对于每一个CPU来说运行队列最好不要超过3。如果是双核CPU就不要超过6。如果队列长期保持在3以上,说明任何一个进程运行时都不能马上得到cpu的响应,这时可能需要考虑升级cpu。另外满负荷运行cpu的使用率最好是user空间保持在
65%~70%
,system空间保持在30%
,空闲保持在0%~5%
。
-
- CPU调度也叫进程调度。分为:短程调度、中程调度、长程调度。
CPU调度发生的情况:
- 从运行状态切换到等待状态
- 从运行状态切换到就绪状态
- 从等待切换到准备就绪
- 终止
非抢占式(nonpreemptive)和抢占式(preemptive)调度。前者是指让程序一直运行着,直到它自己出异常;后者允许其他程序抢占现在正在运行的程序。
- 上下文切换。
系统调用
文档参考1,文档参考2,文档参考3
当用户态的进程调用一个系统调用时,CPU从用户态切换到内核态并开始执行一个内核函数。Linux通过由向量为128(0x80)的编程异常实现CPU由用户态到内核态的转换。
内存
-
- 每个进程都有自己独立的内存空间,各个进程的内存空间具有类似的结构。
- Linux内存管理采用的是页式管理,使用的是多级页表,动态地址转换机构与主存、辅存共同实现虚拟内存。
- 一个新进程建立的时候,将会建立起自己的内存空间,此进程的数据,代码等从磁盘拷贝到自己的进程空间,哪些数据在哪里,都由进程控制表中的task_struct记录,task_struct中记录中一条链表,记录中内存空间的分配情况,哪些地址有数据,哪些地址无数据,哪些可读,哪些可写,都可以通过这个链表记录。
- 每个进程已经分配的内存空间,都与对应的磁盘空间映射。
- 对于32位系统,寻址指针为4字节,对应的虚拟地址空间为0-2^32,即0-4G;对于64位系统,寻址指针为8字节,对应的虚拟地址空间为0-2^64,即0-16G。这个地址空间是虚拟的,并非实际存在的。
SWAP换入换出
文档参考
内存页面分为用户页面和内核页面。
关于SWAP的设置,Oracle官方推荐:
RAM|Swap Space
—|—
Up to 512 MB|2 times the size of RAM
Between 1024 MB and 2048 MB|1.5 times the size of RAM
Between 2049 MB and 8192 MB|Equal to the size of RAM
More than 8192 MB|0.75 times the size of RAM内存寻址
文档参考1,文档参考2
内存寻址是指CPU允许支持的内存大小。双通道内存技术其实是一种内存控制和管理技术,它依赖于芯片组的内存控制器发生作用,在理论上能够使两条同等规格内存所提供的带宽增长一倍。计算机管理内存的基本方式有两种:段式管理和页式管理。内存管理
文档参考
内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。Buffer Cache
文档参考
缓存区cache(磁盘缓存)和缓冲区buffer(内存缓存)都是临时存储区。不同点:- 缓冲区buffer主要存在于RAM中,作为CPU暂时存储数据的区域
- 高速缓存cache是一种高速存储区域,可以是主存或硬盘等其他独立存储区域的一部分
HugePages
文档参考
HugePages是linux内核的一个特性,使用hugepage可以用更大的内存页来取代传统的4K页面。优点有:- 没有swap
- 减轻快表压力
- 减轻换页表的负载
- 提高内存的性能,降低CPU负载
Kmsd
文档参考
KSM是内核中的一种内存共享机制。在2.6.36版本的内核中引入,它会合并某些相同的页面以减少页面冗余,在内核中有一个KSM守护进程 ksmd,它定期扫描用户向它注册的内存区,寻找相同的页面,从而用一个添加写保护的页面来代替, 当有进程尝试写入的时候,会自动分配一个新页面,这点就是典型的COW机制。EPT
文档参考
在虚拟化环境下,intel CPU在处理器级别加入了对内存虚拟化的支持。即扩展页表EPT,而AMD也有类似的成为NPT。
I/0(磁盘)
缺页中断
文档参考
缺页中断是要访问的页不在主存,需要操作系统将其调入主存后再进行访问。在这个时候,被内存映射的文件实际上成了一个分页交换文件。- 软性页缺失指页缺失发生时,相关的页已经被加载进内存,但是没有向MMU注册的情况。操作系统只需要在MMU中注册相关页对应的物理地址即可。
- 硬性页缺失是指相关的页在页缺失发生时未被加载进内存的情况,这时操作系统需要:
- 寻找到一个空闲的页。或者把另外一个使用中的页写到磁盘上(如果其在最后一次写入后发生了变化的话),并注销在MMU内的记录;
- 将数据读入被选定的页
- 向MMU注册该页
IOPS
文档参考1,文档参考2
IOPS(Input/Output Per Second)即每秒的输入输出量(或读写次数),是衡量磁盘性能的主要指标之一。IOPS是指单位时间内系统能处理的I/O请求数量,一般以每秒处理的I/O请求数量为单位。- 顺序IO指读取和写入操作基于逻辑块逐个连续访问来自相邻地址的数据。在顺序IO访问中,HDD所需的磁道搜索时间显着减少,因为读/写磁头可以以最小的移动访问下一个块。
- 数据备份和日志记录等业务是顺序IO业务。
- 随机IO指读写操作时间连续,但访问地址不连续,随机分布在磁盘LUN的地址空间中。
- 产生随机IO的业务有OLTP服务,SQL,即时消息服务等
- 顺序IO指读取和写入操作基于逻辑块逐个连续访问来自相邻地址的数据。在顺序IO访问中,HDD所需的磁道搜索时间显着减少,因为读/写磁头可以以最小的移动访问下一个块。
-
1234567#查看当前系统支持的IO调度算法dmesg | grep -i scheduler[ 0.852570] io scheduler noop registered[ 0.852575] io scheduler deadline registered (default)[ 0.852614] io scheduler cfq registered[ 0.852619] io scheduler mq-deadline registered[ 0.852624] io scheduler kyber registered
IO调度算法
CFQ(完全公平排队I/O调度程序)
特点:- 在最新的内核版本和发行版中,都选择CFQ做为默认的I/O调度器。
- CFQ试图均匀地分布对I/O带宽的访问,避免进程被饿死并实现较低的延迟,是deadline和as调度器的折中。
- CFQ对于多媒体应用(video,audio)和桌面系统是最好的选择。
- CFQ赋予I/O请求一个优先级,而I/O优先级请求独立于进程优先级,高优先级的进程的读写不能自动地继承高的I/O优先级。
工作原理:
- CFQ为每个进程/线程,单独创建一个队列来管理该进程所产生的请求,也就是说每个进程一个队列,各队列之间的调度使用时间片来调度,以此来保证每个进程都能被很好的分配到I/O带宽。I/O调度器每次执行一个进程的4次请求。
NOOP(电梯式调度程序)
特点:- 在Linux2.4或更早的版本的调度程序,那时只有这一种I/O调度算法。
- NOOP实现了一个简单的FIFO队列,它像电梯的工作方法一样对I/O请求进行组织,当有一个新的请求到来时,它将请求合并到最近的请求之后,以此来保证请求同一介质。
- NOOP倾向饿死读而利于写。
- NOOP对于闪存设备,RAM,嵌入式系统是最好的选择。
电梯算法饿死读请求的解释:
- 因为写请求比读请求更容易。
- 写请求通过文件系统cache,不需要等一次写完成,就可以开始下一次写操作,写请求通过合并,堆积到I/O队列中。
- 读请求需要等到它前面所有的读操作完成,才能进行下一次读操作。在读操作之间有几毫秒时间,而写请求在这之间就到来,饿死了后面的读请求。
- Deadline(截止时间调度程序)
特点:- 通过时间以及硬盘区域进行分类,这个分类和合并要求类似于noop的调度程序。
- Deadline确保了在一个截止时间内服务请求,这个截止时间是可调整的,而默认读期限短于写期限。这样就防止了写操作因为不能被读取而饿死的现象。
- Deadline对数据库环境(ORACLE RAC,MYSQL等)是最好的选择。
- AS(预料I/O调度程序)
特点:- 本质上与Deadline一样,但在最后一次读操作后,要等待6ms,才能继续进行对其它I/O请求进行调度。
- 可以从应用程序中预订一个新的读请求,改进读操作的执行,但以一些写操作为代价。
- 它会在每个6ms中插入新的I/O操作,而会将一些小写入流合并成一个大写入流,用写入延时换取最大的写入吞吐量。
- AS适合于写入较多的环境,比如文件服务器。
- AS对数据库环境表现很差。
virtio
文档参考
一种 I/O 半虚拟化解决方案,是一套通用 I/O 设备虚拟化的程序,是对半虚拟化 Hypervisor 中的一组通用 I/O 设备的抽象。提供了一套上层应用与各 Hypervisor虚拟化设备(KVM,Xen,VMware等)之间的通信框架和编程接口,减少跨平台所带来的兼容性问题,大大提高驱动程序开发效率。VFS
文档参考1,文档参考2
VFS(virtual File System),也称为虚拟文件系统交换层(Virtual Filesystem Switch)的作用就是采用标准的Unix系统调用读写位于不同物理介质上的不同文件系统,即为各类文件系统提供了一个统一的操作界面和应用编程接口。VFS是一个可以让open()
、read()
、write()
等系统调用不用关心底层的存储介质和文件系统类型就可以工作的粘合层。
iostat
命令 参数 描述 iostat -x rrqm/s 每秒这个设备相关的读取请求有多少被Merge了(当系统调用需要读取数据的时候,VFS将请求发到各个FS,如果FS发现不同的读取请求读取的是相同Block的数据,FS会将这个请求合并Merge) wrqm/s 每秒这个设备相关的写入请求有多少被Merge了 r/s 每秒读取的扇区数 w/s 每秒写入的扇区数 rkB/s 每秒读K字节数,是 rsect/s 的一半,因为每扇区大小为512字节。(需要计算) wkB/s 每秒写K字节数。是 wsect/s 的一半。(需要计算) avgrq-sz 平均每次设备I/O操作的数据大小(扇区)。delta(rsect+wsect)/delta(rio+wio) avgqu-sz 平均I/O队列长度。即 delta(aveq)/s/1000(因为aveq的单位为毫秒) await 平均每次设备I/O操作的等待时间(毫秒)。即 delta(ruse+wuse)/delta(rio+wio) r_await 发送给要服务的设备的读取请求的平均时间(毫秒)。这包括队列中请求所花费的时间和服务它们所花费的时间。 w_await 发送给要服务的设备的写入请求的平均时间(毫秒)。这包括队列中请求所花费的时间和服务它们所花费的时间。 svctm 平均每次设备I/O操作的服务时间(毫秒) %util 一秒中有百分之多少的时间用于 I/O 操作,即被io消耗的cpu百分比 iotop(yum安装)
- 左右箭头:改变排序方式,默认是按IO排序
- r:改变排序顺序
- o:只显示有IO输出的进程
- p:进程/线程的显示方式的切换
- a:显示累积使用量
- q:退出
I/O(网络)
TCP三次握手四次挥手
文档参考最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器。
- TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
- TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这时报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
- TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
- TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
- 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。
数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时客户端进入FIN-WAIT-1(终止等待1)状态。TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 服务器收到连接释放报文,发出确认报文ACK=1、ack=u+1,并且带上自己的序列号seq=v,此时服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
- 客户端收到服务器的确认请求后,此时客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文FIN=1、ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认ACK=1、ack=w+1,而自己的序列号是seq=u+1,此时客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2*MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
TCP状态变迁
文档参考
三次握手:
- LISTEN:表示服务器的某个SOCKET处于监听状态,可以进行连接了。
- SYN_SENT:表示客户端的某个SOCKET与服务器进行connect时,首先发送SYN报文,然后进入SYN_SENT状态,等待服务器发送ACK+SYN报文。
- SYN_RECV:表示服务器收到客户端发送的SYN报文,然后向客户端发送SYN+ACK报文,随后服务器进入SYN_RECV状态。
- ESTABLISHED:表示连接已经建立,当客户端在SYN_SENT状态时,收到服务器发送的ACK+SYN报文之后,然后进行第三次握手,客户端发送ACK报文,然后进入ESTABLISHED状态,当处于SYN_RECV状态的服务器收到客户端发送的ACK报文之后,也进入ESTABLISHED状态,然后连接建立。
四次挥手:
- FIN_WAIT_1:表示客户端SOCKET想主动关闭连接,于是向服务器发送FIN报文,然后进入FIN_WAIT_1状态。
- FIN_WAIT_2:表示客户端收到服务器发来的ACK报文,此时客户端进入FIN_WAIT_2状态,此时客户端这边的连接已经关闭,但服务器端的连接还没关闭,也就是服务器还可以继续向客户端发送数据。
- CLOSING:这种状态表示此时双方刚好可能都在关闭连接,即客户端向服务器发送FIN报文,进入FIN_WAIT_1状态后,没有收到服务器发来的ACK报文,反而受到服务器发来的FIN报文,说明此时客户端和服务器同时发起关闭连接,随后,客户端进入CLOSING状态。
- TIME_WAIT:表示收到了服务器发来的FIN报文,然后客户端发送ACK报文,随后进入TIME_WAIT状态,等待2MSL之后进入CLOSED状态。
- CLOSE_WAIT:表示当服务器收到客户端发来的FIN报文之后,发送ACK报文,随后服务器进入CLOSE_WAIT状态。
- LAST_ACK:表示服务器主动关闭连接,向客户端发送FIN报文后,随即进入LAST_ACK状态,如果收到了客户端发来的ACK报文之后,就进入CLOSED状态。
为何TIME_WAIT需要等2MSL时间才能回到CLOSED状态:
如果网络不可靠,那么就无法保证最后客户端发送的ACK报文服务器端一定能够收到,因此处于LAST_ACK状态的服务器可能会因为超时而未收到ACK报文,而重新向客户端发送FIN报文,TIME_WAIT的作用就是用来客户端重新发送可能丢失的ACK报文。TCP队列
文档参考- 半连接队列:保存SYN_RECV状态的连接。队列长度由net.ipv4.tcp_max_syn_backlog设置
- accept队列:保存ESTABLISHED状态的连接。队列长度为min(net.core.somaxconn,backlog)。其中backlog是我们创建ServerSocket(intport,int backlog)时指定的参数,最终会传递给listen方法。
IO模型
文档参考- blocking IO
- nonblocking IO
- IO multiplexing
- signal driven IO(不常用)
- asynchronous IO
网卡Bonding模式
文档参考- Mode 0(balance-rr) Round-robin策略,这个模式具备负载均衡和容错能力
- Mode 1(active-backup) 主备策略,在绑定中只有一个网卡被激活,其他处于备份状态
- Mode 2(balance-xor) XOR策略,通过源MAC地址与目的MAC地址做异或操作选择slave网卡
- Mode 3 (broadcast) 广播,在所有的网卡上传送所有的报文
- Mode 4 (802.3ad) IEEE 802.3ad动态链路聚合。创建共享相同的速率和双工模式的聚合组
- Mode 5 (balance-tlb) 适配器传输负载均衡
- Mode 6 (balance-alb) 适配器适应性负载均衡
Bridge
文档参考
计算机内部一般有系统总线来连接内部所有的硬件设备。一个典型的系统总线是PCI(Peripheral Component Interconnect)总线。其他类型的用得较多的总线还有ISA,EISA,MCA,SCSI,和USB。
一个计算机有多个不同类型的总线,这些总线由桥(bridge)链接起来。有以下两种高速总线处理到达或出自内存芯片的数据传输:- 前端总线FSB:连接CPU和RAM控制器
- 后端总线:连接CPU和外部硬件设备CACHE
网络管理
iftop(yum安装)
- TX:发送流量
- RX:接收流量
- TOTAL:总流量
- Cumm:运行iftop到目前时间的总流量
- peak:流量峰值
- rates:分别表示过去 2s 10s 40s 的平均流量
tcpdump
文档参考- 抓取回环网口的包:
tcpdump -i lo
- 防止包截断:
tcpdump -s0
- 以数字显示主机及端口:
tcpdump -n
- 抓取回环网口的包:
内核/Shell
内核定制
文档参考1,文档参考2,文档参考3
linux系统的启动流程:POST自检过程(BIOS) –>如果有多块磁盘,需要在BIOS上选择启动磁盘 –>引导MBR(bootloader引导程序) –> 加载initrd文件 –>执行进程init –>显示欢迎界面
Linux系统定制的目的和意义:- 系统小型化
- 提高实时性
- 对特殊硬件的支持
- 提高系统的可靠性
Linux内核参数优化
文档参考sysctl -a
查看所有系统变量/proc/sys
下内核文件与配置文件sysctl.conf中变量存在着对应关系123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132#最大的待发送TCP数据缓冲区空间net.inet.tcp.sendspace=65536#最大的接受TCP缓冲区空间net.inet.tcp.recvspace=65536#最大的接受UDP缓冲区大小net.inet.udp.sendspace=65535#最大的发送UDP数据缓冲区大小net.inet.udp.maxdgram=65535#本地套接字连接的数据发送空间net.local.stream.sendspace=65535#加快网络性能的协议net.inet.tcp.rfc1323=1net.inet.tcp.rfc1644=1net.inet.tcp.rfc3042=1net.inet.tcp.rfc3390=1#最大的套接字缓冲区kern.ipc.maxsockbuf=2097152#系统中允许的最多文件数量kern.maxfiles=65536#每个进程能够同时打开的最大文件数量kern.maxfilesperproc=32768#当一台计算机发起TCP连接请求时,系统会回应ACK应答数据包。该选项设置是否延迟ACK应答数据包,把它和包含数据的数据包一起发送,在高速网络和低负载的情况下会略微提高性能,但在网络连接较差的时候,对方计算机得不到应答会持续发起连接请求,反而会降低性能net.inet.tcp.delayed_ack=0#屏蔽ICMP重定向功能net.inet.icmp.drop_redirect=1net.inet.icmp.log_redirect=1net.inet.ip.redirect=0net.inet6.ip6.redirect=0#防止ICMP广播风暴net.inet.icmp.bmcastecho=0net.inet.icmp.maskrepl=0#限制系统发送ICMP速率net.inet.icmp.icmplim=100#安全参数,编译内核的时候加了options TCP_DROP_SYNFIN才可以用net.inet.icmp.icmplim_output=0net.inet.tcp.drop_synfin=1#设置为1会帮助系统清除没有正常断开的TCP连接,这增加了一些网络带宽的使用,但是一些死掉的连接最终能被识别并清除。死的TCP连接是被拨号用户存取的系统的一个特别的问题,因为用户经常断开modem而不正确的关闭活动的连接net.inet.tcp.always_keepalive=1#若看到net.inet.ip.intr_queue_drops这个在增加,就要调大net.inet.ip.intr_queue_maxlen,为0最好net.inet.ip.intr_queue_maxlen=1000#防止DOS攻击,默认为30000net.inet.tcp.msl=7500#接收到一个已经关闭的端口发来的所有包,直接drop,如果设置为1则是只针对TCP包net.inet.tcp.blackhole=2#接收到一个已经关闭的端口发来的所有UDP包直接dropnet.inet.udp.blackhole=1#为网络数据连接时提供缓冲net.inet.tcp.inflight.enable=1#如果打开的话每个目标地址一次转发成功以后它的数据都将被记录进路由表和arp数据表,节约路由的计算时间,但会需要大量的内核内存空间来保存路由表net.inet.ip.fastforwarding=0#kernel编译打开options POLLING功能,高负载情况下使用低负载不推荐SMP不能和polling一起用#kern.polling.enable=1#并发连接数,默认为128,推荐在1024-4096之间,数字越大占用内存也越大kern.ipc.somaxconn=32768#禁止用户查看其他用户的进程security.bsd.see_other_uids=0#设置kernel安全级别kern.securelevel=0#记录下任何TCP连接net.inet.tcp.log_in_vain=1#记录下任何UDP连接net.inet.udp.log_in_vain=1#防止不正确的udp包的攻击net.inet.udp.checksum=1#防止DOS攻击net.inet.tcp.syncookies=1#仅为线程提供物理内存支持,需要256兆以上内存kern.ipc.shm_use_phys=1# 线程可使用的最大共享内存kern.ipc.shmmax=67108864# 最大线程数量kern.ipc.shmall=32768# 程序崩溃时不记录kern.coredump=0# lo本地数据流接收和发送空间net.local.stream.recvspace=65536net.local.dgram.maxdgram=16384net.local.dgram.recvspace=65536# 数据包数据段大小,ADSL为1452net.inet.tcp.mssdflt=1460# 为网络数据连接时提供缓冲net.inet.tcp.inflight_enable=1# 数据包数据段最小值,ADSL为1452net.inet.tcp.minmss=1460# 本地数据最大数量net.inet.raw.maxdgram=65536# 本地数据流接收空间net.inet.raw.recvspace=65536#ipfw防火墙动态规则数量,默认为4096,增大该值可以防止某些病毒发送大量TCP连接,导致不能建立正常连接net.inet.ip.fw.dyn_max=65535#设置ipf防火墙TCP连接空闲保留时间,默认8640000(120小时)net.inet.ipf.fr_tcpidletimeout=864000参考值(具体根据系统硬件配置对应值)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172/proc/sys/net/core/wmem_max# 最大socket写buffer,可参考的优化值:873200/proc/sys/net/core/rmem_max# 最大socket读buffer,可参考的优化值:873200/proc/sys/net/ipv4/tcp_wmem# TCP写buffer,可参考的优化值: 8192 436600 873200/proc/sys/net/ipv4/tcp_rmem# TCP读buffer,可参考的优化值: 32768 436600 873200/proc/sys/net/ipv4/tcp_mem# 同样有3个值,意思是:# net.ipv4.tcp_mem[0]:低于此值,TCP没有内存压力.# net.ipv4.tcp_mem[1]:在此值下,进入内存压力阶段.# net.ipv4.tcp_mem[2]:高于此值,TCP拒绝分配socket.# 上述内存单位是页,而不是字节.可参考的优化值是:786432 1048576 1572864/proc/sys/net/core/netdev_max_backlog# 进入包的最大设备队列.默认是300,对重负载服务器而言,该值太低,可调整到1000./proc/sys/net/core/somaxconn# listen()的默认参数,挂起请求的最大数量.默认是128.对繁忙的服务器,增加该值有助于网络性能.可调整到256./proc/sys/net/core/optmem_max# socket buffer的最大初始化值,默认10K./proc/sys/net/ipv4/tcp_max_syn_backlog# 进入SYN包的最大请求队列.默认1024.对重负载服务器,增加该值显然有好处.可调整到2048./proc/sys/net/ipv4/tcp_retries2# TCP失败重传次数,默认值15,意味着重传15次才彻底放弃.可减少到5,以尽早释放内核资源./proc/sys/net/ipv4/tcp_keepalive_time/proc/sys/net/ipv4/tcp_keepalive_intvl/proc/sys/net/ipv4/tcp_keepalive_probes# 这3个参数与TCP KeepAlive有关.默认值是:# tcp_keepalive_time = 7200 seconds (2 hours)# tcp_keepalive_probes = 9# tcp_keepalive_intvl = 75 seconds# 意思是如果某个TCP连接在idle 2个小时后,内核才发起probe.如果probe 9次(每次75秒)不成功,内核才彻底放弃,认为该连接已失效.对服务器而言,显然上述值太大. 可调整到:# /proc/sys/net/ipv4/tcp_keepalive_time 1800# /proc/sys/net/ipv4/tcp_keepalive_intvl 30# /proc/sys/net/ipv4/tcp_keepalive_probes 3/proc/sys/net/ipv4/ip_local_port_range# 指定端口范围的一个配置,默认是32768 61000,已够大.net.ipv4.tcp_syncookies = 1# 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭net.ipv4.tcp_tw_reuse = 1# 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭net.ipv4.tcp_tw_recycle = 1# 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭net.ipv4.tcp_fin_timeout = 30# 表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间net.ipv4.tcp_keepalive_time = 1200# 表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟net.ipv4.ip_local_port_range = 1024 65000# 表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000net.ipv4.tcp_max_syn_backlog = 8192# 表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数net.ipv4.tcp_max_tw_buckets = 5000# 表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为 5000。对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,但是对于Squid,效果却不大。此项参数可以控制TIME_WAIT套接字的最大数量,避免Squid服务器被大量的TIME_WAIT套接字拖死
脚本编程
系统监控
在工作中我使用的是Zabbix做系统监控,参考zabbix官网
本文出自”Jack Wang Blog”:http://www.yfshare.vip/2018/07/27/运维知识体系之操作系统层/