文章目录
- 一. 寄存器和存储器
- 二. 操作系统
- 二. 进程
- PCB
- 1. pid
- 2. 内存指针
- 3. 文件描述符表
- 4. 属性
- 1) 状态
- 2) 优先级
- 3) 上下文
- 4) 记账信息
一. 寄存器和存储器
- 存储器是内存和硬盘的通称
内存, 存储空间比硬盘小, 速度比硬盘快, 价格比硬盘高, 掉电后数据流失 - 寄存器是CPU上的一个模块
寄存器, 存储空间比内存还小, 速度比内存快, 价格比内存更贵, 掉电后数据也流失
内存和硬盘之间的速度, 差3-4个数量级
寄存器和内存的速度, 也查了3-4个数量级
由于寄存器和内存的速度差异很大, 现代的CPU往往还提供了缓存
在我的CPU上, 有三个缓存, 数字越小, 存储空间也越小, 速度就越快
有些数据, 可能CPU要频繁使用, 每次使用的时候从内存读取, 速度就比较慢, 就可以把这样高频使用的数据放在缓存里(数据使用频率特别高, 放在L1缓存; 数据使用频率没那么高, 考虑放在L2L3里)
当读取指令的时候, 先看看缓存中有没有, 没有就读取内存, 有的话就直接读缓存
二. 操作系统
软件, 是一组指令的集合, 操作系统, 就是软件
操作系统, 要能够管理好各种硬件资源, 让他们很好的相互配合; 也需要管理好各种软件资源, 给每个软件都提供稳定的运行环境
操作系统 = 内核 + 配套的应用程序
内核是操作系统最核心的部分, “管理” 都是在内核里完成的
配套的应用程序例如画图板, 记事本, qq音乐等, 这些应用程序往往要靠内核提供一些功能作为支撑
我们所写的代码, 就是一个"应用程序"
操作系统内核, 给应用程序提供一系列api, 例如有的api是操作显示器, 有的api是用来想用鼠标键盘的
操作系统内核通过驱动程序, 来操作硬件
例如:
System.out,println(“hello world”);
- 上述代码, 是要操作显示器, 显示器是一个硬件设备, 不是程序直接操作的, 而是你的程序告诉操作系统"我要操作显示器", 操作系统给你完成这个任务
- 这里是调用java内置的标准库函数, 这个函数的内部本质上, 就是调用操作系统内核提供的api, 告诉操作系统我要控制显示器
这一过程叫做系统调用(system call)
- 此时就进入操作系统内核了, 内核通过显示器的驱动程序(显卡驱动), 在显示器上绘制出对应的内容(字符串)
应用程序运行的地方----用户态
操作系统内核运行的地方----内核态
二. 进程
一个程序, 运行起来/跑起来, 在操作系统中, 就会出现对应的进程
进程(process), 就是一个跑起来的应用程序
这里就把当前运行的所有进程都列了出来, 除了自己运行的程序是进程, 还有一些系统自带的/安装的某些程序, 也是进程, 这些程序对你的系统能够稳定的运行有很重要的意义
还可以查看当前进程都占用了哪些系统资源
要想让一个程序运行, 就必须得给这个进程分配系统资源, 包括不限于: cpu, 内存, 硬盘, 网络带宽, 显卡…
所以, 进程可以视为是操作系统进行资源分配的基本单位
那么操作系统上包含了很多进程, 此时就需要考虑"管理"了
对于管理, 操作系统采用: 先描述, 再组织
先描述: 通过一些结构体/类, 把一个进程的核心信息抽象提取出来吗并进行表示(主流的操作系统都是C语言实现的, 所以用结构体, Windows也包含了一部分C++, C为主)
在操作系统中, 通常使用**PCB(进程控制块)**这样的结构体来描述进程
再组织: 通过一定的数据结构, 把多个这样的结构体/类的对象串起来, 方便进一步进行各种增删改查
操作系统通常会使用链表(其实是多个链表综合在一起构成的)这样的结构, 来把多个PCB串起来
例如:
- 任务管理器中查看进程列表
就是在遍历链表中的每个节点, 并且获取出对应的信息 - 创建新的进程(双击某个程序运行)
就是应用程序感知鼠标双击操作, 调用操作系统提供的api, 在内核里创建进程, 新的进程会创建出一个新的PCB, 并且添加到上述链表中 - 销毁某个进程(某个程序退出)
就是要把链表对应的PCB结点删除
PCB
PCB这个结构体中包含了很多信息, 此处我们只讨论几个重要的
1. pid
pid, 进程的id / 标识符
2. 内存指针
进程运行时, 需要消耗一定的硬件资源, 内存就是一个关键的资源
一个程序, 在运行的时候, 就会被从硬盘(xxx.exe)加载到内存中, 同时也加载了这个程序的 指令 / 依赖的数据
PCB中的内存指针, 就存放着该进程要运行的指令和依赖的数据的地址
这一组指针就告诉操作系统, 该进程要运行的指针在内存的哪个房间, 也告诉系统, 该进程依赖的数据, 又在哪个房间里
3. 文件描述符表
一个进程运行时, 会操作一些文件
文件描述符表就通过一个顺序表这样的顺序结构, 记录下当前的进程, 都打开了哪些文件
4. 属性
我们知道, 进程的数量是远远多于CPU的数量的, 假设逻辑内核有16个
并行执行:
一个核心, 同一时刻, 只能运行一个进程
那么有16个核心, 同一个时刻, 能运行16个进程
并发执行:
但是, 一个核心, 不同时刻, 可以运行多个进程
这一刻执行进程1, 下一刻, 执行进程2, 再下一刻, 执行进程3
CPU将总的执行时间, 切分成若干个小片段, 每个片段执行一个进程, 每个片段称为"时间片"
由于时间片比较短, CPU切换进程的速度极快, 人类感知不到, 那么站在人的角度, 就是这若干个进程在"同时执行", 这种执行方式叫分时复用
并行执行, 微观上, 是同时执行
并发执行, 微观上, 是串行执行, 宏观上是同时执行
往往, 我们把并行执行和并发执行统称为并发执行
把编写解决并发问题的程序, 称为"并发编程"
1) 状态
(简单介绍, 线程篇细讲)
如果一个进程随时都可以执行, 则处于**“就绪状态”**
如果一个进程由于某种原因不能执行, 则处于**“阻塞状态”**
(往往进程在等待IO时会处于阻塞状态, 如scanner.next())
2) 优先级
每个进程都有优先级, 优先级高的先上cpu运行
3) 上下文
进程在CPU执行过程中, 会产生很多的"中间结果", 在进程进程切换出CPU之前, 就需要把这些中间结果(CPU各种寄存器中的值)保存到PCB的上下文里(寄存器->内存, 这个过程称为存档)
下次进程回到CPU上执行的时候, 就需要先把之前的存档恢复回来(内存->寄存器, 这个过程叫读档)
4) 记账信息
延续刚才的优先级, 哪个进程分配的资源多和少, 需要通过表格来统计, 毕淼有哪些进程一直没有吃到CPU的资源
上述的属性, 支持着进程调度