Linux性能基础:CPU、内存、磁盘等概述

目录

[1. CPU](#1. CPU)

[1.1. CPU常见品牌](#1.1. CPU常见品牌)

[1.2. CPU性能概述](#1.2. CPU性能概述)

[① CPU主频](#① CPU主频)

[② CPU位数](#② CPU位数)

[③ CPU缓存指令集](#③ CPU缓存指令集)

[④ CPU核心数](#④ CPU核心数)

[⑤ IPC](#⑤ IPC)

[1.3. 上下文切换](#1.3. 上下文切换)

[1.4. 进程与线程](#1.4. 进程与线程)

[① 进程](#① 进程)

[② 线程](#② 线程)

[2. 内存](#2. 内存)

[2.1. 内存主频](#2.1. 内存主频)

[2.2. 内存带宽](#2.2. 内存带宽)

[2.3. 内存分类](#2.3. 内存分类)

[2.4. 内存的分配](#2.4. 内存的分配)

[2.5. 内存的回收](#2.5. 内存的回收)

[2.6. 内存泄漏](#2.6. 内存泄漏)

[3. 磁盘](#3. 磁盘)

[3.1. 磁盘的构成](#3.1. 磁盘的构成)

[3.2. 磁盘的类型](#3.2. 磁盘的类型)

[3.3. 磁盘性能概述](#3.3. 磁盘性能概述)

[3.4. 测试磁盘读写速度](#3.4. 测试磁盘读写速度)


1. CPU

简介

CPU 即中央处理器(central processing unit的简称),是电子计算机的主要设备之一。其功能主要是解释计算机指令以及处理计算机软件中的数据。

  • 电子计算机三大核心部件:CPU、内部存储器、输入/输出设备。
  • CPU 的作用主要为:处理指令、执行操作、控制时间、处理数据。

CPU的工作原理

CPU的工作分为以下 5 个阶段:取指令阶段、指令译码阶段、执行指令阶段、访存取数和结果写回

  1. 取指令阶段(IF,instruction fetch),即将一条指令从主存储器中取到指令寄存器的过程。程序计数器中的数值,用来指示当前指令在主存中的位置。当 一条指令被取出后,程序计数器(PC)中的数值将根据指令字长度自动递增。
  2. 指令译码阶段(ID,instruction decode),取出指令后,指令译码器按照预定的指令格式,对取回的指令进行拆分和解释,识别区分出不同的指令类 别以及各种获取操作数的方法。现代CISC处理器会将拆分已提高并行率和效率。
  3. 执行指令阶段(EX,execute),具体实现指令的功能。CPU的不同部分被连接起来,以执行所需的操作。
  4. 访存取数阶段(MEM,memory),根据指令需要访问主存、读取操作数,CPU得到操作数在主存中的地址,并从主存中读取该操作数用于运算。部分指令不需要访问主存,则可以跳过该阶段。
  5. 结果写回阶段(WB,write back),作为最后一个阶段,结果写回阶段把执行指令阶段的运行结果数据"写回"到某种存储形式。结果数据一般会被写到CPU的内部寄存器中,以便被后续的指令快速地存取;许多指令还会改变程序状态字寄存器中标志位的状态,这些标志位标识着不同的操作结果,可被用来影响程序的动作。

在指令执行完毕、结果数据写回之后,若无意外事件(如结果溢出等)发生,计算机就从程序计数器中取得下一条指令地址,开始新一轮的循环,下一个指令周期将顺序取出下一条指令。许多复杂的CPU可以一次提取多个指令、解码,并且同时执行。


1.1. CPU常见品牌

|--------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 品牌 | 来源 | 说明 |
| Intel | 美国(英特尔) | Intel生产的CPU分为高中低端,最低端的G系列,然后是低端i3系列,中端i5系列,高端i7系列和至尊i9系列。i3、i5、i7只代表它属于哪一个定位的产品,并不是代表着它的最终型号。查看各个型号性能:百度搜索CPU天梯图 |
| AMD | 美国(超威半导体) | AMD价格相对便宜,性价比高,超频空间大,更适合DIY用户使用,相对来说拥有更高的主频和更多的核心数,某些处理器支持开核来获得更多核心。但稳定性不佳,发热量大,功耗大,温度高,架构比起intel来说落后。不过AMD最新推出了RYZEN架构处理器,解决了发热量大,功耗大,温度高的问题,性能敢与intel处理器抗衡。 |
| 龙芯 | 中国(龙芯中科) | 龙芯处理器是中国人自主研发的计算机处理器,龙芯1号已经量产,基于龙芯1号的网络计算机和其它产品也开始面市并得到应用,性能数倍于龙芯1号的龙芯2号已进行首次流片,可以说,龙芯系列无论在产业化方面还是在核心技术突破方面,都一直保持着快速和稳定的节奏。 |
| 兆芯 | 中国(上海兆芯) | 国资控股,同时掌握CPU、GPU和芯片组三大核心技术,具备三大核心芯片及相关IP设计能力。 |
| 申威 | 中国(申威科技) | 创新可信程度最高的国产CPU厂商,主供军方和超算领域。 |
| 飞腾 | 中国(天津飞腾) | 专注于高性能、低功耗集成电路芯片的研发和生产。 |
| 鲲鹏 | 中国(华为海思) | 专注于ARM架构芯片研发,产品覆盖服务器、桌面、嵌入式、移动端等领域。 |
| 海光 | 中国(海光信息) | 主要从事集成电路研发和技术服务,x86技术来自于AMD公司的授权。 |


1.2. CPU性能概述

影响CPU性能的指标主要分为:主频、 CPU位数、CPU缓存指令集、CPU核心数和IPC。

① CPU主频
  • CPU主频是指CPU内核工作的时钟频率,单位Hz。它直接的决定了CPU的性能,也可以通过超频来提高CPU主频来获得更高性能。
  • 主频越大,运算越快。例如CPU在一个时钟周期内运算一条指令,2GHz运行一条指令需要10ns,那么1GHz需要20ns,运算速度慢了一倍。因为2GHz的时钟周期比1GHz的时钟周期占用的时间减少了一半,速度自然就快了一倍。
  • 主频不代表CPU整体速度。例如:AMD公司的AthlonXP系列,CPU大多以较低的主频达到Intel公司Pentium4系列CPU较高的主频。因此主频仅是CPU性能表现的其中一个方面,而不代表CPU的整体性能。
  • 注意:主频越高,处理器发热也就越严重
bash 复制代码
lscpu    # 查看CPU信息
   
   
   

② CPU位数

处理器能够一次性计算的浮点数的位数,通常情况下,CPU的位数越高,CPU 进行运算时候的速度就会变得越快(64位 > 32位)。

  • 32位代表cpu一次能够处理32位的数据(4字节大小)
  • 64位代表cpu一次能够处理64位的数据(8字节大小)

64位与32位相比最明显的一点就是 64位的cpu与32位的cpu多了一8个64位的通用寄存器,并且在内存的寻址能力上面与32位的cpu相比也提升不少。

bash 复制代码
getconf LONG_BIT    # 查看CPU位数
   
   
   

③ CPU缓存指令集
  • 指令集是指CPU中用来计算和控制计算机系统的一套指令的集合,指令的强弱是CPU的重要指标,指令集是提高微处理器效率的最有效工具之一。
  • CPU 的缓存可以分为一级缓存、二级缓存、三级缓存,缓存性能直接影响CPU处理性能。部分特殊职能的CPU可能会配备四级缓存。

CPU的缓存结构

  • CPU为了提升执⾏效率,减少CPU与内存的交互(交互影响CPU效率),⼀般在CPU上集成了多级缓存架构

|---------------|---------------------------------------------------------------------------------------------------------------|
| 缓存级别 | 解释说明 |
| 一级缓存(L1,多核独享) | 每个 CPU 核心都有一块属于自己的 L1 高速缓存,指令和数据在 L1 是分开存放的,所以 L1 高速缓存通常分成指令缓存和数据缓存。 |
| 二级缓存(L2,多核独享) | L2 高速缓存同样每个 CPU 核心都有,但是 L2 高速缓存位置比 L1 高速缓存距离 CPU 核心 更远,它大小比 L1 更大,CPU 型号不同大小也就不同,通常大小在几百 KB 到几 MB 不等,访问速度则更慢。 |
| 三级缓存(L3,多核共享) | L3 高速缓存通常是多个 CPU 核心共用的,位置比 L2 高速缓存距离 CPU 核心 更远,大小也会更大些,通常大小在几 MB 到几十 MB 不等,具体值根据 CPU 型号而定。 |

bash 复制代码
   
   
   
    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         lscpu |grep -E 
         
         "^L"    
         
         # 查看单个cpu高速缓存
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
             L1d:一级数据缓存
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
             L1i:一级指令缓存
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
             L2 :二级缓存
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
             L3 :三级缓存
        
        
        
       
       
       

   

   
   
   

存储器 "速度快慢" 排序:寄存器 > L1 > L2 > L3 > 内存

  • 一级缓存到CPU核心时间约 1ns
  • 二级缓存到CPU核心时间约 4ns
  • 三级缓存到CPU核心时间约 12ns
  • 内存到CPU核心时间约 100ns

存储器 "存储空间" 排序:寄存器 < L1 < L2 < L3 < 内存

什么是CPU指令集?

CPU依靠指令来计算和控制系统,每款CPU在设计时就规定了一系列与其硬件电路相配合的指令系统。指令集搭建的是一个桥梁,是软硬件之间沟通的桥梁(例如:用户需要使用 cat 命令查看文件,那么需要向系统发送一条指令 cat。但是对于cat这个命令,操作系统并不认识,所以需要通过指令集搭建桥梁,将 cat 命令解析为二进制,再发送给系统,系统通过二进制信息再反馈给我们)。

bash 复制代码
grep "flags" /proc/cpuinfo |head -1    # 查看当前CPU支持的指令集
   
   
   
bash 复制代码
gcc -march=native -Q --help=target | grep march    # 查看当前指令集
   
   
   

指令集的分类

|-----------------|----------------|---------------------|
| 名称 | 说明 | CPU |
| 复杂指令集(CISC) | CISC功能完备,效率较低。 | X86 |
| 精简指令集(RISC) | RISC功能简洁,效率较快。 | ARM(高通)、RISC-V、MIPS |

如果将CPU指令集比作盖房子的砖。RISC相当于使用标准砖块,小平房可以盖,摩天大楼也可以盖,底层的原材料很简单,都是标准化的砖头。而CISC除了标准砖块,还设计了砖瓦等结构件,如果要盖的房子种类多了,就需要定义更多更复杂的结构件,结构件的管理也 会越来越复杂,而且在建设某种常见建筑的时候,大部分特殊的结构架是闲置不用的,也就影响了施工效率。

基于CISC模式下的CPU设计,在各种新需求下,堆叠的功能越来越复杂,芯片设计难度也越来越高,效率低下,因此就出现了RISC精简指令集的概念。

复杂指令(CISC)的优点

能够有效缩短新指令的微代码设计时间,允许设计师实现 CISC 体系机器的向上兼容。新的系统可以使用一个包含早期系统的指令超集合,也就可以使用较早电脑上使用的相同软件。微程序指令的格式与高级语言相匹配,因而编译器并不一定要重新编写。
复杂指令(CISC)的缺点

指令集以及芯片的设计比上一代产品更复杂,不同的指令,需要不同的时钟周期来完成,执行较慢的指令,将影响整台机器的执行效率。
精简指令(RISC)的优点

在使用相同的芯片技术和相同运行时钟下,RISC 系统的运行速度将是 CISC 的2~4倍。由于RISC处理器的指令集是精简的,它的内存管理单元、浮点单元等都能设计在同一块芯片上。RISC 处理器比相对应的 CISC 处理器设计更简单,所需要的时间将变得更短,并可以比CISC处理器应用更多先进的技术,开发更快的下一代处理器。
精简指令(RISC)的缺点

多指令的操作使得程序开发者必须小心地选用合适的编译器,而且编写的代码量会变得非常大。另外就是RISC体系的处理器需要更快的存储器,这通常都集成于处理器内部,就是L1 Cache(一级缓存)。


④ CPU核心数
  • CPU核心数指的是CPU内核数量,表示一个CPU由多少个核心组成。cpu核心是CPU的重要组成部件,在内核频率、缓存大小等条件相同的情况下,CPU核心数量越多,CPU的整体性能越强(这里说的是整体性能。如果测试串行性能,更看重CPU的主频和指令集;如果测试并发,除了看主频和指令集,主要关注核心数)。

如上图,CPU核心数分别由Socket、Core、Thread组成。

CPU核心数 = Socket * Core * Thread

socket = node

  • socket是物理概念,指的是主板上CPU插槽;node是逻辑概念,对应于socket。

core = 物理CPU

  • core是物理概念,一个独立的硬件执行单元,对应于物理CPU。

thread = 逻辑CPU = Processor

  • thread是逻辑CPU,也就是Processor。

CPU node

由于每个node内部有自己的CPU总线和内存,如果CPU跨不同的node的话,就会出现一个node中的CPU去访问另外一个node中的内存的情况,这就导致内存访问延迟的增加。

bash 复制代码
   
   
   
    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         [yt@localhost ~]$ numactl -H    
         
         # 查看node信息
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         available: 2 nodes (0-1)  
         
         # 可用node为2,编号为node0、node1
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         node 0 cpus: 0 1 2 3      
         
         # node0 分配的cpu(0-3)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         node 0 size: 131037 MB    
         
         # node0 分配的总内存(128GB)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         node 0 free: 108516 MB    
         
         # node0 剩余内存
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         node 1 cpus: 4 5 6 7      
         
         # node1 分配的cpu(4-7)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         node 1 size: 131071 MB    
         
         # node1 分配的总内存(128GB)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         node 1 free: 115183 MB    
         
         # node1 剩余内存
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         node distances:
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         node 0 1
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         0: 10 20
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         1: 20 10
        
        
        
       
       
       

   

   
   
   

Linux安装 numactl: yum -y numactl

以上信息为示例信息。共有2个node,每个node分配4个CPU、128GB内存。

假设这里需要测试两个程序的性能

  • procedure1 需要100GB内存
  • procedure2 需要40GB内存

两个程序总共需要140GB内存,但是一个node最大容量只有128GB,node0肯定会跨节点访问node1,造成内存访问延迟的增加,影响性能指标。

最好的办法就是将两个程序分开使用node,procedure1使用node0,procedure2使用node1,可达到性能指标最大化。

使用 numactl 命令指定节点

  • numactl -N 0 procedure1
  • numactl -N 1 procedure2

点击查看 numactl 使用方法


⑤ IPC
  • IPC(Instruction Per Clock)表示每个时钟的指令,即CPU每一时钟周期内所执行的指令多少。IPC代表了一款CPU的设计架构,一旦该CPU设计完成之后,IPC值就不会再改变了。
  • 一般来说IPC是越高越好, 这意味着单位时间执行了更多的指令, 通过观测IPC可以一定程度上了解软件的执行效率。
  • CPU性能 = IPC * 频率(MHz时钟速度)

IPC 进程间通信

  • IPC(Inter-Process Communication)是共享 "命名管道" 的资源,它是为了让进程间通信而开放的命名管道,通过提供可信任的用户名和口令,连接双方可以建立安全的通道并以此通道进行加密数据的交换,从而实现对远程计算机的访问。

通信的目的

  1. 数据传输,一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M之间。
  2. 共享数据,多个进程想要操作共享数据,一个进程对数据的修改,其他进程应该立刻看到。
  3. 通知事件,一个进程需要向另一个或一组进程发送消息,通知它们发生了某件事情。
  4. 资源共享,多个进程之间共享同样的资源。为了做到这一点,需要内核提供锁和同步机制。
  5. 进程控制,有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

Linux命令 ipcs 提供IPC设施的信息

bash 复制代码
   
   
   
    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         资源选项:
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -m, --shmems      共享内存段
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -q, --queues      消息队列
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -s, --semaphores  信号量
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -a, --all         全部(默认)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         输出格式:
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -t, --time        显示附加、脱离和更改时间
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -p, --pid         显示 PID 的创建者和最后操作
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -c, --creator     显示创建者和拥有者
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -l, --limits      显示资源限制
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -u, --summary     显示状态摘要
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
              --human       以易读格式显示大小
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
          -b, --bytes       以字节数显示大小
        
        
        
       
       
       

   

   
   
   

1.3. 上下文切换

上下文切换指的是内核(操作系统的核心)在CPU上对进程或者线程进行切换。

上下文切换过程中的信息被保存在进程控制块(PCB-Process Control Block)中。PCB又被称作切换帧(SwitchFrame)。上下文切换的信息会一直被保存在CPU的内存中,直到被再次使用。

上下文切换的原因

  1. 当前正在执行的任务完成,系统的CPU正常调度下一个任务。
  2. 当前正在执行的任务遇到 I/O 等阻塞操作,调度器挂起此任务,继续调度下一个任务。
  3. 多个任务并发抢占锁资源,当前任务没有抢到锁资源,被调度器挂起,继续调度下一个任务。
  4. 用户的代码挂起当前任务,比如线程执行yield()方法,让出CPU。
  5. 硬件中断。

上下文切换的开销

  1. 上下文切换是操作系统内核优化的一个关键参数指标。在任务间发生切换需要花费大量的时间用于处理,诸如:保存和恢复寄存器和内存页表、更新内核相关数据结构等操作。
  2. 上下文切换通常是计算密集型的。也就是说, 它需要相当可观的处理器时间, 在每秒几十上百次的切换中, 每次切换都需要纳秒量级的时间。所以, 上下文切换对系统来说意味着消耗大量的CPU时间。

上下文切换的性能影响

  1. 上下文切换会对性能造成负面影响,切换次数越低越好。
  2. 一些上下文切换相对其他切换而言更加昂贵;其中一个更昂贵的上下文切换是跨核上下文切换(Cross-Core Context Switch)。
  3. 一个线程可以运行在一个专用处理器上,也可以跨处理器。由单个处理器服务的线程都有处理器关联(Processor Affinity),这样会更加有效。在另一个处理器内核抢占和调度线程会引起缓存丢失,作为缓存丢失和过度上下文切换的结果要访问本地内存。这称为"跨核上下文切换"。

点击查看 vmstat 命令使用方法(包含上下文切换情况)


1.4. 进程与线程

  • 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。
  • 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
  • 一个进程可以有很多线程,每条线程并行执行不同的任务。

线程与进程的区别

  1. 地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
  2. 通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信,需要进程同步和互斥手段的辅助,以保证数据的一致性。
  3. 调度和切换:线程上下文切换比进程上下文切换要快得多。
  4. 在多线程OS中,进程不是一个可执行的实体。
bash 复制代码
   
   
   
    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         查看系统最大进程数
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         sysctl kernel.pid_max
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         修改系统最大进程数
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         echo 
         
         "kernel.pid_max = 1000000" >> /etc/sysctl.conf
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         sysctl -p
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         查看当前进程数
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         ps -eLf | 
         
         wc -l
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         查看物理CPU数量
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         grep 
         
         'physical id' /proc/cpuinfo | 
         
         sort -u |
         
         wc -l
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         查看CPU核心数量
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         grep 
         
         'core id' /proc/cpuinfo | 
         
         sort -u | 
         
         wc -l
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         查看CPU总线程数
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         grep 
         
         'processor' /proc/cpuinfo | 
         
         sort -u | 
         
         wc -l
        
        
        
       
       
       

   

   
   
   
    
    ![](https://file.jishuzhan.net/article/1714073249127600129/e131387152448193c676d6f7de678c8e.webp)
   
   
   
   
   
   

① 进程

进程的特性

  1. 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
  2. 并发性:任何进程都可以同其他进程一起并发执行。
  3. 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
  4. 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。
  5. 结构特征:进程由程序、数据和进程控制块三部分组成。

引起创建进程的事件

  1. 用户登录:在分时系统中,用户在终端键入登录命令后,如果是合法用户,系统将为该终端建立一个进程,并把它插入到就绪队列中。
  2. 作业调度:在批处理系统中,当作业调度程序按照一定的算法调度到某作业时,便将该作业装入到内存,为它分配必要的资源,并立即为它创建进程,再插入到就绪队列中。
  3. 提供服务:当运行中的用户程序提出某种请求后,系统将专门创建一个进程来提供用户所需要的服务,例如,用户程序要求进行文件打印,操作系统将为它创建一个打印进程,这样,不仅可以使打印进程与该用户进程并发执行,而且还便于计算出为完成打印任务所花费的时间。
  4. 应用请求:在上述三种情况中,都是由系统内核为它创建一个新进程,而这一类事件则是基于应用进程的需求,由它创建一个新的进程,以便使新进程以并发的运行方式完成特定任务。

引起进程终止的事件

  1. 正常结束:在任何计算机系统中,都应该有一个表示进程已经运行完成的指示。例如,在批处理系统中,通常在程序的最后安排一条Hold指令或终止的系统调用。当程序运行到Hold指令时,将产生一个中断,去通知OS本进程已经完成。
  2. 异常结束:在进程运行期间,由于出现某些错误和故障而迫使进程终止。这类异常事件很多,常见的有:越界错误,保护错,非法指令,特权指令错,运行超时,等待超时,算术运算错,I/O故障。
  3. 外界干预:外界干预并非指在本进程运行中出现了异常事件,而是指进程应外界的请求而终止运行。这些干预有:操作员或操作系统干预,父进程请求,父进程终止。

引起进程阻塞和唤醒的事件

  1. 请求系统服务:当正在执行的进程请求操作系统提供服务时,由于某种原因,操作系统并不立即满足该进程的要求时,该进程只能转变为阻塞状态来等待,一旦要求得到满足后,进程被唤醒。
  2. 启动某种操作:当进程启动某种操作后,如果该进程必须在该操作完成之后才能继续执行,则必须先使该进程阻塞,以等待该操作完成,该操作完成后,将该进程唤醒。
  3. 新数据尚未到达:对于相互合作的进程,如果其中一个进程需要先获得另一(合作)进程提供的数据才能运行以对数据进行处理,则是要其所需数据尚未到达,该进程只有(等待)阻塞,等到数据到达后,该进程被唤醒。
  4. 无新工作可做:系统往往设置一些具有某特定功能的系统进程,每当这种进程完成任务后,便把自己阻塞起来以等待新任务到来,新任务到达后,该进程被唤醒。

进程的调度算法

  1. 实时系统中:FIFO(First Input First Output,先进先出算法),SJF(Shortest Job First,最短作业优先算法),SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。
  2. 交互式系统中:RR(Round Robin,时间片轮转算法),HPF(Highest Priority First,最高优先级算法),多级队列,最短进程优先,保证调度,彩票调度,公平分享调度。

进程的运行态

Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间 。在这两种空间中运行的进程状态分别称为内核态(system)和用户态(user)。

内核空间(Ring 0)

具有最高权限,可以直接访问所有资源(读取文件、分配内存、IO操作、创建子进程),都是内核操作。这也表明,当IO操作频繁时,system参数会很高。

用户空间(Ring 3)

只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用进入到内核中,才能访问这些特权资源。

典型的用户态空间程序有:shells、数据库、web服务器、PHP程序、java程序...

点击查看 top 命令使用方法(包含CPU占用情况)


② 线程

线程的工作原理

线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。线程可以创建和撤消线程,从而实现程序的并发执行。一般,线程具有就绪、阻塞和运行三种基本状态。

在多中央处理器的系统里,不同线程可以同时在不同的中央处理器上运行,甚至当它们属于同一个进程时也是如此。大多数支持多处理器的操作系统都提供编程接口来让进程可以控制自己的线程与各处理器之间的关联度(affinity)。

有时候,线程也称作轻量级进程。就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其它每个进程应有的状态。

进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。尽管这让线程之间共享信息变得更容易,但您必须小心,确保它们不会妨碍同一进程里的其它线程。

线程的本操作

  • 派生:线程在进程内派生出来,它即可由进程派生,也可由线程派生。
  • 阻塞(Block):如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。
  • 激活(unblock):如果阻塞线程的事件发生,则该线程被激活并进入就绪队列。
  • 调度(schedule):选择一个就绪线程进入执行状态。
  • 结束(Finish):如果一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。

线程组

每个Java线程都是某个线程组的成员。线程组提供一种机制,使得多个线程集于一个对象内,能对它们实行整体操作。譬如,你能用一个方法调用来启动或挂起组内的所有线程。Java线程组由ThreadGroup类实现。

当线程产生时,可以指定线程组或由实时系统将其放入某个缺省的线程组内。线程只能属于一个线程组,并且当线程产生后不能改变它所属的线程组。

多线程

对于多线程的好处这就不多说了。但是,它同样也带来了某些新的麻烦。只要在设计程序时特别小心留意,克服这些麻烦并不算太困难。在生成线程时必须将线程放在指定的线程组,也可以放在缺省的线程组中,缺省的就是生成该线程的线程所在的线程组。一旦一个线程加入了某个线程组,不能被移出这个组。

死锁

如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源。系统中没有饿死和死锁的线程。Java并不提供对死锁的检测机制。对大多数的Java程序员来说防止死锁是一种较好的选择。最简单的防止死锁的方法是对竞争的资源引入序号,如果一个线程需要几个资源,那么它必须先得到小序号的资源,再申请大序号的资源。

轻量级锁

轻量级锁(Lightweight Locking)是从Java6开始引入的概念,本意是为了减少多线程进入互斥的几率,并不是要替代互斥。它利用了CPU原语Compare-And-Swap(CAS,汇编指令CMPXCHG),尝试在进入互斥前,进行补救。

优化

Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意。原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖操作系统互斥(mutex)来实现的。而互斥是一种会导致线程挂起,并在较短的时间内又需要重新调度回原线程的,较为消耗资源的操作。所以需要进行对线程进行优化,提高效率。


2. 内存

简介

  • 内存(Memory)是计算机的重要部件,也称内存储器和主存储器,它用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。
  • 它是外存与CPU进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平。
  • 只要计算机开始运行,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成,CPU将结果传送出来。
  • 内存的大小决定了系统能运行多少程序。

内存的生命周期

  1. 分配所需的内存
  2. 内存的读与写
  3. 不需要时将其释放

内存单位

1024B = 1KB = 2^10字节

1024KB = 1MB = 2^20字节

1024MB = 1GB = 2^30字节

1024GB = 1TB = 2^40字节

1024TB = 1PB = 2^50字节

1024PB = 1EB = 2^60字节

1024EB = 1ZB = 2^70字节

1024ZB = 1YB = 2^80字节

2.1. 内存主频

  • 内存主频是以MHz(兆赫)为单位来计量的。内存主频越高在一定程度上代表内存能达到的速度越快。内存主频决定该内存最高能在什么样的频率正常工作。
  • 目前主流的内存频率是DDR4,以及内存频率更高的DDR5。
bash 复制代码
   
   
   
    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         [root@localhost]$ dmidecode | grep -A16 
         
         "Memory Device" | grep 
         
         "Speed"
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
                 Speed: 827 MHz (1.3 ns)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
                 Speed: 827 MHz (1.3 ns)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
                 Speed: 827 MHz (1.3 ns)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
                 Speed: 827 MHz (1.3 ns)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
                 Speed: 827 MHz (1.3 ns)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
                 Speed: 827 MHz (1.3 ns)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
                 Speed: 827 MHz (1.3 ns)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
                 Speed: 827 MHz (1.3 ns)
        
        
        
       
       
       

   

   
   
   

2.2. 内存带宽

内存带宽 = 总线宽度 × 总线频率 × 一个时钟周期内交换的数据包个数

  • 从功能上理解,我们可以将内存看作是内存控制器与CPU之间的桥梁或与仓库。显然,内存的容量决定 "仓库" 的大小,而内存的带宽决定 "桥梁" 的宽窄,两者缺一不可,这也就是我们常常说道的"内存容量" 与 "内存速度"。(一级缓存与二级缓存采用的是SRAM,我们也可以将其宽泛地理解为 "内存带宽")
  • 除了内存容量与内存速度,延时周期也是决定其性能的关键。当CPU需要内存中的数据时,它会发出一个由内存控制器所执行的要求,内存控制器接著将要求发送至内存,并在接收数据时向CPU报告整个周期(从CPU到内存控制器,内存再回到CPU)所需的时间。

内存带宽的重要性

基本上当CPU接收到指令后,它会最先向CPU中的一级缓存(L1 Cache)去寻找相关的数据,虽然一级缓存是与CPU同频运行的,但是由于容量较小,所以不可能每次都命中。

这时CPU会继续向下一级的二级缓存(L2 Cache)寻找,同样的道理,当所需要的数据在二级缓存中也没有的话,会继续转向 L3 Cache、内存、硬盘。

如此一来,内存的性能就在一定程度上决定了这个系统的表现,这点在多媒体设计软件和3D游戏中表现得更为明显。3D显卡的内存带宽(或许称为显存带宽更为合适)的重要性也是不言而喻的,甚至其作用比系统的内存带宽更为明显。


2.3. 内存分类

随机存储器(RAM)

随机存储器(Random Access Memory)表示既可以从中读取数据,也可以写入数据。当机器电源关闭时,存于其中的数据就会丢失。

RAM分为两种(动态随机存储器、静态随机存储器):

  1. DRAM( Dynamic RAM,动态随机存储器)的存储单元是由电容和相关元件做成的,电容内存储电荷的多寡代表信号0和1。电容存在漏电现象,电荷不足会导致存储单元数据出错,所以DRAM需要周期性刷新,以保持电荷状态。DRAM结构较简单且集成度高,通常用于制造内存条中的存储芯片。
  2. SRAM( Static RAM,静态随机存储器)的存储单元是由晶体管和相关元件做成的锁存器,每个存储单元具有锁存 "0" 和 "1" 信号的功能。它速度快且不需要刷新操作,但集成度差和功耗较大,通常用于制造容量小但效率高的CPU缓存。

只读存储器(ROM)

ROM表示只读存储器(Read Only Memory),在制造ROM的时候,数据就被存入并永久保存。这些信息只能读出,一般不能写入,即使机器停电,这些数据也不会丢失。

高速缓存(CACHE)

cache也就是CPU中的的一级缓存(L1 Cache)、二级缓存(L2 Cache)、三级缓存(L3 Cache)这些数据,它位于CPU与内存之间,是一个读写速度比内存更快的存储器。

当CPU向内存中写入或读出数据时,这个数据也被存储进高速缓冲存储器中。当CPU再次需要这些数据时,CPU就从高速缓冲存储器读取数据,而不是访问较慢的内存。当然,如需要的数据在Cache中没有,CPU会再去读取内存中的数据。


2.4. 内存的分配

  • 内存分配是指在程序执行的过程中分配或者回收存储空间的分配内存的方法。
  • 内存分配方法有静态内存分配和动态内存分配两种。

内存分配的方式

1、单一连续分配方式

把内存分为系统区用户区两个部分。

系统区仅提供给OS使用。通常放在内存低址部分。

用户区表示除系统区以外的全部内存,提供给用户使用。但只能用于单用户、单任务的操作系统中。

2、固定分区分配

将内存空间划分为若干个固定大小区域,在每个分区中只装入一道作业,便可以有多道作业并发执行。

一旦有空闲分区时,便可以再从外存的后备作业中选择一个适当大小的作业装入该分区;当该作业结束时,再从后备作业队列中找出另一作业调入该分区。

为了便于内存分配,通常将分区按大小进行排队,并为之建立一张分区使用表 ,包含每个分区的起始地址、大小 、状态(是否分配)。当用户程序需要装入时,由内存分配程序检索该表,从中找出一个满足要求、尚未分配的分区,将分区分配给该程序,且在表中标记 "已分配";若未找到大小足够的分区,则会拒绝该程序分配内存。

3、动态分区分配

动态分区分配时是根据进程的实际需要,动态的为之分配内存。

分区的大小和个数,依装入作业的需要而定。

作业装入内存时,把可用内存分出一个连续区域给作业,且分区大小正好适合作业大小的需要。

分配算法

  1. 首次适应算法FF :FF算法要求空闲分区表以地址递增的次序排列(在分配内存时,从表首开始顺序查找,直至找到一个大小能满足要的空闲分区为止;然后按作业的大小,从该分区中划出一块内存空间分配给请求者,余下的空闲分区仍留在空闲分区表中。若从头到尾都不存在满足要求的分区,则分配失败)
    • 优点:优先利用内存低址部分的内存空间,保留了高址部分的大空闲区
    • 缺点:低址部分不断划分,产生小碎片,每次查找从低址部分开始 ,增加了查找的开销
  2. 循环首次适应算法 :在分配内存空间时,从上次找到的空闲分区到下一个空闲分区开始查找,直到找到一个能满足要求的空闲分区,从中划出一块与请求大小相等的内存空间分配给作业(为实现算法,需要设置一起始查寻指针;采用循环查找方式)。
    • 优点:使内存空间分区分布均匀,减少查找的开销
    • 缺点:缺乏大的空闲分区
  3. 最佳适应算法 :所谓 "最佳" 是指每次为作业分配内存时,总是把能满足要求、又是最小的空闲分区分配给作业,避免大材小用。要求将所有的空闲分区按其容量 从小到大 的顺序形成以空闲分区链。
    • 缺点:产生许多难以利用的小空闲区
  4. 最坏适应算法 :此算法需要扫描整个空闲分区表或链表 ,总是挑选 一个最大的空闲区分割给作业使用。该算法要求将所有的空闲分区按其容量以 从小到大的顺序形成以空闲分区链,查找 时只要看第一个分区能否满足作业要求。
    • 优点:剩下的空闲区还可以利用,同时查找效率很高。
    • 缺点:缺乏大的空闲分区
  5. 快速适应算法:该算法又称为分类搜索法,是将空闲分区根据其容量大小进行分类,对于 每一类具有相同容量的所有空间分区,单独设立一个空闲分区链表。系统中存在多个空闲分区链表,同时在内存中设立一张管理索引表,该表的每一个表项对应了一种空闲分区类型,并记录了该类型空闲分区链表表头的指针。

分区分配及回收操作

  • 分区分配:利用某种分配算法,从空闲分区链(表)中找出所需大小的分区。假设请求的分区大小为 u.size,表中每个空闲分区的大小表示为 m.size,若 m.size - u.size <= size(规定不再切割的分区大小),将整个分区分配给 请求者,否则从分区中按请求的大小划出一块内存空间分配出去,余下部分留在空闲链中,将分配区首址返回给调用者。
  • 回收内存 :当进程运行完毕释放内存时,系统根据回收区首址,在空闲分区链(表)中找到相应的插入点,可能有四中情况:
    • 回收区与插入点的前一个分区 F1 邻接。
    • 回收区与插入点的后一个分区 F2 邻接。
    • 回收区与插入点的前后两个分区 F1、F2 邻接。
    • 回收区既不与 F1 邻接,也不与 F2 邻接。

4、可重定位分区分配

  1. 动态重定位的引入 :在连续分配方式中,必须把系统或用户程序装入以连续的内存空间。如果在系统中只有若干个小分区,即使他们的容量总和大于要装入的程序,但由于这些分区不相邻,也无法将程序装入内存
    1. 解决办法:将内存中的所有作业进行移动,使他们全部邻接,这样把原来分散的小分区拼接成大分区,这种方法称为 "拼接" 或 "紧凑"。
    2. 缺点:用户程序在内存中的地址发生变化,必须重新定位。
  2. 动态重定位的实现:在动态运行时装入的方式,将相对的地址转换为物理地址的工作 在程序指令真正要执行时才进行。地址转换需要重定位寄存器的支持。程序执行时访问的内存地址是相对地址与重定位寄存器中的地址相加而成。
  3. 动态重定位分区分配算法:动态重定位分区分配算法 与 动态分区分区算法基本相同,差别在于增加了 "紧凑" 的功能。

5、对换

把内存中暂时不能运行的进程或暂时不用的程序、数据调到外存,以便腾出足够的内存空间,再把已经具备运行条件 的进程和进程所需的程序、数据调入内存。

对换的引入

阻塞进程占据大量内存空间

许多作业再外存而不能进入内存运行

进程的换出

系统首先选择处于阻塞状态且优先级最低的进程作为换出进程,再启动盘块,将该进程的程序和数据传送到磁盘的对换区。

进程的换入

系统应定时查看所有进程的状态,从中找出 "就绪" 状态 但已换出的进程,将换出进程最久的进程作为换入进程,将之换入,直至已无可换入的进程或无可换出的进程为止。


2.5. 内存的回收

内存回收指的是对用户空间中的堆段和文件映射段进行回收(用户使用 malloc、mmap 等分配出去的空间)。用户可以手动地使用 free()等进行内存释放。当没有空闲的物理内存时,内核就会开始自动地进行回收内存工作。回收的方式主要是两种:后台内存回收和直接内存回收。

  • 后台内存回收 (kswapd):在物理内存紧张的时候,会唤醒 kswapd 内核线程来回收内存,这个回收内存的过程异步的,不会阻塞进程的执行,但回收的速度较慢。
  • 直接内存回收 (direct reclaim):如果后台异步回收跟不上进程内存申请的速度,就会开始直接回收,这个回收内存的过程是同步的,会阻塞进程的执行,但回收速度较快。

如果直接内存回收后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会触发 OOM (Out of Memory)机制,选择 OOM 评分较高的进程将其杀死,释放内存资源,直到释放足够的内存。

  • OOM 的评分算法:process_pages + oom_score_adj * totalpages / 1000
    • process_pages:进程已经使用的物理内存页面数。
    • oom_score_adj:进程的 OOM 校准值(0-1000,0为从不杀死)。
    • totalpages:系统总的可用页面数
  • 查看某个进程的 OOM 评分:cat /proc/[pid]/oom_score (该文件显示内核给该进程的当前分数,以便为 oom 杀手选择一个进程。分数越高,就越有可能被 oom 杀手选择)。

可被回收的内存类型

主要有两类内存可以被回收,而且它们的回收方式也不同。

  • 文件页 (File-backed Page):内核缓存的磁盘数据(Buffer)和内核缓存的文件数据(Cache)都叫作文件页。大部分文件页,都可以直接释放内存,以后有需要时,再从磁盘重新读取就可以了。而那些被应用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页),就得先写入磁盘,然后才能进行内存释放。所以,回收干净页的方式是直接释放内存,回收脏页的方式是先写回磁盘后再释放内存
  • 匿名页 (Anonymous Page):这部分内存没有实际载体,不像文件缓存有硬盘文件这样一个载体,比如堆、栈数据等。这部分内存很可能还要再次被访问,所以不能直接释放内存,它们回收的方式是通过操作系统的 Swap 机制,把不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘读入内存就可以了。

文件页和匿名页的回收都是基于 LRU 算法。回收内存的操作基本都会发生磁盘 I/O,如果回收内存的操作很频繁,意味着磁盘 I/O 次数会很多,会影响系统的性能。


2.6. 内存泄漏

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

产生内存泄漏分为以下4类:

  1. 常发性内存泄漏:发生内存泄漏的代码会被多次执行到,每次被执行时都会导致一块内存泄漏。

  2. 偶发性内存泄漏:发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。

  3. 一次性内存泄漏:发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅有一块内存发生泄漏。

  4. 隐式内存泄漏:程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

产生内存泄漏的原因:

在C语言中,从变量存在的时间生命周期角度上,把变量分为静态存储变量和动态存储变量两类。

  • 静态存储变量是指在程序运行期间分配了固定存储空间的变量
  • 动态存储变量是指在程序运行期间根据实际需要进行动态地分配存储空间的变量。

在内存中供用户使用的内存空间分为三部分:程序存储区、静态存储区、动态存储区。程序中所用的数据分别存放在静态存储区和动态存储区中。

  • 静态存储区数据在程序的开始就分配好内存区,在整个程序执行过程中它们所占的存储单元是固定的,在程序结束时就释放,因此静态存储区数据一般为全局变量。
  • 动态存储区数据则是在程序执行过程中根据需要动态分配和动态释放的存储单元,动态存储区数据有三类函数形参变量、局部变量和函数调用时的现场保护与返回地址。由于动态存储变量可以根据函数调用的需要,动态地分配和释放存储空间,大大提高了内存的使用效率,使得动态存储变量在程序中被广泛使用。

程序中动态分配的存储空间,在程序执行完毕后需要进行释放。没有释放动态分配的存储空间而造成内存泄漏,是使用动态存储变量的主要问题。一般情况下,开发人员使用系统提供的内存管理基本函数,如malloc、realloc、calloc、free等,完成动态存储变量存储空间的分配和释放。但是,当开发程序中使用动态存储变量较多和频繁使用函数调用时,就会经常发生内存管理错误,例如:

  • 分配一个内存块并使用其中未经初始化的内容;

  • 释放一个内存块,但继续引用其中的内容;

  • 子函数中分配的内存空间在主函数出现异常中断时、或主函数对子函数返回的信息使用结束时,没有对分配的内存进行释放;

  • 程序实现过程中分配的临时内存在程序结束时,没有释放临时内存。内存错误一般是不可再现的,开发人员不易在程序调试和测试阶段发现,即使花费了很多精力和时间,也无法彻底消除。

检测内存泄漏的工具:

无论是C还是C++程序,运行时候的变量主要有三种分配方式:堆分配、栈分配、全局和静态内存分配。内存泄漏主要是发生在堆内存分配方式中,即 "配置了内存后,所有指向该内存的指针都遗失了",若缺乏语言这样的垃圾回收机制,这样的内存片就无法归还系统。因为内存泄漏属于程序运行中的问题,无法通过编译识别,所以只能在程序运行过程中来判别和诊断。以下为常见检测工具:

  • ccmalloc:Linux和Solaris下对C和C++程序的简单的使用内存泄漏和malloc调试库。
  • Dmalloc:Debug Malloc Library。
  • Electric Fence:Linux分发版中由Bruce Perens编写的malloc()调试库。
  • Leaky:Linux下检测内存泄漏的程序。
  • LeakTracer:Linux、Solaris和HP-UX下跟踪和分析C++程序中的内存泄漏。
  • MEMWATCH:由Johan Lindh编写,是一个开放源代码C语言内存错误检测工具,主要是通过gcc的precessor来进行。
  • Valgrind:调试和分析Linux程序, 用C和c++编写的程序。
  • KCachegrind:用于Cachegrind和Calltree生成的分析数据的可视化工具。
  • IBM Rational PurifyPlus:帮助开发人员查明C/C++、托管.NET、Java和VB6代码中的性能和可靠性错误。PurifyPlus 将内存错误和泄漏检测、应用程序性能描述、代码覆盖分析等功能组合在一个单一、完整的工具包中。
  • ParasoftInsure++:针对C/C++应用的运行时错误自动检测工具,它能够自动监测C/C++程序,发现其中存在着的内存破坏、内存泄漏、指针错误和I/O等错误。并通过使用一系列独特的技术(SCI技术和变异测试等),彻底的检查和测试我们的代码,精确定位错误的准确位置并给出详细的诊断信息。能作为MicrosoftVisual C++的一个插件运行。
  • Compuware DevPartner for Visual C++ BoundsChecker Suite:为C++开发者设计的运行错误检测和调试工具软件。作为Microsoft Visual Studio和C++ 6.0的一个插件运行。
  • Electric Software GlowCode:包括内存泄漏检查,code profiler,函数调用跟踪等功能。给C++和.Net开发者提供完整的错误诊断,和运行时性能分析工具包。
  • Compuware DevPartner Java Edition:包含Java内存检测,代码覆盖率测试,代码性能测试,线程死锁,分布式应用等几大功能模块。
  • Quest JProbe:分析Java的内存泄漏。
  • ej-technologies JProfiler:一个全功能的Java剖析工具,专用于分析J2SE和J2EE应用程序。它把CPU、执行绪和内存的剖析组合在一个强大的应用中。
  • BEAJRockit:用来诊断Java内存泄漏并指出根本原因,专门针对Intel平台并得到优化,能在Intel硬件上获得最高的性能。

3. 磁盘

简介

  • 磁盘(disk)是指利用磁记录技术存储数据的存储器。
  • 磁盘是计算机主要的存储介质,可以存储大量的二进制数据,并且断电后也能保持数据不丢失。早期计算机使用的磁盘是软磁盘(Floppy Disk,简称软盘),如今常用的磁盘是硬磁盘(Hard disk,简称硬盘)。

3.1. 磁盘的构成

磁盘分别由盘片、磁头、磁道、扇区、柱面组成。

  • 盘片:一个磁盘(如一个 1T 的机械硬盘)由多个盘片叠加而成。盘片的表面涂有磁性物质,这些磁性物质用来记录二进制数据。因为正反两面都可涂上磁性物质,故一个盘片可能会有两个盘面。
  • 磁头:磁头用于读取盘面中磁道内的扇区中存储的数据。一个盘片有上下2个盘面对应2个磁头。
  • 磁道:磁道无法用肉眼看到,仅是盘面上以特殊方式磁化了的一些磁化区,磁盘上的信息便是沿着这样的轨道存放。
  • 扇区:磁盘上的每个磁道被等分为若干个弧段,这些弧段便是磁盘的扇区,每个扇区可以存放512个字节的信息,磁盘驱动器在向磁盘读取和写入数据时,要以扇区为单位。
  • 柱面:磁盘的柱面数与一个盘面上的磁道数是相等的,由于每个盘面都有自己的磁头,因此,盘面数等于总的磁头数。

3.2. 磁盘的类型

按硬盘材质可分为:固态硬盘(SSD)、机械硬盘(HDD)、混合硬盘(HHD)。

固态硬盘(SSD)

SSD(Solid State Drive)由多个闪存芯片加主控以及缓存组成的阵列式存储,属于以固态电子存储芯片阵列制成的硬盘。相对机械硬盘,读取速度更快,寻道时间更小,可加快操作系统启动速度和软件启动速度。

SSD内置控制器、FLASH 存储阵列,通过主控,数据直接以数字的存储方式进入存储矩阵或者从矩阵中被读取出来。由于没有机械结构,读取更快速,可以直接通过主控找到存储矩阵中存放数据的位置或者是空余的空间写入数据,整体速度比机械硬盘更快。

  • **优点:**读写速度快,防震抗摔性,低功耗,无噪音,工作温度范围大,轻便。
  • **缺点:**容量小,寿命有限,售价高。

机械硬盘(HDD)

HDD(Hard Disk Drive)是比较常见的一种硬盘,电脑主要的存储媒介之一。由一个或者多个铝制或者玻璃制成的磁性碟片,磁头,转轴,控制电机,磁头控制器,数据转换器,接口和缓存等几个部分组成。

工作时由磁头和磁盘两者运动,找到需要写入或者读取的区域,然后改变磁场方向完成数据的写入、读取。HDD硬盘的运转和磁头的运动有一定的延迟,而且转速也会影响到读取和写入的时间,所以整体写入速度不如SSD。

  • **优点:**容量大,价格实惠,寿命高。
  • **缺点:**读写慢,有噪音,体积大,怕震动,发热量高。

混合磁盘(HHD)

混合硬盘(hybrid harddrive)是机械硬盘与固态硬盘的结合体,采用容量较小的闪存颗粒用来存储常用常用文件,而磁盘才是最重要的存储介质,闪存仅起到了缓冲作用,将更多的常用文件保存到闪存内减小寻道时间,从而提升效率。

  • **优点:**读写快,防震抗摔性强,低功耗,无噪音,工作温度范围大
  • **缺点:**容量比机械盘小,寿命比机械硬盘短,成本价高

硬盘接口类型

目前所能见到的硬盘接口类型主要有IDE、SATA、SCSI、SAS、FC等。

  • IDE代表着硬盘的一种类型,但在实际的应用中,人们也习惯用IDE来称呼最早出现IDE类型硬盘ATA-1,这种类型的接口随着接口技术的发展已经被淘汰了,而其后发展分支出更多类型的硬盘接口,比如ATA、Ultra ATA、DMA、Ultra DMA等接口都属于IDE硬盘。其特点为:价格低廉,兼容性强,性价比高,数据传输慢,不支持热插拔等等。
  • 使用SATA(Serial ATA)口的硬盘又叫串口硬盘,是未来PC机硬盘的趋势。Serial ATA采用串行连接方式,串行ATA总线使用嵌入式时钟信号,具备了更强的纠错能力,与以往相比其最大的区别在于能对传输指令(不仅仅是数据)进行检查,如果发现错误会自动矫正,这在很大程度上提高了数据传输的可靠性。串行接口还具有结构简单、支持热插拔的优点。
  • SCSI并不是专门为硬盘设计的接口,是一种广泛应用于小型机上的高速数据传输技术。SCSI接口具有应用范围广、多任务、带宽大、CPU占用率低,以及热插拔等优点,但较高的价格使得它很难如IDE硬盘般普及,因此SCSI硬盘主要应用于中、高端服务器和高档工作站中。其特点为:传输速率高、读写性能好、可连接多个设备、可支持热插拔,但是价格相对来说比较贵。
  • SAS就是串口的SCSI接口。一般服务器硬盘采用这两类接口,其性能比上述两种硬盘要高,稳定性更强,但是价格高,容量小,噪音大。
  • FC是光纤通道,和SCIS接口一样光纤通道最初也不是为硬盘设计开发的接口技术,是专门为网络系统设计的,但随着存储系统对速度的需求,才逐渐应用到硬盘系统中

查看磁盘类型

cat /sys/block/[磁盘名]/queue/rotational #查看磁盘类型

rotational 表示旋转。HDD是机械硬盘,是磁原理存储,有盘面、柱头这些概念;SSD是固态硬盘,是电原理进行存储,不存在盘面这些概念。

0:表示不旋转,即固态硬盘(SSD)

1:表示可旋转,即机械硬盘(HDD)

lsblk -d -o name,rota #同上,查看全部磁盘类型


lsscsi #列出所有 SCSI 设备

lsscsi -s #列出磁盘容量


3.3. 磁盘性能概述

硬盘性能一般体现在:单碟容量、转速、寻道时间、缓存、传输速率(transfer rate)等几个方面。

单碟容量

单碟容量就是指一张硬盘碟片的容量。因为一个硬盘里面有多张碟片,单碟容量对硬盘大小起着至关重要的影响,且单碟容量直接决定了硬盘的持续数据传输率。

在转速同等的情况下,单碟容量大比单碟容量小的硬盘可以在相同的时间下读取到更多的文件,因此硬盘的传输速率也会加快。

硬盘转速

转速是硬盘内电机主轴的旋转速度,也就是硬盘盘片在一分钟内所能完成的最大转数。转速的快慢是标示硬盘档次的重要参数之一,它是决定硬盘内部传输率的关键因素之一。硬盘的转速越快,硬盘寻找文件的速度也就越快,相对的硬盘的传输速度也就得到了提高。

家用的普通硬盘的转速一般有5400rpm、7200rpm。

  • 5400转多为笔记本,这是目前硬盘最低的转速。
  • 7200转这是标准的高速台式机硬盘。但有些笔记本硬盘也有这个转速。价格较贵。

10000/15000转,这是服务器的SAS接口的硬盘拥有的最高转速。一般个人电脑的主板上没这个接口,是速度最快的机械硬盘。

寻道时间

寻道时间是指硬盘在接收到系统指令后,将磁头从磁道开始移动到数据所在磁道的平均时间。它在一定程度上反映了硬盘读取数据的能力,是影响硬盘内部数据传输速率的重要参数。时间越短,产品性能越好。

硬盘缓存

硬盘的缓存容量也是决定硬盘性能的一个参数。普通硬盘缓存是物理缓存。在这种情况下,缓存越大越好。目前主流1TB普通硬盘拥有64M缓存。

传输速率

磁盘的内部传输速率指的是磁头读写磁盘时的最高速率。这个速率不包括寻道以及等待扇区旋转到磁头下所耗费时间的影响。硬盘的速度越快,硬盘搜索文件的速度就越快,相对硬盘的传输速度也就越快。当计算机的硬盘速度提高时,计算机的读写速度也会提高。


3.4. 测试磁盘读写速度

bash 复制代码
   
   
   
    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         测试磁盘读(块大小为8k,拷贝1048576块,共8GB)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         dd iflag=direct,nonblock 
         
         if=[磁盘名] of=/dev/null bs=8K count=1048576
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         测试磁盘写(块大小为8k,拷贝1048576块,共8GB)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         dd oflag=direct,nonblock 
         
         if=[磁盘名] of=./test_w bs=8K count=1048576
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
         
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         测试磁盘同时读写(块大小为8k,拷贝1048576块,共8GB)
        
        
        
       
       
       

    
* 
       
       
       
        
        
        
       
       
       

       
       
       

        
        
        
         
         dd iflag=direct,nonblock oflag=direct,nonblock 
         
         if=[磁盘名] of=./test_rw bs=8K count=1048576
        
        
        
       
       
       

   

   
   
   

direct :直接使用直接I/O数据

nonblock:使用非阻塞I/O

if :读出

of :写入

bs :块大小

count :块次数


相关推荐
韩楚风3 小时前
【linux 多进程并发】linux进程状态与生命周期各阶段转换,进程状态查看分析,助力高性能优化
linux·服务器·性能优化·架构·gnu
陈苏同学3 小时前
4. 将pycharm本地项目同步到(Linux)服务器上——深度学习·科研实践·从0到1
linux·服务器·ide·人工智能·python·深度学习·pycharm
Ambition_LAO3 小时前
解决:进入 WSL(Windows Subsystem for Linux)以及将 PyCharm 2024 连接到 WSL
linux·pycharm
Pythonliu74 小时前
茴香豆 + Qwen-7B-Chat-Int8
linux·运维·服务器
你疯了抱抱我4 小时前
【RockyLinux 9.4】安装 NVIDIA 驱动,改变分辨率,避坑版本。(CentOS 系列也能用)
linux·运维·centos
追风赶月、4 小时前
【Linux】进程地址空间(初步了解)
linux
栎栎学编程4 小时前
Linux中环境变量
linux
挥剑决浮云 -4 小时前
Linux 之 安装软件、GCC编译器、Linux 操作系统基础
linux·服务器·c语言·c++·经验分享·笔记
小O_好好学5 小时前
CentOS 7文件系统
linux·运维·centos
x晕x6 小时前
Linux dlsym符号查找疑惑分析
linux·运维·服务器