性能测试必备基础知识(二)

1. CPU 使用率

除了空闲时间外的其他时间占总 CPU 时间的百分比,就是CPU 使用率,即 1- 空闲时间/CPU 总时间

当计算 CPU 使用率时,我们通常使用 /proc/stat 文件中的数据。该文件提供了有关 CPU 的计数器信息,包括各种状态下的节拍数。通过 cat  /proc/stat 命令就可详细查看其信息,其中各

列的含义如下:

  • user(通常缩写为 us),代表用户态 CPU 时间。注意,它不包括下面的 nice 时间,但包括了 guest 时间。

  • nice(通常缩写为 ni),代表低优先级用户态 CPU 时间,也就是进程的 nice 值被调整为 1-19 之间时的 CPU 时间。这里注意,nice 可取值范围是 -20 到 19,数值越大,优先级反而越低。

  • system(通常缩写为sys),代表内核态 CPU 时间。

  • idle(通常缩写为id),代表空闲时间。注意,它不包括等待 I/O 的时间(iowait)。

  • iowait(通常缩写为 wa),代表等待 I/O 的 CPU 时间。

  • irq(通常缩写为 hi),代表处理硬中断的 CPU 时间。

  • softirq(通常缩写为 si),代表处理软中断的 CPU 时间。

  • steal(通常缩写为 st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的 CPU 时间。

  • guest(通常缩写为 guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。

  • guest_nice(通常缩写为 gnice),代表以低优先级运行虚拟机的时间。

2. 软中断

假设你正在使用一款音乐播放器应用程序,同时还在进行其他任务,如浏览网页。在某个时刻,音乐播放器应用程序需要通知你有一条新的消息到达。然而,应用程序不能直接中断你正在浏览的网页,因为这会干扰你的体验。因此,应用程序使用软中断来通知你。

当新的消息到达时,音乐播放器应用程序触发一个软中断,类似于发送一个"通知"。这个软中断会暂停当前正在执行的任务(浏览网页),然后转移控制权到应用程序内部的中断处理程序。在中断处理程序中,应用程序可以执行特定的操作,比如弹出一个消息通知,显示新消息的内容。

一旦中断处理程序完成执行,控制权将返回给浏览器应用程序,你可以继续浏览网页,并在屏幕上看到新消息的通知。

这个例子中,软中断允许应用程序在后台处理事件,并以非侵入性的方式通知用户。它提供了应用程序与操作系统之间的通信和协调,而无需直接中断正在执行的任务。软中断的关键特点是它由软件触发和处理,而不是由外部设备触发。

在实际的计算机系统中,软中断用于各种目的,如处理网络数据包、处理磁盘操作、定时器事件等。它们提供了一种有效的方式来处理异步事件,同时保持系统的响应性和稳定性。

网卡接收到数据包后,会通过硬件中断的方式,通知内核有新的数据到了。这时,内核就应该调用中断处理程序来响应它。网卡接收到数据包后,会通过硬件中断的方式,通知内核有新的数据到了。这时,内核就应该调用中断处理程序来响应它。最后再发送一个软中断信号,需要从内存中找到网络数据,再按照网络协议栈,对数据进行逐层解析和处理,直到把它送给应用程序。

3. 内存映射

在现代操作系统中,包括Linux,内存管理采用了虚拟内存的概念。每个进程都有自己的虚拟地址空间,它是一个抽象的地址空间,与实际的物理内存地址是分离的。虚拟地址是由进程使用的,而不是直接映射到物理内存。

内存映射通过将虚拟内存地址映射到物理内存地址,将虚拟地址空间与物理内存关联起来。这样,进程可以使用虚拟地址来访问内存,而无需关心实际的物理内存位置。

为了实现这种映射,操作系统维护了一个页表,它是一个数据结构,记录了虚拟地址与物理地址之间的映射关系。每个进程都有自己的页表,用于管理它的虚拟地址空间。

当进程访问一个虚拟地址时,操作系统会使用页表查找对应的物理地址。如果存在映射关系,操作系统将虚拟地址转换为对应的物理地址,以便在物理内存中读写数据。如果没有映射关系,则会触发页面错误(page fault),操作系统会根据情况从磁盘中加载数据到物理内存,并更新页表以建立映射关系。

内存映射在文件操作中也使用了类似的机制。当将文件内容映射到进程的地址空间时,操作系统会将文件的内容映射到一段虚拟地址空间,这样进程就可以通过读写这些虚拟地址来实现对文件的访问。

因此,内存映射不仅用于将虚拟内存地址映射到物理内存地址,也用于将文件内容映射到进程的地址空间中。页表是维护这些映射关系的重要数据结构,它记录了虚拟地址与物理地址之间的对应关系。

 

4. 虚拟内存空间分布

  1. 代码段(Text Segment):
    代码段包含了可执行程序的机器指令。它通常是只读的,因为指令不应该被修改。当程序被加载到内存中时,代码段被映射到进程的虚拟内存空间,以供执行。

  2. 数据段(Data Segment):
    数据段包含了全局变量和静态变量的内存空间。它通常是可读写的。数据段在程序运行时初始化,并且在整个程序的生命周期中保持不变。

  3. 堆(Heap):
    堆是动态分配内存的区域。在堆中,程序可以通过调用诸如malloc()和free()等函数来动态地分配和释放内存。堆的大小可以在运行时进行调整。

  4. 栈(Stack):
    栈用于存储局部变量、函数调用和函数参数。栈是以后进先出(LIFO)的方式进行管理的。每当调用函数时,栈会分配一段内存来存储该函数的局部变量和其他相关信息。当函数返回时,这些内存将被释放。

  5. 共享库区域(Shared Library Region):
    共享库区域包含了共享库的代码和数据。这些库可以由多个进程共享,以节省内存空间。当多个进程使用相同的共享库时,它们可以将该库映射到自己的虚拟内存空间中,以便共享使用。

  6. 内核空间(Kernel Space):
    内核空间是由操作系统内核使用的虚拟内存区域。它包含了操作系统内核的代码、数据和驱动程序等。用户进程无法直接访问内核空间,需要通过系统调用等特定接口与内核进行交互。

5. 内存分配

内存分配:

  1. 静态分配:静态分配是指在编译时或程序启动时就确定内存的分配情况。例如,全局变量和静态变量的内存分配在程序加载时完成,并在整个程序的生命周期中保持不变。

  2. 动态分配:动态分配是在程序运行时根据需要动态分配和释放内存。常用的动态内存分配函数是malloc()、calloc()和realloc()。这些函数允许你请求一定大小的内存块,并返回一个指向该内存块的指针。动态分配的内存块位于堆(Heap)区域。

内存回收:

  • 回收缓存,比如使用 LRU(Least Recently Used)算法,回收最近使用最少的内存页面;

  • 回收不常访问的内存,把不常用的内存通过交换分区直接写到磁盘中;

  • 杀死进程,内存紧张时系统还会通过 OOM(Out of Memory),直接杀掉占用大量内存的进程。

其中,第二种方式回收不常访问的内存时,会用到交换分区(以下简称 Swap)。Swap 其实就是把一块磁盘空间当成内存来用。它可以把进程暂时不用的数据存储到磁盘中(这个过程称为换出),当进程访问这些内存时,再从磁盘读取这些数据到内存中(这个过程称为换入)。

不过要注意,通常只在内存不足时,才会发生 Swap 交换。并且由于磁盘读写的速度远比内存慢,Swap 会导致严重的内存性能问题。

第三种方式提到的 OOM(Out of Memory),其实是内核的一种保护机制。它监控进程的内存使用情况,并且使用 oom_score 为每个进程的内存使用情况进行评分:

  • 一个进程消耗的内存越大,oom_score 就越大;

  • 一个进程运行占用的 CPU 越多,oom_score 就越小。

posted @ 2023-12-05 22:26  空慧居士  阅读(204)  评论(0编辑  收藏  举报