【多线程】计算机工作原理、操作系统(内含进程、PCB属性、进程调度、内存分配、进程间的通信) —— 简单介绍
目录
1. 计算机工作原理
1.1 冯诺依曼体系(Von Neumann Architecture)
1.2 CPU指令(Instruction)
1.3 CPU寄存器(Register)
2. 操作系统 (Operating System)
2.1 进程/任务(Process/Task)
2.2 进程控制块(PCB Process Control Block)
2.3 PCB里面特征/属性(主要的)
2.4 进程的调度 —— CPU 分配
【小结】
2.5 虚拟地址空间
2.6 进程间通信(Inter Process Communication)
1. 计算机工作原理
1.1 冯诺依曼体系(Von Neumann Architecture)
现代的计算机, 大多遵守 冯诺依曼体系结构
CPU: 中央处理器,进行算术运算和逻辑判断,一个计算机中最核心的部分。
衡量CPU的重要指标(当然远远不止这两个啦):
- 核心数:可以简单理解为一个核就是一个人干活,多核就是多个人干活。核心可以理解为一个能完成完整计算功能的整体,是由很多的计算单元构成的,提高集成程度或者提高核心数,就可以提高cpu速度。
- 频率:CPU的运算速度,可以简单理解为一秒钟执行的指令个数。2.40GHz,一秒cpu可以执行24亿条指令。
存储器:分为外存和内存,用于存储数据(使用二进制方式存储)
内存(咱们平时说的内存、运行内存):存储空间小,访问速度快,成本高,掉电后数据丢失(易失性)。
外存(硬盘,软盘,光盘,U盘):存储空间大,访问速度慢,成本低,掉电后数据保留(持久化存储)。
输入设备:用户给计算机发号施令的设备。如鼠标、键盘等。
输出设备:计算机个用户汇报结果的设备。如显示屏等。
网卡,触摸屏既是输入设备也是输出设备。
存储空间大小:硬盘 > 内存 >> CPU
数据访问速度:CPU >> 内存 > 硬盘
1.2 CPU指令(Instruction)
指令是cpu上能够执行任务的最小单元,相当于你告诉计算机完成该任务每一步要做什么。
- cpu是一个铁憨憨,你给它发布一个任务,需要你把任务拆分成非常非常细致的每个部分,才能让它执行。
- 一个cpu设计的时候,就会提供一些可以进行的操作(支持哪些指令)。比如,加法指令,读取内存指令,写入内存指令,条件判定/跳转指令,函数调用指令,堆栈操作指令.….这些最小单元,都是由二进制的方式来表示的机器语言。不同架构的cpu支持的指令/机器语言是不相同的。
指令表简化版本:
指令(instruction) | 功能说明 | 4位 opcode | 操作的地址或者寄存器 |
LOAD_A | 从 RAM 的指定地址,将数据加载到 A 寄存器 | 0010 | 4 位 RAM 地址 |
LOAD_B | 从 RAM 的指定地址,将数据加载到 B 寄存器 | 0001 | 4 位 RAM 地址 |
STORE_A | 将数据从 A 寄存器写入RAM 的指定地址 | 0100 | 4 位 RAM 地址 |
ADD | 计算两个指定寄存器的数据的和,并将结果放入第二个寄存器 | 1000 | 2 位的寄存器 ID 2 位的寄存器 ID |
1.3 CPU寄存器(Register)
- 寄存器是CPU内置的存储数据的模块。
- CPU中有些寄存器有些寄存器,是有特定含义特定作用的。有些寄存器没有特定含义,就只是用来保存运算的中间结果的。
1、保存当前执行到哪个指令(程序计数器)
是一个2字节/4字节/8字节的整数,这个整数存的是一个内存地址。内存地址是程序下一条要执行的指令所在的位置。
2、维护栈相关的寄存器
3、其他的通用寄存器,往往是用来保存计算的中间结果的
2. 操作系统 (Operating System)
操作系统是一组做计算机资源管理的软件的统称。目前常见的操作系统有:Windows系列、Unix系列、Linux系列、OSX系列、Android系列、iOS系列、鸿蒙等。
- 操作系统 = 内核 + 配套的应用程序
- 操作系统"内核”是操作系统里面最核心的功能模块。详细展开又分为内核态和用户态。即一个程序在运行过程中,可能是在用户态工作,也可能是在内核态工作。
- 配套的应用程序:例如windws10中 “此电脑”-》文件资源管理器,任务栏等程序都是系统自带的。
操作系统(内核)的主要功能:
- 对下,要管理所有硬件设备:硬件厂商会提供相应的驱动程序,操作系统通过驱动程序间接操作硬件设备。(CPU、内存、硬盘、主板、风扇、电源等这些设备怎么好好工作,怎么相互协调配合都是操作系统统筹规划的)
- 对上,要给软件提供稳定的运行环境:进程的隔离性,一个计算机上可以同时运行多个程序这些程序各自独立运行,不会互相干扰。例如,在写代码正在听网易云音乐,当代码出bug了,网易云音乐不会停止。
- 操作系统是软件、硬件以及用户之间交互的媒介。
- 操作系统会给应用程序提供API(接口),让应用程序调用,完成不同的功能。
- java程序猿一般不会直接接触到操作系统的api。不同的操作系统,提供的api是不同的。此处这些操作系统api都被jvm给封装好了。(非常类似于jdbc )。这里很好的体现了java代码跨平台特性。
操作系统内核中涉及到关键性概念有很多,进程只是其中一个。此处重点只讨论进程。(和咱们写代码密切相关)
此处谈到的“操作系统"主要还是围绕 Linux 进行讨论。
2.1 进程/任务(Process/Task)
一个跑起来的程序(并非是一个软件)就是一个进程,没跑起来就不算进程(没跑起来的叫做程序)。
应用程序有两种状态:
- 未运行:此时是一个躺在硬盘上的exe文件,可执行文件。
- 运行中:exe文件会被加载到内存中,并且cpu执行了里面的指令。进程
任务资源管理器中可以查看,当前计算机上正在执行的应用程序(进程):
每个进程要想执行,都需要消耗一定的系统资源(硬件资源)。
在操作系统内部,进程是操作系统进行资源分配的基本单位。
一个计算机的总体资源是有限的,每个进程都需要消耗一些资源,所以电脑的卡不卡和你的硬盘上的东西多不多没关系,与进程的多少以及进程消耗资源的多少有关系。
2.2 进程控制块(PCB Process Control Block)
进程是一个重要的“软件资源”(被分配的),是由操作系统内核负责管理的。
进程是如何被管理的,通过 描述 + 组织 的方式。
- 描述:使用类/结构体,把被管理的一个对象,各个属性都表示出来。通过C语言的结构体或者C++、Java语言中的类,把一个进程的各种属性描述出来。
- 组织:通过某些数据结构(比如链表),把上面的结构体一个一个串起来,并进一步的进行各种增删查改
- 系统中专门有一个结构体(操作系统内核是使用C/C++写的)描述进程的属性。这个结构体统称为"进程控制块" —— PCB。PCB是内存中的结构。
- 使用PCB(进程控制块)描述进程的属性,一个进程就可以使用一个或者多个PCB来表示。这里认为一个进程使用一个PCB
- 系统中会使用类似于双向链表这样的数据结构来组织多个PCB。
其中结构体就是C语言中的结构体,为什么不用java的类,因为操作系统基本上都是C/C++来写的;用来描述进程的这个结构体,起了一个特殊的名字叫做PCB(进程控制块),PCB是一个结构体,不是硬件“PCB板”
- 创建新的进程,本质上就是创建PCB这样的结构体对象,把它插入到链表中。
- 销毁进程,本质上就是把链表上的PCB节点删除并释放。
- 任务管理器查看(展示)进程列表,本质上就是遍历这个PCB链表的每个节点。
要想更明确认识进程详细特性,还需要讨论一下pcb里面的属性。
2.3 PCB里面特征/属性(主要的)
pcb是一个非常庞大的结构体,包含很多属性(这里只介绍主要的属性)。在Linux系统中,pcb 叫做 task_struct
1、pid:进程的身份标识符(唯一的数字)
运行的一个软件可能有多个程序,就有多个进程对应有多个pid。同一时刻,一台机器上的每个进程的pid都是唯一的。
2、内存指针(一组属性):进程持有的内存资源
- 每个进程在运行的时候,都会分配一定的内存空间。
- 通过这些指针(一组内存指针),可以知道进程的指令和数据在哪里(操作系统运行一个可执行程序时,会把exe文件中的指令、数据以及程序运行过程中产生的临时数据都加载到不同位置的内存中,这些指针保存着这些地址)
- 最典型的,进程的内存空间,需要有专门的区域存储要执行的指令,以及指令依赖的数据,同时还需要存储一些运行时产生的临时数据。
3、文件描述符表:进程持有的硬盘资源
- 类似于顺序表这样的数据结构,有很多元素。
- 一个进程会涉及到硬盘操作,例如从硬盘读数据/往硬盘上写数据,并不是直接操作硬盘本身。是通过操作文件的方式,从而间接的操作硬盘。文件是存储在硬盘上的。
- 当前进程关联了哪些文件,都能操作哪些文件,就是通过文件描述符表进行表示的
4、进程状态
5、进程优先级
6、进程上下文
7、进程的记账信息
以上4、5、6、7PCB中的属性共同决定了进程持有的CPU资源,通过进程的调度实现,我们单独下文介绍。
2.4 进程的调度 —— CPU 分配
CPU资源如何分配给进程的:操作系统里面有一个重要的模块调度器,负责有限的CPU来调度执行这么多的进程。
cpu是如何调用进程的:进程有上百个,CPU有几个呢?1个??其实不是,现在的CPU都是多核CPU
4核 8线程,一个CPU分成4个核心,每个核心又能一个顶俩(超线程技术),这种情况就视为是8核就行了。
“狼多肉少”,这些进程是希望能够“同时运行”—— 分时复用
- 并发执行:微观上同一时刻,一个核心上只能运行一个进程,但它能通过对进程快速轮转调度的方式(分时复用,cpu主频一秒执行24亿条指令),执行多个进程,宏观上是“同时执行”,微观上有先有后。
- 并行执行:微观上同一时刻,两个核心上的进程是同时执行的。
- 上述并发和并行,在应用程序这一层感知不到,是系统内部完成调度的。
- 作为普通的程序猿平时也不会具体区分并发还是并行。从编程角度来说,底层是并发还是并行,对代码没啥影响。一般把并发和并行统称为并发。并发编程
下面这些属性,就是用于支持操作系统实现进程调度的效果。
1)进程的状态
- 就绪状态:随叫随到,进程时刻准备着被调度到CPU上执行。
- 运行状态:正在cpu上执行。
- 阻塞状态:短时间内无法调度到CPU上执行。(某个进程某个执行条件不具备,例如写代码时使用Scanner等待用户输入,如果不去输入,程序会一直卡在这)
很多操作系统,不会明确区分就绪和运行状态,认为是一种状态(就绪状态)。
进程状态还有很多种其他的状态(Linux中,暂停状态,僵尸状态,退出状态等),这些状态不做进一步讨论。
2)进程的优先级
进程调度时,有的进程先执行,有的进程后执行,进程也是有优先级的。操作系统进行调度并不是一视同仁的。
3)进程的上下文
- 操作系统在进行进程切换的时候,需要把进程执行的"中间状态"记录下来保存好,下次这个进程再轮转到CPU上运行的时候,就可以恢复上次的状态继续往下执行。
- 进程的上下文,是CPU中的各个寄存器的值。寄存器是CPU内置的存储数据的模块,保存的是程序运行过程中的中间结果/状态。
- 保存上下文,就是把这些CPU寄存器的值,记录保存到内存中(该PCB指定的内存);恢复上下文,就是把内存中的寄存器值恢复回去
- 类似于:"存档,读档",保存上下文本质上就是存档,恢复上下文本质上就是读档。
4)进程的记账信息
- 通过优先级机制,对不同的进程分配了不同权重的资源。有可能会出现极端的情况,所有的资源都给某个进程,其他进程一点都没分配着。
- 操作系统会统计每个进程在cpu上占用时间和执行指令数目的信息,根据记账信息来决定下一阶段如何调度。
【小结】
2.5 虚拟地址空间
虚拟地址空间,程序运行时所分配的内存地址,并非是真实的物理内存的地址,而是经过了一层抽象,虚拟出来的地址。
C语言学过的指针,指针指向的内存地址就是虚拟的内存地址,并非真实的物理内存地址。
物理内存地址:
- 想象一下你的宿舍楼,有个大走廊,走廊上有很多房间,每个房间就是一个宿舍,
- 每个房间还有一个编号,通过编号就可以定位到房间的位置,房间编号就是"地址"。
- 内存(物理上是个内存条)可以存很多数据,内存就可以想象成是一个大走廊,走廊非常长,有很多房间每个房间大小1 Byte,每个房间还有个编号,从0开始依次累加这个内存编号,就是“地址",这个地址也就认为"物理地址"。
- 内存(RAM)有个特性:随机访问。访问内存上任意地址的数据,速度都极快,时间上都差不多。
- 正是这个特点,造就了数组取下标操作时间复杂度为 O(1)
为什么程序不能直接获得物理内存地址?
例如:如果代码不小心出bug 了,就可能导致访问的内存就越界了。
进程1的指针变量,出bug变成了0x8000,就会导致进程2崩溃;明明是进程1的bug,却导致了进程2崩溃了。
很可能就会你写个程序一运行,你的chrome崩溃了或者你的qq崩溃了,画图板崩溃了......
针对进程使用的内存空间,为了进行“隔离"引入了虚拟地址空间,代码里不再直接使用真实的物理地址了,而是使用虚拟的地址,由操作系统和专门的硬件设备负责进行虚拟地址到物理地址的转换。(有一个类似于hash表这样的映射结构,称为页表)
- 一旦进程1虚拟地址访问越界了,指针地址成了0x4000在转换为物理地址的时候,操作系统内核发现当前这里的地址,超出进程1的访问范围了,
- 此时就直接会向进程1反馈一个错误(具体来说是发送一个SIGN SEGEMENT FAULT信号,引起进程1的崩溃),谁出了 bug谁崩溃,其他进程不受影响了。
- 多了地址的转换步骤,效率肯定会受到折扣,但是我们认为这样的代价是值得的,两个进程之间不再会互相影响彼此了。
- MMU硬件设备就是让转换能够更快一点,很多时候地址转换是集成在CPU 里的。
- 虚拟地址空间,主要为了避免进程之间相互产生影响
通过上述方式,把进程之间给隔离开了。
现代的应用,要完成一个复杂的业务需求,往往无法通过一个进程独立完成,总是需要进程和进程进行配合地达到应用的目的,如此,进程之间就需要有进行“信息交换“的需求。进程间通信的需求就应运而生。
2.6 进程间通信(Inter Process Communication)
有些时候进程之间,确实需要进行数据的交互(相互配合),在隔离性的基础上,开个口子。
进程间通信实现方式很多,但核心思路是一致的,需要一个“公共空间”使多个进程都能访问到,基于这个公共空间来进行交互数据即可。
这里的通信方式/公共空间有很多种具体的体现形式。目前,主流操作系统提供的进程通信机制有如下:
1. 管道 2. 共享内存 3. 文件 4. 网络 5. 信号量 6. 信号
其中,网络是一种相对特殊的 IPC 机制,它除了支持同主机两个进程间通信,还支持同一网络内部非同一主机上的进程间进行通信。
后面会主要讲基于文件,基于网络(socket) 进行通信机制
好啦Y(^o^)Y,本节内容到此就结束了。下一篇内容一定会火速更新!!!
后续还会持续更新多线程方面的内容,还请大家多多关注本博主,第一时间获取新鲜的知识。
如果觉得文章不错,别忘了一键三连哟!