LinuxCPU达到瓶颈该怎样优化

发布时间:2021-11-01 16:57:32 作者:柒染
来源:亿速云 阅读:147

本篇文章给大家分享的是有关LinuxCPU达到瓶颈该怎样优化,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

在Linux系统中,由于成本的限制,往往会存在资源上的不足,例如 CPU、内存、网络、IO 性能。本文,就对 Linux 进程和 CPU  的原理进行分析,总结出 CPU 性能优化的方法。

1. 分析手段

在理解平均负载之前,先要理清楚 Linux 下的进程状态。

1.1. 进程状态

1.1.1. R (TASK_RUNNING),可执行状态

只有在该状态的进程才可能在 CPU 上运行。而同一时刻可能有多个进程处于可执行状态,这些进程的 task_struct 结构(进程控制块)被放入对应  CPU 的可执行队列中(一个进程最多只能出现在一个 CPU 的可执行队列中)。进程调度器的任务就是从各个 CPU 的可执行队列中分别选择一个进程在该 CPU  上运行。

很多操作系统教科书将正在 CPU 上执行的进程定义为 RUNNING  状态、而将可执行但是尚未被调度执行的进程定义为READY状态,这两种状态在linux下统一为 TASK_RUNNING 状态。

1.1.2. S (TASK_INTERRUPTIBLE),可中断的睡眠状态

处于这个状态的进程因为等待某某事件的发生(比如等待 socket 连接、等待信号量),而被挂起。这些进程的 task_struct  结构被放入对应事件的等待队列中。当这些事件发生时

(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。通过 ps 命令我们会看到,一般情况下,进程列表中的绝大多数进程都处于  TASK_INTERRUPTIBLE 状态(除非机器的负载很高)。毕竟 CPU 就这么一两个,进程动辄几十上百个,如果不是绝大多数进程都在睡眠,CPU  又怎么响应得过来。

1.1.3. D (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态

与 TASK_INTERRUPTIBLE 状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。

不可中断,指的并不是 CPU 不响应外部硬件的中断,而是指进程不响应异步信号。

绝大多数情况下,进程处在睡眠状态时,总是应该能够响应异步信号的。否则你将惊奇的发现,kill -9  竟然杀不死一个正在睡眠的进程了!于是我们也很好理解,为什么 ps 命令看到的进程几乎不会出现 TASK_UNINTERRUPTIBLE 状态,而总是  TASK_INTERRUPTIBLE 状态。

而 TASK_UNINTERRUPTIBLE  状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了。

(参见《linux 内核异步中断浅析》) 在进程对某些硬件进行操作时(比如进程调用 read 系统调用对某个设备文件进行读操作,而 read  系统调用最终执行到对应设备驱动的代码,并与对应的物理设备进行交互),可能需要使用 TASK_UNINTERRUPTIBLE  状态对进程进行保护,以避免进程与设备交互的过程被打断,造成设备陷入不可控的状态。这种情况下的 TASK_UNINTERRUPTIBLE 状态总是非常短暂的,通过  ps 命令基本上不可能捕捉到。

1.1.4. T (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态

向进程发送一个 SIGSTOP 信号,它就会因响应该信号而进入 TASK_STOPPED 状态(除非该进程本身处于  TASK_UNINTERRUPTIBLE 状态而不响应信号)。(SIGSTOP 与 SIGKILL 信号一样,是非常强制的。不允许用户进程通过 signal  系列的系统调用重新设置对应的信号处理函数。)

向进程发送一个 SIGCONT 信号,可以让其从 TASK_STOPPED 状态恢复到 TASK_RUNNING 状态。

当进程正在被跟踪时,它处于 TASK_TRACED 这个特殊的状态。"正在被跟踪"指的是进程暂停下来,等待跟踪它的进程对它进行操作。比如在 gdb  中对被跟踪的进程下一个断点,进程在断点处停下来的时候就处于 TASK_TRACED 状态。而在其他时候,被跟踪的进程还是处于前面提到的那些状态。

对于进程本身来说,TASK_STOPPED 和 TASK_TRACED 状态很类似,都是表示进程暂停下来。而 TASK_TRACED 状态相当于在  TASK_STOPPED 之上多了一层保护,处于 TASK_TRACED 状态的进程不能响应 SIGCONT 信号而被唤醒。只能等到调试进程通过 ptrace  系统调用执行 PTRACE_CONT、PTRACE_DETACH 等操作(通过 ptrace 系统调用的参数指定操作),或调试进程退出,被调试的进程才能恢复  TASK_RUNNING 状态。

1.1.5. Z (TASK_DEAD - EXIT_ZOMBIE),退出状态,进程成为僵尸进程

进程在退出的过程中,处于 TASK_DEAD 状态。在这个退出过程中,进程占有的所有资源将被回收,除了 task_struct  结构(以及少数资源)以外。于是进程就只剩下 task_struct 这么个空壳,故称为僵尸。之所以保留 task_struct,是因为 task_struct  里面保存了进程的退出码、以及一些统计信息。而其父进程很可能会关心这些信息。比如在 shell  中,$?变量就保存了最后一个退出的前台进程的退出码,而这个退出码往往被作为 if 语句的判断条件。

当然,内核也可以将这些信息保存在别的地方,而将 task_struct 结构释放掉,以节省一些空间。但是使用 task_struct  结构更为方便,因为在内核中已经建立了从 pid 到 task_struct 查找关系,还有进程间的父子关系。释放掉  task_struct,则需要建立一些新的数据结构,以便让父进程找到它的子进程的退出信息。

父进程可以通过 wait 系列的系统调用(如 wait4、waitid)来等待某个或某些子进程的退出,并获取它的退出信息。然后 wait  系列的系统调用会顺便将子进程的尸体(task_struct)也释放掉。

子进程在退出的过程中,内核会给其父进程发送一个信号,通知父进程来"收尸"。这个信号默认是 SIGCHLD,但是在通过 clone  系统调用创建子进程时,可以设置这个信号。

1.2. 平均负载

单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。

既然是平均的活跃进程数,那么最理想的,就是每个cpu 上都刚好运行着一个进程,这样每个 cpu 都得到了充分利用,比如当平均负载  2时,意味着什么呢?

1、 在只有2 个 CPU的系统上,意味着所有的 CPU都刚好被完全占用

2、 在 4 个CPU的系统上,意味着 CPU 有 50%的空闲

3、 而在只有 1 个CPU 的系统上,则意味着有一半的进程竞争不到 CPU

1.2.1. 平均负载多少合理?

平均负载最理想的情况是等于 CPU 个数。查看系统 CPU 的命令如下:

cat /proc/cpuinfo
LinuxCPU达到瓶颈该怎样优化

Figure 1 四核 CPU 查看平均负载的命令:

给了我们三个不同时间间隔的平均值,给我们提供了分析系统负载趋势的数据来源,让我们更全面、更立体地理解目前的负载情况。

LinuxCPU达到瓶颈该怎样优化

15 分钟内却有很大的负载

uptime 命令在有些嵌入式设备中,会被裁减掉,但是可以通过 proc 文件系统来获取。命令:

cat /proc/loadavg

很显然,当前命令展示的平均负载在 CPU 为 4 个时候已经过载

1.2.2. 平均负载与 CPU 使用率

平均负载不仅包括了正在使用 CPU 的进程,还包括了等待 CPU 和等待 I/O 的进程。

CPU 使用率是指单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应。比如:

1.3. CPU 上下文切换

在每个任务运行前, CPU 都需要知道任务从哪里加载、又从哪里开始运行、也就是说,需要系统事先给他设置好 CPU 寄存器和程序计数器(Program  Counter, PC)

它们都是 CPU 在运行任何任务前,比如的依赖环境,因此也被叫做 CPU 上下文。

1.3.1. 进程上下文切换

Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间

1.3.2. 进程上下文切换和系统调用的区别

进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。

系统调用过程中,并不涉及到虚拟内存等进程用户态的资源,也不会切换进程。

因此,进程的上下文切换比系统调用时多了一步:在保存当前进程的内核状态和 CPU  寄存器之前,需要先把该进程的虚拟内存、栈等保存下来;而加载了下一个进程的内核态后,还需要刷新进程的虚拟内存和用户栈。

1.3.3. 什么时候会切换进程上文

1.3.4. 线程上下文切换

线程和进程的区别

线程的上下文切换两种情况

1.3.5. 中断上下文切换

中断处理会打断进程的正常调度和执行。在打断其他进程时,需要将进程当前的状态保存下来,中断结束后,进程仍然可以从原来的状态恢复运行。

进程上下文切换和中断上下文切换的区别

进程上下文切换和中断上文切换的相同之处

1.3.6. CPU 上下文切换小结

1.3.7. 如何查看系统的上下文切换

常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分析

CPU 上下文切换和中断次数。

LinuxCPU达到瓶颈该怎样优化

Figure 2 每隔 2 秒输出一组数据

需要特别关注的四列内容:

在嵌入式 Linux 设备中,一般 vmstat 工具是不存在的。所以如果想要 vmstat  工具,可以自己实现代码,他的原理是获取/proc/diskstats 和/proc/slabinfo 的信息组合而成。实现代码见 procps 工具

vmstat 只给出了系统总体的上下文切换情况,并不能查看每个进程的上下文切换情况。

:查看某个进程中线程的上下文切换情况,下图查看的是 hicore 进程中所有的线程上下文切换情况。

LinuxCPU达到瓶颈该怎样优化

关注两列内容:

1. 自愿上下文切换:进程无法获取所需资源,导致的上下文切换。比如, I/O、内存等系统资源不足时。

2. 非自愿上下文切换:进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如,大量进程都在争抢 CPU 时。

1.3.9. Procps 工具

procps 是一组命令行和全屏工具,是由内核动态生成的一个 "伪"  文件系统,可以提供进程表中条目状态的信息。该文件系统为内核数据结构提供了一个简易接口,procps  程序通常就集中在这个描述了系统进程运行状态的数据结构上。

procps 包括以下程序:

1.3.10. sysstat 工具

在嵌入式 Linux 设备中同样也不存在该工具,busybox 中也没有相关命令。需要安装 Linux 性能监控工具 sysstat,他是一个工具集,包括  sar、sadf、mpstat、iostat、pidstat 等,这些工具可以监控系统性能和使用情况。各工具的作用如下:

1. iostat - 提供 CPU 统计,存储 I/O 统计(磁盘设备,分区及网络文件系统)

2. mpstat - 提供单个或组合 CPU 相关统计

3. pidstat - 提供 Linux 进程级别统计:I/O、CPU、内存等

4. sar - 收集、报告、保存系统活动信息:CPU、内存、磁盘、中断、网络接口、TTY、内核表等

5. sadc - 系统活动数据收集器,作为 sar 后端使用

6. sa1 - 收集系统活动日常数据,并二进制格式存储,它作为 sadc 的工具的前端,可以通过 cron 来调用

7. sa2 - 生成系统每日活动报告,同样可作为 sadc 的工具的前端,可以通过 cron 来调用

8. sadf - 可以以 CSV、XML 格式等显示 sar 收集的性能数据,这样非常方便的将系统数据导入到数据库中,或导入到 Excel  中来生成图表

9. nfsiostat-sysstat: 提供 NFS I/O 统计

10. cifsiostat: 提供 CIFS 统计

sysstat 功能强大,功能也在不断的增强,每个版本提供了不同的功能,可以到 sysstat 官网了 解 工 具 最 先 发 展 情 况 和 获 得 相  应 的 帮 助 手 册 。 官 网 地 址 :

1.3.11. 中断

中断是一种异步的事件处理机制,可以提高系统的并发处理能力。中断处理程序会打断其他进程的运行,为了减少对正常进程运行调度的影响,中断处理程序就需要尽可能快地运行。

Linux 将中断处理过程分成了两个阶段,也就是上半部和下半部。

· 上半部用来快速处理中断,它在中断禁止模式下运行,主要处理跟硬件紧密相关的或时间敏感的工作。

· 下半部用来延时处理上半部未完成的工作,通常以内核线程的方式运行。

/proc/interrupts:查看硬中断发生的类型

硬件中断发生频繁,是件很消耗 CPU 资源的事情,Linux 默认情况下是将所有的硬件中断都绑定在 CPU0 上,在多核 CPU  条件下如果有办法把大量硬件中断分配给不同的

CPU (core) 处理显然能很好的平衡性能。

1.3.13. 根据上下文切换的类型做具体分析

· 自愿上下文切换变多,说明进程都在等待资源,有可能发生 I/O 等其他问题

· 非自愿上下文切换变多,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈

· 中断次数变多,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。

1.4. CPU 使用率

/proc/stat,提供的是系统的 CPU 和任务统计信息。这个信息非常的原始

LinuxCPU达到瓶颈该怎样优化

CPU 使用率相关的重要指标

19 之间时的 CPU 时间。nice 可取值范围是 -20 到 19, 数值越大,优先级反而越低

真正查看 CPU 使用率的命令是通过 top 命令

LinuxCPU达到瓶颈该怎样优化

1.5. 软中断

提供了软中断的运行情况。

LinuxCPU达到瓶颈该怎样优化
LinuxCPU达到瓶颈该怎样优化

2. 优化方法

2.1 CPU 使用率

CPU 使用率描述了非空闲时间占总 CPU 时间的百分比,根据 CPU 上运行任务的不同,又被分为用户 CPU、系统 CPU、等待 I/O  CPU、软中断和硬中断等。

2.2 平均负载(Load Average)

平均负载,也就是系统的平均活跃进程数,它反映了系统的整体负载情况,主要包括三个数值,分别指过去 1 分钟、过去 5 分钟和过去 15  分钟的平均复制子。

理想情况下,平均负载等于逻辑 CPU 个数,这表示每个 CPU 都恰好被充分利用。如果平均负载大于逻辑 CPU 个数,就表示负载比较重了。

2.3 进程上下文切换

2.4 CPU 缓存的命中率

由于 CPU 发展的速度远快于内存的发展, CPU 的处理速度就比内存的访问速度快得多。这样,CPU  在访问内存的时候,免不了要等待内存的响应。为了协调这两者巨大的性能差距,CPU 缓存(通常是多级缓存)就出现了。

根据不断增长的热点数据,这些缓存按照大小不同分为 L1、L2、L3 等三级缓存,其中

L1 和 L2 常用在单核中,L3 则用在多核中。

从 L1 到 L3,三级缓存的大小依次增大,相应的,性能依次降低(当然比内存还是好

得多)。而它们的命中率,衡量的是 CPU 缓存的复用情况,命中率越高,则表示性能越好。

2.5 tcmalloc 替换 ptmalloc

2.5.1 ptmalloc

Ptmalloc 采用主-从分配区的模式,当一个线程需要分配资源的时候,从链表中找到一个没加锁的分配区,在进行内存分配。

小内存分配

在 ptmalloc 内部,内存块采用 chunk 管理,并且将大小相似的 chunk 用链表管理,一个链表被称为一个 bin。前 64 个 bin  里,相邻的 bin 内的 chunk 大小相差 8 字节,称为 small bin,后面的是 large bin,large bin 里的 chunk  按先大小,再最近使用的顺序排列,每次分配都找一个最小的能够使用的 chunk。

LinuxCPU达到瓶颈该怎样优化

Chunk 的结构如上所示,A 位表示是不是在主分配区,M 表示是不是 mmap 出来的,P 表示上一个内存紧邻的 chunk  是否在使用,如果没在使用,则 size of previous

chunk 是上一个 chunk 的大小,否则无意义(而且被用作被分配出去的内存了),正式根据

P 标记位和 size of previous chunk 在 free 内存块的时候来进行 chunk 合并的。当然,如果 chunk 空闲,mem  里还记录了一些指针用于索引临近大小的 chunk 的,实现原理就不复述了,知道大致作用就行。

在 free 的时候,ptmalloc 会检查附近的 chunk,并尝试把连续空闲的 chunk 合并成一个大的 chunk,放到 unstored  bin 里。但是当很小的 chunk 释放的时候,ptmalloc 会把它并入 fast bin 中。同样,某些时候,fast bin  里的连续内存块会被合并并加入到一个 unsorted bin 里,然后再才进入普通 bin 里。所以 malloc 小内存的时候,是先查找 fast

bin,再查找 unsorted bin,最后查找普通的 bin,如果 unsorted bin 里的 chunk 不合适,则会把它扔到 bin  里。

大内存分配

Ptmalloc 的分配的内存顶部还有一个 top chunk,如果前面的 bin 里的空闲 chunk 都不足以满足需要,就是尝试从 top chunk  里分配内存。如果 top chunk 里也不够,就要从操作系统里拿了。

还有就是特别大的内存,会直接从系统 mmap 出来,不受 chunk 管理,这样的内存在回收的时候也会 munmap 还给操作系统。

简而言之,就是:

小内存: [获取分配区(arena)并加锁] -> fast bin -> unsorted bin -> small bin  -> large bin

-> top chunk -> 扩展堆

大内存: 直接 mmap

总结

释放的时候,几乎是和分配反过来,再加上可一些 chunk 合并和从一个 bin 转移到另一个 bin 的操作。并且如果顶部有足够大的空闲  chunk,则收缩堆顶并还给操作系统。

介于此,对于 ptmalloc 的内存分配使用有几个注意事项:

1. Ptmalloc 默认后分配内存先释放,因为内存回收是从 top chunk 开始的。

2. 避免多线程频繁分配和释放内存,会造成频繁加解锁。

3. 不要分配长生命周期的内存块,容易造成内碎片,影响内存回收。

2.5.2 Tcmalloc

具体实现原理不加以赘述,可自行百度学习之,总结以下特点。

· Tcmalloc 占用更少的额外空间。例如,分配 N 个 8 字节对象可能要使用大约 8N * 1.01

字节的空间。即,多用百分之一的空间。Ptmalloc2 使用最少 8 字节描述一个 chunk。

· 更快。小对象几乎无锁, >32KB 的对象从 CentralCache 中分配使用自旋锁。 并且>32KB  对象都是页面对齐分配,多线程的时候应尽量避免频繁分配,否则也会造成自旋锁的竞争和页面对齐造成的浪费。

2.6 思维导图

LinuxCPU达到瓶颈该怎样优化

3. 分析工具

从 CPU 的性能指标出发。当你要查看某个性能指标时,要清楚知道哪些工具可以做到。

4. 思路

性能优化并不是没有副作用的,通常情况下 Linux  系统是不需要特意调整某些指标。往往性能优化会带来整体系统的复杂度的上升,降低了可移植性,也可能在调整某个指标的时候导致其他指标异常。

并不是所有的性能问题都需要去优化,需要对瓶颈点进行优化。比如当前系统有瓶颈,用户 CPU 使用率升高了 10%,而系统系统 CPU 使用率却升高了  50%,这个时候就应该首先优化系统 CPU 使用率。

4.1 应用程序优化

从应用程序的角度来说,降低 CPU  使用率最好的方法是,排除所有不必要的工作,只保留最核心的逻辑。比如减少循环层次、减少递归、减少动态内存分配等等。

常见的几种应用程序的性能优化方法:

4.2 系统优化

从系统的角度来说,优化 CPU 的运行,一方面要充分利用 CPU 缓存的本地性,加速缓存访问;另一方面,就是要控制进程的 CPU  使用情况,减少进程间的相互影响。

常见的方法:

配置 smp_affinity,就可以把中断处理过程自动负载均衡到其他 CPU 上

以上就是LinuxCPU达到瓶颈该怎样优化,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注亿速云行业资讯频道。

推荐阅读:
  1. 【oracle性能优化】- 使用AWR定位oracle性能瓶颈
  2. 如何有效优化Mysql瓶颈

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

linux cpu

上一篇:Vue正则写法是什么

下一篇:PPT中如何更改饼图的颜色

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》