脑图系列-操作系统
打开电源操作系统做了什么?
加载BIOS
当计算机插上电源时,计算机主板的BIOS开始工作。BIOS会进行POST(Power-On Self-Test)自检,检测计算机的硬件是否正常,包括处理器、内存、硬盘、显卡、网卡等设备。如果有问题,则会在屏幕上显示错误信息。
加载引导程序
当自检完成后,BIOS会从预设的启动设备(如硬盘、光盘、U盘等)中加载操作系统的引导程序。
初始化内核
引导程序会进一步加载操作系统内核和文件系统等组件,操作系统内核(kernel)是操作系统的核心组件,它负责管理计算机的各个部分、处理系统调用和线程调度等。一旦内核被找到并加载,操作系统就开始初始化内核,并设置必要的数据结构和内核变量。
加载设备驱动程序
设备驱动程序是操作系统中的一部分,负责控制计算机中的各种硬件设备,如硬盘、显卡、网卡等。操作系统会加载相应的设备驱动程序,使得这些硬件设备可以正常工作。
启动系统服务
启动系统服务,如网络服务、远程登录服务、防火墙服务等,以满足计算机在不同环境下的需求。
内存管理
分段和分页是两种不同的内存管理技术,用于管理和分配进程的内存空间。它们的主要目标是提供对物理内存的有效和安全访问,但它们采用了不同的方法来实现这一目标
分页
大部分虚拟内存系统都会使用分页技术。分页技术把物理内存按照固定大小分为页面(一般是4KB)。通过分页,我们可以更细粒度的管理物理内存和虚拟空间的映射,也可以更高效的利用物理内存
- 一个64KB的虚拟地址和32KB的物理地址映射图
- 如果用户访问到的虚拟地址还没有分配物理地址,那么系统就会发生缺页中断。将需要访问的页面装载到页框中,修改MMU中的映射关系,重新返回。
- 对于虚拟地址和物理地址的映射关系的存储,我们一般是通过页表来完成的。页表中会记录虚拟地址的虚拟页号和偏移量,以及其对应的实际页框号。从数学角度说,页表是一个函数,它的参数是虚拟页号,结果是物理页框号。
- 因为增加了页表的映射,中间就一定会有空间和时间的损耗,有两个问题
页表
• 虚拟地址到物理地址的映射需要时间,这个时候就需要引入快表(TLB)加速页表的访问
• 如果虚拟地址空间很大,页表也会很大,可能会浪费空间。假设64bit的操作系统,虚拟地址的大小为2^64,如果每页是4KB,那么需要的记录数就是2^64/2^12=2^52项。同时,因为每个进程都有一个独立的虚拟地址,所以每个进程都有自己独立的页表,那么这种情况下,记录的项目就会变得非常多
• 可以采用多级页表和倒排页表的方式来解决
• 多级页表的核心就在于避免把所有的页表全部保存在内存中,如果是不需要的页表,就不会保留
• 倒排页表常用于64bit的计算机中,往往需要和TLB结合使用才可以
分段
有了分页之后,一个进程就可以享受很大的,独立的虚拟地址了。但是有一个问题我们没有考虑到,一个进程中一定会包含代码,常量,以及代码在运行时所产生的变量。那么,用户的程序如何保证这些代码所在的空间不和代码运行时候的堆栈空间冲突呢?
- 如果我们只是简单的将虚拟内存按照一维的方式将空间分给这些堆栈,代码片段等,就很难保证空间的有效利用
•
• 我们发现,程序的堆栈空间已经用完了,但是代码段还有很多没有用。这个时候就需要程序手动管理这些一维空间,无疑的比较复杂且困难的
- 所以就有了分段技术的产生。它可以让每个段独享一部分可以自然增长的空间,且和其他段相互隔离
•
分段分页的区别
CPU利用率与负载
CPU 利用率
CPU 利用率表示 CPU 正在执行指令的时间比例,即 CPU 忙碌的程度。它是一个百分比值,表示在某个时间间隔内,CPU 处于非闲置状态的时间比例
- 如果 CPU 利用率为 70%,这意味着在观察的时间间隔内,有 70% 的时间 CPU 正在处理任务,而 30% 的时间处于空闲状态。
- top命令可以查看负载和cpu占用
查看
• top - 10:58:07 up 18:13, 1 user, load average: 0.32, 0.24, 0.19
Tasks: 64 total, 1 running, 63 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.1%us, 0.2%sy, 0.0%ni, 92.8%id, 0.1%wa, 0.0%hi, 0.0%si, 6.8%st
Mem: 8388608k total, 5928076k used, 2460532k free, 0k buffers
Swap: 16777216k total, 0k used, 16777216k free, 3181996k cached
• 第三行cpu指标含义同下
- vmstat
• vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。
• procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 1 0 2446260 0 3202312 0 0 201 16304 1 6 0 0 84 5 1
• cpu指标
• %us:用户进程执行时间百分比
• %sy:内核系统进程执行时间百分比
• %id:空闲时间百分比
• %wa:IO等待时间百分比
• %st:虚拟 CPU 等待实际 CPU 的时间的百分比
• 概要
• us的值比较高时,说明用户进程消耗的CPU时间多,但是如果长期超50%的使用,那么我们就该考虑优化程序算法或者进行加速。
• sy的值高时,说明系统内核消耗的CPU资源多,这并不是良性表现,我们应该检查原因。
• wa的值高时,说明IO等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。
负载(Load)
负载(Load)通常指的是一段时间内系统等待处理的工作量。在 Unix-like 系统中,负载平均(Load Average)通常表示在过去 1 分钟、5 分钟和 15 分钟内运行队列的平均长度。运行队列长度是指在某一时刻,处于就绪状态和运行状态的进程数量的和。这个数值包括了正在使用 CPU 的进程和等待 CPU 的进程。
- 在Linux系统中,系统负载是对当前CPU工作量的度量,被定义为特定时间间隔内运行队列中的平均线程数。
- uptime
查看负载
• uptime命令可以显示的信息显示依次为:现在时间、系统已经运行了多长时间、目前有多少登陆用户、系统在过去的1分钟、5分钟和15分钟内的平均负载。
• 13:29 up 23:41, 3 users, load averages: 1.74 1.87 1.97
• 1.74 1.87 1.97 这三个数字的意思分别是1分钟、5分钟、15分钟内系统的平均负荷。我们一般表示为load1、load5、load15
- w
• w命令能够显示目前登入系统的用户信息
• 项目显示顺序如下:登录帐号、终端名称、远 程主机名、登录时间、空闲时间、JCPU、PCPU、当前正在运行进程的命令行
• 20:08 up 55 days, 10:49, 2 users, load averages: 3.31 3.29 3.01
USER TTY FROM LOGIN@ IDLE WHAT
xiaomu console - 24 124 55days -
xiaomu s003 - 20:01 - w
- top
• top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器
多大合适
- 当系统负荷持续大于0.7,你必须开始调查了,问题出在哪里,防止情况恶化。
- 当系统负荷持续大于1.0,你必须动手寻找解决办法,把这个值降下来。
- 当系统负荷达到5.0,就表明你的系统有很严重的问题,长时间没有响应,或者接近死机了。你不应该让系统达到这个值。
- 跑定时任务集中处理数据时负载比较高,正常,其他情况酌情判断
• 如果我们认为0.7算是单核机器负载的安全线的话,那么四核机器的负载最好保持在3(4*0.7 = 2.8)以下
负载高排查
- 1、top命令查看负载和高负载的进程id
- 2、top -Hp <pid>
• 查看进程内线程负载信息,选择占用cpu较高的线程pid
- 3、printf %x <线程pid>
• 线程pid转为16进制进行日志查看
- 4、jstack <进程pid> |grep -A 200 <16进制线程pid>
• 查看线程日志,看到执行的方法
举例
一家餐厅,这家餐厅有4张餐桌( CPU 的核心数),每张餐桌只能服务一个顾客(任务或者进程)
- 如果说,晚高峰时段,4张餐桌中有3张一直有顾客在用餐,那么我们可以说这家餐厅的“使用率”(类似于 CPU 利用率)大概是 75%。这就是CPU利用率。
- 而“负载”则更像是在描述,除了正在用餐的顾客外,还有多少顾客在等待空出的餐桌。如果晚高峰时段除了3个正在用餐的顾客外,还有1位顾客在等待,我们可以说这个时候餐厅的负载是 4(3位正在用餐 + 1位等待)。如果有4位顾客在用餐,另外4位在等待,那餐厅的负载就是 8
• 负载指排队等待人数
- CPU 利用率是告诉我们餐厅(CPU)的资源(餐桌/处理能力)被使用的程度,而负载则是在告诉我们餐厅面对的顾客总数(包括正在服务的和等待的)。CPU 利用率高,意味着餐厅运转忙碌;负载高,意味着很多顾客需要服务,可能会导致顾客等待。
• cpu利用率为顾客用餐时间
CPU 利用率关注的是资源的使用效率,而负载则关注的是服务的压力大小
进程,线程和协程
进程
进程是系统进行资源分配和保护的基本单位
进程具有一个独立的执行环境。 进程是程序在计算机上的一次执行活动 。通常情况下,进程拥有一个完整的、私有的基本运行资源集合。特别地,每个进程都有自己的内存空间。
进程往往被看作是程序或应用的代名词,然而,用户看到的一个单独的应用程序实际上可能是一组相互协作的进程集合
线程
线程有时也被称为轻量级的进程。进程和线程都提供了一个执行环境,但创建一个新的线程比创建一个新的进程需要的资源要少。线程是在进程中存在的,每个进程最少有一个线程。线程又如下特点:
1、线程共享进程的资源,包括内存和打开的文件。线程之间的通信不用进行系统调用,更节约时间
2、线程更轻量,线程实体包括程序,数据和TCB
3、线程是调度的基本单位
协程
所谓的协程,是英语翻译过来的(Coroutine),也叫纤程。通过Coroutine来理解,是协作的程序。它其实是不能和进程,线程相提并论的,因为协程是用户态的东西,不会被OS感知到的。但是因为GoLang的大火,大家总是把他们放到一起来比较
对于多次IO操作来说,我们可以用多线程来完成,但是多线程会引起资源竞争,导致CPU算力的浪费。为了避免这种情况,我们也可以用异步的方式,但是异步的方式会引起callback hell,严重影响了代码的可读性。
所以就需要协程,不让OS通过竞争的方式决定调用哪些线程,而是由用户自己决定如何去执行逻辑。它可以让我们用逻辑流的顺序去写控制流,而且还不会导致操作系统级的线程阻塞。因为协程是由应用程序决定的,所以任务切换的上下文也会交给了用户态来保存。目前GO和Ruby等编程语言都实现了协程,Java也在19的时候引入了协程。
演化
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程
在多个进程之间切换的时候,需要进行上下文切换。但是上下文切换势必会耗费一些资源。于是人们考虑,能不能在一个进程中增加一些“子任务”,这样减少上下文切换的成本。比如我们使用Word的时候,它可以同时进行打字、拼写检查、字数统计等,这些子任务之间共用同一个进程资源,但是他们之间的切换不需要进行上下文切换
所以,在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)
多个线程的切换还是需要时间的,而且多线程也会竞争CPU资源引起不必要的资源浪费,更不要说加锁阻塞了。所以,有没有可能不做切换,充分压榨CPU的资源呢?这就诞生了协程。协程可以将用户自己决定什么时候切换,而不是让操作系统去做抢占式调度,这样就大大节约了资源。
进程间通信方式
管道(Pipe)
管道是一种半双工的通信方式,允许一个进程写入数据到管道,同时另一个进程从管道中读取数据。在Unix/Linux系统中,使用pipe系统调用来创建管道。
命名管道(Named Pipe)
与普通管道不同,命名管道允许不相关的进程通过给定的名称来进行通信。在Unix/Linux系统中,命名管道通过文件系统中的特殊文件实现
消息队列
进程可以通过消息队列向其他进程发送消息。消息队列是一种通过消息缓冲区进行通信的机制,进程可以将消息发送到队列,而其他进程则可以从队列中读取消息。在Unix/Linux系统中,使用msgget、msgsnd和msgrcv等系统调用来操作消息队列。
共享内存
多个进程可以将同一块内存映射到它们的地址空间中,从而实现共享内存。这样,一个进程对共享内存的写操作会影响到其他进程对相同内存区域的读操作。在Unix/Linux系统中,使用shmget、shmat等系统调用来操作共享内存。
信号量(Semaphores)
信号量是一种用于进程同步和互斥的通信方式。通过信号量,进程可以对资源进行加锁或解锁。在Unix/Linux系统中,可以使用semget、semop等系统调用来操作信号量。
套接字(Socket)
套接字是一种网络编程中常见的通信方式,但也可以用于本地进程间通信。通过套接字,进程可以在不同主机或同一主机上的不同进程之间进行通信。
文件映射(Memory-mapped Files)
进程可以通过将文件映射到它们的地址空间来共享数据。这种方式通过mmap系统调用在Unix/Linux系统中实现。
全双工和半双工
单工、半双工和全双工是通信中两种基本的传输模式,描述了数据在通信双方之间的流动方式
单工
单工通信只允许数据在一个方向上传输,即只能从一个点向另一个点发送数据。通信的一方只能发送,而另一方只能接收。 一般的广播电台是单工通信的例子,听众只能接收广播,无法向电台发送信息。
半双工
在半双工通信中,数据可以在通信双方之间双向传输,但不能同时进行。换句话说,通信的两端可以交替地发送和接收数据,但不能同时进行这两个操作。类比于对讲机,一方说话时另一方只能听,不能同时说话。
全双工
在全双工通信中,数据可以在通信双方之间双向同时传输。每一方都可以独立地发送和接收数据,而且这两个操作可以同时进行。类比于电话,两个人可以同时说话而互不干扰。
用户态、内核态
内核是操作系统的核心,负责管理和控制计算机硬件资源,提供各种系统服务。假设所有的应用程序都能够直接访问硬件资源,那么可能会导致系统崩溃、数据丢失等问题。
因此,操作系统将应用程序和操作系统内核进行了隔离,确保内核能够独立地控制硬件资源。操作系统内核是运行在特权模式下的,可以访问所有硬件资源和底层系统资源,而应用程序是运行在非特权模式下的,只能访问被授权的资源,不能直接操作硬件资源和底层系统资源。
也就是说,为了保护系统的稳定性和安全性,操作系统将运行在不同的特权模式下,即内核态和用户态。
内核态
内核态是操作系统运行在特权模式下的状态,此时操作系统具有最高的权限,可以访问所有的硬件资源和底层系统资源,如处理器、内存、I/O等。
- 在内核态下,操作系统可以执行所有的指令,而且不受访问权限的限制。内核态下的程序通常是操作系统内核代码或者设备驱动程序等,它们可以直接访问硬件资源和系统资源,以实现系统的功能和服务。
- 在用户态下,应用程序只能执行一些受限的指令集,不能直接执行特权指令。当应用程序需要访问系统资源时,需要通过系统调用的方式切换到内核态,请求操作系统提供服务。内核态和用户态之间的切换是通过操作系统内核提供的中断或异常机制实现的。
- 有时我们的应用进程可能也需要进行一些系统调用,比如读写文件这种IO操作,这时候就会触发用户态向内核态的切换。
用户态
用户态是指应用程序运行在非特权模式下的状态,此时应用程序只能访问被授权的资源,不能直接操作硬件资源和底层系统资源。
• 此时,CPU会暂停当前进程的执行,保存当前进程的状态(包括程序计数器、寄存器、栈指针等)并切换到内核态执行相应的操作,操作完成后再将控制权切换回用户态,恢复进程的执行。
缓存
多级缓存
发展
- 随着CPU技术的发展,CPU的执行速度越来越快。而由于内存的技术并没有太大的变化,所以从内存中读取和写入数据的过程和CPU的执行速度比起来差距就会越来越大,这就导致CPU每次操作内存都要耗费很多等待时间。
- 人们想出来了一个好的办法,就是在CPU和内存之间增加高速缓存。缓存的概念大家都知道,就是保存一份数据拷贝。他的特点是速度快,内存小,并且昂贵。
- 程序的执行过程就变成了:
当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。 - 随着CPU能力的不断提升,一层缓存就慢慢的无法满足要求了,就逐渐的衍生出多级缓存。
- 按照数据读取顺序和与CPU结合的紧密程度,CPU缓存可以分为一级缓存(L1),二级缓存(L2),部分高端CPU还具有三级缓存(L3),每一级缓存中所储存的全部数据都是下一级缓存的一部分。
- 这三种缓存的技术难度和制造成本是相对递减的,所以其容量也是相对递增的。
- 当CPU要读取一个数据时,首先从一级缓存中查找,如果没有找到再从二级缓存中查找,如果还是没有就从三级缓存或内存中查找。
- 单核CPU只含有一套L1,L2,L3缓存;如果CPU含有多个核心,即多核CPU,则每个核心都含有一套L1(甚至和L2)缓存,而共享L3(或者和L2)缓存。
说明
•
MESI缓存一致性协议
缘由
- 每个CPU会有L1、L2甚至L3缓存,在多核计算机中会有多个CPU,那么就会存在多套缓存,那么这多套缓存之间的数据就可能出现不一致的现象
- 为了解决这个问题,有了内存模型。内存模型定义了共享内存系统中多线程程序读写操作行为的规范。通过这些规则来规范对内存的读写操作,从而保证指令执行的正确性。
- 通过在总线加LOCK#锁的方式
解决
• 由于在锁住总线期间,其他CPU无法访问内存,会导致效率低下,因此缓存一致性协议更适合
- 通过缓存一致性协议(Cache Coherence Protocol)
• 最出名的就是Intel 的MESI协议
• 当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
• 每个缓存可能有有4个状态
• M(Modified):这行数据有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。
• E(Exclusive):这行数据有效,数据和内存中的数据一致,数据只存在于本Cache中。
• S(Shared):这行数据有效,数据和内存中的数据一致,数据存在于很多Cache中。
• I(Invalid):这行数据无效。
• 工作流程
• 当一个处理器需要读取一个缓存行时,它会首先检查该缓存行的状态。
• 如果状态是Exclusive或Shared,则直接从缓存中读取数据。因为这两种状态中数据都是有效的。
• 如果状态是Modified,则表明数据被修改了,则需要先将修改的数据写回主内存,然后再从主内存中读取数据。
• 如果状态是Invalid,则需要从主内存中加载数据到缓存,并设置状态为Exclusive或Shared。
• 当一个处理器修改了一个缓存行时,它会将状态设置为Modified,并通知其他处理器或核心该缓存行的状态已被修改,从而导致其他处理器中相应缓存行的状态变为Invalid。
• 通过总线事物或总线嗅探等机制通知
CPU与GPU区别
CPU
CPU是计算机的大脑,专门用于执行各种通用任务,如操作系统管理、数据处理、多任务处理等。它的架构设计旨在适应多种任务,具有较少的核心,但每个核心非常强大且灵活
GPU
GPU最初是为图形渲染和图像处理而设计的,因此其架构包含大量的小核心,适合并行处理。这些核心可以同时处理多个相似的任务,使其在某些计算任务中表现得比CPU更高效。
GPU拥有大量的核心,可以同时处理多个任务。这使得GPU在同时进行大量相似计算时非常出色。
- 我们熟知的挖矿和大模型训练,都符合GPU适合的那种重复性计算工作
CPU核心数少、GPU核心数多;CPU适合做各种复杂任务,GPU适合做重复性的计算任务。
持续写入的日志文件清理
不可直接删除,只能写入覆盖进行清空
命令
> file_name
cat /dev/null > file_name
echo "">file_name