操作系统知识点总结
操作系统
操作系统定义:
-
OS是管理计算机硬件与软件资源的程序,是计算机的基石
-
OS本质上是一个运行在计算机上的软件程序,用于管理计算机硬件和软件资源
-
OS存在屏蔽了硬件层的复杂性
-
OS的内核是OS的核心部分,它负责系统的「内存管理」,「硬件设备的管理」,「文件系统的管理」,「应用程序的管理」,操作系统内核是连接应用程序和硬件的桥梁,决定着系统的性能和稳定性
系统调用、用户态、内核态定义:
-
用户态(user mode):用户态运行的进程或程序可以直接读取「用户程序」的数据
-
系统态(kernel mode):系统态运行的进程或程序几乎可以访问计算机的「任何资源」,不受限制
-
系统调用:程序一般运行在「用户态」,当要调用「系统级别的子功能」就需要「系统调用」了。也就是说在我们运行的用户程序中,凡是与系统态级别的资源有关的操作(如文件管理、进程控制、内存管理等),都必须通过「系统调用」方式向操作系统提出服务请求,并由操作系统代为完成。「系统调用」也给用户访问「操作系统内核」提供了「唯一」的途径
-
系统调用分类:
-
设备管理:完成设备的请求或释放,以及设备启动等功能
-
文件管理:完成文件的读、写、创建及删除等功能
-
进程控制:完成进程的创建、撤销、阻塞及唤醒等功能
-
进程通信:完成进程之间的消息传递或信号传递等功能
-
内存管理:完成内存的分配、回收等功能
-
进程与线程:
-
一个进程中可以有多个线程,Java程序中多个线程共享对应进程的堆和方法区 (JDK1.8 之后的 元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈和本地方法栈
-
为什么要有线程:线程可以提高CPU的并行度
-
进程与线程的区别:
-
调度:
-
进程是「资源分配」的基本单位
-
线程是「任务调度」的基本单位
-
-
切换:
-
进程上下文切换较慢(涉及到页表转换)
-
线程上下文切换较快(不涉及页表转换)
-
-
拥有资源:
-
进程是拥有资源的一个独立单位
-
线程基本不拥有系统资源(只拥有程序计数器、栈、寄存器等资源),但是可以访问隶属于进程的全部资源
-
-
系统开销:
-
进程被创建或撤销时,系统都要为之分配或回收系统资源,如内存空间,I/O设备等
-
线程被创建或撤销时,开销相较于进程而言小很多
-
-
-
线程共享隶属于进程的那些资源:
-
页表(虚拟地址 → 物理地址的转换表)
-
堆区域、方法区
-
进程与线程的切换流程:
-
进程的切换:
-
实际上就是「被中断运行进程」与「待运行进程」进行「上下文切换」
-
切换「页表」以使用新的「虚拟地址空间」
-
切换内核栈,新的内核栈中加入新的内容「PCB控制块,资源相关」(切换硬件上下文)
-
-
-
线程的切换:
-
实际上就是「被中断运行线程」与「待运行线程」进行「上下文切换」
- 切换栈,新的栈中加入新的内容「TCB控制快,资源相关」(切换硬件上下文)
-
-
对于Linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2步是进程和线程切换都要做的(上下文切换快)
-
每个进程都有自己的「虚拟地址空间」,也就是都有属于自己的「页表」,而线程是「共享」所在进程的「虚拟地址空间」的,因此同一个进程中的线程进行线程切换时不涉及「虚拟地址空间」的转换
内核态线程和用户态线程:
-
「进程」的实现只能由「操作系统内核」来实现,只存在「内核态」而不存在用户态实现的情况
-
「线程」就不同了,线程的管理者可以是「用户」也可以是「操作系统」本身,线程是进程内部的东西,当然存在由进程直接管理线程的可能性。因此线程的实现就应该分为内核态线程实现和用户态线程实现
-
内核态线程的实现:
-
操作系统向管理「进程」一样,应该保持维护「线程的所有资源」,将「线程控制块」存放在操作系统的「内核空间」中
-
此时操作系统就同时掌管「进程控制块」和「线程控制块」
-
优点:
-
用户编程简单
-
如果一个线程执行阻塞操作,操作系统可以从容的调度另外一个线程的执行
-
-
缺点:
- 涉及「用户态」与「内核态」之间的转换
-
-
-
用户态线程的实现:
-
用户态管理线程就是用户自己做线程的切换,自己管理线程的信息,操作系统无需知道线程的存在
-
优点:
-
实现灵活,切换线程快
-
无需涉及「用户态」与「内核态」之间的转换
-
-
缺点:
- 用户编程复杂,可能发生线程饥饿、死锁
-
-
-
-
现代操作系统:
-
鉴于用户态线程与内核态线程都存在缺陷,现代操作将两者结合起来
-
「用户态」的执行负责「进程内部线程」在「非阻塞」时的切换
-
「内核态」的操作系统负责「阻塞线程」的切换
-
进程的状态:
-
创建状态:进程正在被创建
-
就绪状态:进程获得了除了CPU以外所有的资源,一旦获得CPU资源即可运行
-
运行状态:进程在CPU上运行
-
阻塞状态:又称为等待状态,进程正在等待某一事件而暂停运行
-
结束状态:运行结束,让出CPU
进程间的通信方式:
-
管道 / 匿名管道(Pipes) :FIFO,数据存储在「内存」中,半双工的通信方式,用于具有亲缘关系进程之间的通信,跟随进程的生命周期
-
命名管道(Names Pipes) :FIFO,数据以「管道文件」的形式存储在磁盘/文件系统中,半双工的通信方式,允许无亲缘关系的进程通信,跟随进程的生命周期
-
消息队列(Message Queuing) :FIFO,也可以根据消息的类型读取,数据保存在「OS内核缓冲区」,消息的链表,有写权限的进程可以向队列中添加消息,有读权限的进程则可以读走队列中的消息,跟随OS的生命周期,但读取和写入会带来用户态和内核态的转换
-
共享内存(Shared memory):映射一段能被其他进程所访问的共享内存,共享内存由一个进程创建。往往与「信号量」进行配合使用实现进程之间的同步和通信
-
信号量(Semaphores) :用来控制多个进程对共享内存空间的访问,常作为一种锁机制,实际上是一个计数器,实现进程的互斥和同步
-
信号(Signal) :异常模式下,用于通知接收进程某个事件已经发生,而无需知道该进程的状态,比如kill -9
-
套接字(Sockets) :上述都是同一台机器的进程通信,而作用于「不同机器间」的进程通信(RPC)
总结:
由于每个进程的用户空间都是独立的,不能相互访问,这时就需要借助「OS内核空间」来实现进程间通信,原因很简单,每个进程都是共享一个「OS内核空间」
线程间的通信方式:
-
线程间通信的模型有两种:
-
共享内存
- 使用Volatile关键字,共同监听被Volatile修饰的变量
-
消息传递
- Wait()和Notify()方法,线程相互通信
-
进程间和线程间的同步方式:
-
进程和线程的同步定义:两个或多个共享关键资源的线程(多个线程共享一个资源)的并发执行。应该同步线程以避免关键的资源使用冲突
-
互斥对象:采用互斥对象机制,只有拥有「互斥对象」的线程才有访问公共资源的权限,因为互斥对象只有一个,所以可以保证公共资源不会被多个资源同时访问,比如Java中的Sychronized和各种Lock锁都是使用的这种机制
-
信号量:允许同一时刻多个线程访问同一资源(线程并行),但是需要控制同一时间访问此资源的最大线程数量(AQS中的State等)
-
信号:通过「通知操作」的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作(Object类中的Notify方法等)
-
进程的调度算法:
-
使用调度算法的目的:为了确定首先执行哪个进程以及最后执行哪个进程以实现最大 CPU 利用率
-
先到先服务调度算法(FCFS):选择最先来的进程给予它CPU的时间片
-
短作业优先调度算法(SJF):选择运行时间最短的进程给予它CPU的时间片
-
最短剩余时间调度算法:最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度
-
优先级调度算法:为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行
-
时间片轮转调度算法:最古老、最简单的算法。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
-
多级反馈队列调度算法:既能使高优先级的作业得到响应又能使短作业迅速完成,因而它是目前被公认的一种较好的进程调度算法,UNIX 操作系统采取的便是这种调度算法
-
死锁:
-
死锁产生的四个必要条件:(有一个条件不成立,则不会产生死锁)
-
互斥条件:一个资源一次只能被一个进程使用
-
请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放
-
不剥夺条件:进程获得的资源,在未完全使用完之前,不能被强行剥夺
-
循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系
-
银行家算法:
-
当一个进程申请使用资源的时候,银行家算法通过先「试探」分配给该进程资源,然后通过「安全性算法」判断分配后的系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待
-
安全性算法:
-
若申请的资源数量小于等于Available,然后接着判断分配给「P1」后剩余的资源,能不能使「进程队列」的某个进程执行完毕,若没有进程可执行完毕,则系统处于不安全状态(即此时没有一个进程能够完成并释放资源,随时间推移,系统终将处于死锁状态)
-
若有进程可执行完毕,则假设回收已分配给它的资源(剩余资源数量增加),把这个进程标记为可完成,并继续判断队列中的其它进程,若所有进程都可执行完毕,则系统处于安全状态,并根据可完成进程的分配顺序生成安全序列
-
内存管理:
-
内存管理的作用:主要负责内存的分配与回收,地址转换(将逻辑地址转化为具体的物理地址)
-
内存管理的两种机制:
-
块大小 > 页大小 > 段大小
-
连续分配机制:
连续分配管理方式是指为一个用户程序分配一个连续的内存空间- 块式管理:远古时期的管理方式,将内存分为几个固定大小的块,每个块中只包含一个进程。如果程序运行需要内存的话,操作系统就分配给它一块,如果程序运行只需要很小的空间的话,分配的这块内存很大一部分几乎被浪费了。这些在每个块中未被利用的空间,称之为内存碎片
-
非连续分配机制:
非连续分配管理方式允许一个程序使用的内存分布在离散或者说不相邻的内存中-
页式存储:把主存分为大小相等且固定的一⻚一⻚的形式,⻚较小,相对相比于块式管理的划分力度更大,提高了内存利用率,减少了碎片。⻚式管理通过「⻚表」对应「逻辑(虚拟)地址」和「物理地址」,这样才能非连续分配
-
段式存储:⻚式管理虽然提高了内存利用率,但是⻚式管理其中的⻚实际并无任何实际意义。段式管理把主存分为一段段的,每一段的空间又要比一⻚的空间小很多 。但是,最重要的是段是有实际意义的,每个段定义了一组「逻辑信息」 ,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。 段式管理通过「段表」对应「逻辑(虚拟)地址」和「物理地址」
-
段页式存储:结合了「页式管理」 和「段式管理」的优点。段⻚式管理机制就是把主存先分成若干段,每个段又分成若干⻚,也就是说「段⻚式管理机制」中「段与段之间」以及「段的内部」都是离散的
-
-
分页太大或太小的影响:
-
分页太小:
-
优点:减少内存碎片,提高内存利用率
-
缺点:进程的页表太长,占用太多内存空间;降低页面「换进换出」的效率
-
-
分页太大:
-
优点:减小页表长度,提高页面「换进换出」效率
-
缺点:内存碎片多,降低内存利用率
-
-
-
快表和多级页表:
-
页式存储中,很重要的两点:
-
虚拟地址转换为物理地址要快
-
要解决虚拟地址空间太大,页表页比较大的情况
-
-
快表:为了提高虚拟地址到物理地址的转换速度,操作系统在「⻚表」基础之上引入了「快表」来加速虚拟地址到物理地址的转换
-
多级页表:提高内存空间性能,引入「多级⻚表」的主要目的是为了避免把「全部⻚表」一直放在内存中占用过多空间,将页表进行分级,特别是那些根本就不需要的⻚表就不需要保留
分页机制和分段机制的异同:
-
分页是「页式存储」的实现方式、分段是「段式存储」的实现方式
-
共同点:
-
分⻚机制和分段机制都是为了提高内存利用率,较少内存碎片
-
⻚和段都是非连续(离散)存储的,所以两者都是非连续(离散)分配内存的方式。但是,每个⻚和段中的内存是连续的
-
-
区别:
-
⻚的大小是固定的,由操作系统决定,而段的大小不固定,取决于我们当前运行的程序
-
分⻚仅仅是为了满足操作系统内存管理的需求,而段是逻辑信息的单位,在程序中可以体现为代码段,数据段
-
分页的地址空间是一维地址空间,而分段的地址空间是二维的
-
-
-
现代操作系统:
- 大部分使用「分页机制」对内存进行管理。分页机制加上与内存相关的异常就已经足够完成内存管理功能。没有必要增加复杂度再引入分段机制。而且分段是x86 CPU提供的功能,为了考虑移植性,现代操作系统大部分都是使用的「分页机制」
逻辑(虚拟)地址和物理地址的区别:
-
编程语言一般操作的都是逻辑(虚拟)地址,是相对的地址
-
物理地址是指真实的地址,具体点就是内存寄存器中的地址
交换空间:
-
操作系统把「物理内存」分成一块一块的小内存,每一块内存被称为页(page)。当内存资源不足时,Linux把「某些页的内容」转移至「硬盘」上的一块空间上,以「释放内存空间」。硬盘上的那块空间叫做「交换空间」(swap space),而这一过程被称为交换(swapping)
-
物理内存(总容量) + 交换空间(总容量) = 虚拟内存(可用容量)
-
用途:
-
物理内存不足时一些不常用的页可以被交换出去,腾给系统
-
程序启动时很多内存页被用来初始化,之后便不再需要,可以交换出去
-
虚拟内存:
-
作用:没有虚拟地址空间的时候,程序都是直接访问和操作的都是物理内存,直接把物理地址暴露出来的话会带来严重问题,比如可能对操作系统造成伤害以及给同时运行多个程序造成困难,「虚拟内存」可以让程序可以拥有超过系统物理内存大小的「连续」的可用的内存空间。另外,虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉
-
定义:虚拟内存就是说,让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。虚拟内存使用「部分加载」的技术,让一个进程或者资源的「某些页面」加载进内存,从而能够加载更多的进程,甚至能加载比内存大的进程,这样看起来好像内存变大了,这部分内存其实包含了磁盘或者硬盘,并且就叫做虚拟内存(使用了交换空间技术)
-
局部性原理:局部性原理是虚拟内存技术的基础,正是因为程序运行具有局部性原理,才可以只装入部分程序到内存就开始运行
-
虚拟内存的技术实现(三种实现方式):
-
请求分页存储管理:建立在「分⻚存储」管理之上,为了支持虚拟存储器功能而增加了「请求调⻚」功能、「⻚面置换」功能。请求分⻚是目前最常用的一种实现虚拟存储器的方法。请求分⻚存储管理系统中,在作业开始运行之前,仅装入当前要执行的部分段即可运行
-
请求分段存储管理:建立在「分段存储」管理之上,增加了请求「调段」功能、「分段置换」功能。请求分段储存管理方式就如同请求分⻚储存管理方式一样,在作业开始运行之前,仅装入当前要执行的部分段即可运行
-
请求段页式存储管理:
-
-
技术实现总结:
-
一定容量的内存和外存:在载入程序的时候,只需将程序的「一部分」装入内存,而将「其他部分」留在外存(硬盘),然后程序就可以执行了
-
缺⻚中断:如果需执行的指令或访问的数据尚未在内存(称为缺⻚或缺段),则由处理器通知操作系统将相应的⻚面或段从外存(硬盘)中调入内存,然后继续执行程序
-
虚拟地址空间(页表):逻辑地址到物理地址的变换
-
-
虚拟内存中的「页面置换算法」:
-
定义:在程序运行过程中,如果要访问的页面不在内存中,就发生「缺页中断」从而将该页调入「内存」中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘的「交换空间」中来腾出内存空间
-
OPT(OPTimal) 最佳⻚面置换算法
-
FIFO(First In First Out) 先进先出⻚面置换算法
-
LRU (Least Currently Used) 最近最久未使用⻚面置换算法
-
LFU (Least Frequently Used) 最少使用⻚面置换算法
-
-
OS内存模型:
-
计算机中存储设备和处理器的运算速度查了几个数量级的差距(内存最大读写速度 跟不上处理器的处理速度)所以加入一层读写速度尽可能接近处理器处理速度的「高速缓存」作为内存与处理器之间的缓冲区,运算时把需要使用到的「数据」复制到「缓冲区」运算结束后再写回「主存」中
-
每个处理器都有自己的独立的「高速缓存区」,基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,但是也为计算机系统带来更高的复杂度
-
引入了一个新的问题:缓存一致性(CacheCoherence)
-
在多处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一主内存(MainMemory)
-
多处理器的运算任务都涉及到一个内存区域同步会主内存时会导致各个缓存中的数据不一致,每个处理器访问缓存时都要遵守一些协议「MSI MESI MOSI」等
缓冲区溢出:
-
缓冲区溢出是指当计算机向「缓冲区」填充数据时超出了缓冲区本身的容量,「溢出的数据」覆盖在「合法数据」上
-
危害(原因:造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入)
-
程序崩溃,导致拒绝服务
- 分段错误,「页表映射」异常,无法通过逻辑地址访问正确的物理地址
-
跳转并且执行一段恶意代码
- 通过制造「缓冲区溢出」使程序运行一个用户shell,再通过shell执行其它命令,如果该程序拥有root权限,相当于攻击者获得了一个root权限的shell,就可以对系统做任何操作了
-
同步、异步、阻塞、非阻塞:
-
同步、异步(与消息的通知机制有关):
-
同步:
- 同步是在发出一个功能调用时,在没有得到结果之前,该调用就不返回(如:在调用一个系统调用sum函数时,没有sum计算完成就不返回完成)
-
异步:
- 异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。比如:调用aio_read系统调用时,不必等IO操作完成就直接返回,调用结果通过信号来通知调用者
-
-
阻塞、非阻塞(与等待消息通知时的程序状态有关):
-
阻塞:
- 阻塞调用是指调用结果返回之前,当前线程会被挂起。该函数只有在得到结果之后才会返回
-
非阻塞:
- 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回,此时线程可以做其他事情
-
Unix OS的5种IO模型:
-
阻塞式I/O:(应用进程被阻塞,直到数据从内核缓冲区复制到用户缓冲区才返回)
-
非阻塞式I/O:(应用进程不会被阻塞,可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成,称为轮询)
-
I/O多路复用(select,poll,epoll等):(单个进程拥有处理多个 I/O 事件的能力)
-
信号驱动式I/O(SIGIO):(数据在到达内核缓冲区之后向应用进程发送 SIGIO 信号)
-
异步I/O(POSIX的aio_系列函数):(内核完成所有操作后向应用进程发送信号)
I/O 复用技术:
-
定义:多个 I/O 操作都能在「同一个进程/线程」内并发交替的顺序完成,这叫做 I/O 多路复用,这里的「复用」是指复用同一个「进程/线程」
-
Select(O(n)):无差别轮询的去处理 I/O 请求,底层通过数组实现,select具有O(n)的无差别轮询复杂度,同时处理的流越多,轮询时间就越长
-
Poll(O(n)):忙轮询的去处理 I/O 请求,底层通过链表实现,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有「最大连接数」的限制,原因是它是基于链表来存储的
-
Epoll(O(1)):理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的
-
工作模式:
-
LT(水平触发):当Epoll_wait()检测到描述符事件到达时,将此事件通知给进程,进程可以不立即处理事件,下次调用Epoll_wait()方法会再次通知进程
-
ET(边沿触发):当Epoll_wait()会将事件通知给进程,进程必须立即处理事件。减少了同一事件的触发次数,效率更高
-
-
零拷贝:
-
以读操作为例:假设用户程序发起一次读请求,会调用read相关读「系统函数」,然后会从用户态切换到内核态(系统调用),随后CPU会让DMA从磁盘把数据拷贝到「内核缓冲区」,等到「内核缓冲区」真正有数据之后,CPU会把「内核缓冲区」的数据拷贝到「用户缓冲区」,最终用户程序才能读取到
系统调用 → 用户态转为系统态 → 数据从磁盘到内核缓冲空间 → 数据从内核缓冲区到用户缓冲区 → 用户程序读 -
零拷贝:将数据从「内核缓冲区」拷贝到「用户缓冲区」这个CPU拷贝一步给省去,相当于是将数据直接拷贝到用户缓冲区来提高数据传输的效率和性能
-
常见的零拷贝技术:
-
mmap(内核缓冲区与用户缓冲区共享)
-
sendfile(系统底层函数支持)
-
孤儿进程、僵尸进程、守护进程:
-
孤儿进程表示父进程退出了,但是子进程还在运行,这些就是孤儿进程,孤儿进程会被init进程(进程号为1)回收,所以基本不会有问题
- 父死子活
-
僵尸进程是子进程正常退出,会给父进程发送信号,告诉父进程释放掉进程号,但是如果父进程不选择接受,不调用wait/waitpid,进程号将不会被释放
-
子死父活
-
如何解决:手动调用wait/waitpid
-
局部性原理:
-
局部性原理是虚拟内存技术的基础,正是因为程序运行具有局部性原理,才可以只装入部分程序到内存就开始运行
-
时间局部性:指的是在程序运行过程中「最近被引用到的存储器位置」在程序执行后期还会「被多次引用」到的可能性很大
-
空间局部性:指的是程序运行过程中如果一个存储器的位置被引用,那么在程序执行后期该存储器附近的位置被引用的可能性很大
-
通俗易懂:
-
时间:一个变量在程序运行过程中,如果被引用过一次,那后续很有可能会再被引用到
-
空间:一个变量被访问到过后,这个变量所在的位置附近的位置很有可能在程序后续运行中被访问到
-
-
优点:
- 充分的利用 CPU资源
-
缺点:
- 某些场景应避免,如大数据场景(数据倾斜)
Linux如何创建进程:
- 当一个程序调用fork()时,将本进程的内存空间复制出来一个(Copy-On-Write写时复制),构建一个新的进程
CPU负载和利用率的区别:
-
CPU负载:
-
Load Average :负载的3个数字,比如上图的4.86,5.28,5.00,分别代表系统在过去的1分钟,5分钟,15分钟内的系统平均负载
-
代表的是当前系统「正在运行」和「处于等待运行」的进程数之和。也指的是处于「可运行状态」和「不可中断状态」的平均进程数
-
如果单核CPU的话,负载达到1就代表CPU已经达到满负荷的状态了,超过1,后面的进行就需要排队等待处理了
-
如果是是多核多CPU的话,假设现在服务器是2个CPU,每个CPU2个核,那么总负载不超过4都没什么问题
-
-
-
CPU利用率:
- CPU利用率指的是当前正在运行的进程实时占用CPU的百分比,是对一段时间内CPU使用状况的统计
-
e.g:
-
假设CPU为单核,有一个进程占用了CPU,这时候负载就是1,如果还有一个进程在等待让出CPU,那么负载就是2
-
如果在1个小时内,第一个进程占用CPU了10分钟,第二个进程占用CPU了20分钟,剩下30分钟CPU都没被占用,那么这一个小时内的CPU利用率就是50%
-
CPU负载很高但利用率很低「IO密集型」:
-
CPU负载很高,利用率却很低,说明处于等待状态的任务很多,负载越高,代表可能很多僵死的进程。通常这种情况是「IO密集型」的任务,大量请求在请求相同的IO,导致任务队列堆积
-
解决办法:通过命令 ps -axjf 查看是否存在状态为 D+ 状态的进程,这个状态指的就是不可中断的睡眠状态的进程。处于这个状态的进程无法终止,也无法自行退出,只能通过恢复其依赖的资源或者重启系统来解决
CPU负载很低但利用率很高「计算密集型」:
-
这表示CPU的任务并不多,但是任务执行的时间很长,大概率就是你写的代码本身有问题,通常是计算密集型任务,生成了大量耗时短的计算任务
-
解决办法:通过命令 top 找到占用CPU最高的进程,定位代码
-
通过
top
找到占用率高的进程。
-
通过
top -Hp pid
找到占用CPU高的线程ID。这里找到958的线程ID -
再把线程ID转化为16进制,
printf "0x%x\n" 958
,得到线程ID0x3be
- 通过命令
jstack 163 | grep '0x3be' -C5 --color
或者jstack 163|vim +/0x3be -
找到有问题的代码
-