操作系统介绍
操作系统介绍
系统软件!!
https://www.bilibili.com/video/BV1js411b7vg?from=search&seid=2313098069245169080 B 站清华大学公开课
https://github.com/chyyuu/os_course_info 资料
ucgore 操作系统练习 github.com/chyyuu/mooc_os_lab
计算机工作原理: 取内存中地址的汇编命令 控制器执行
定义
一直运行在计算机上的程序(通常称为内核), 管理 CPU 轮转/内存空间/IO 设备等
主要操作系统
unix BSD 系统 ,linux 家族 , windows 家族...
历史发展: 起步 -》多道程序设计(异步) -》分时操作系统(时间片轮转 底层使用时钟)-》多核多处理 -》 分布式系统(网路的提高,PC 性能提高,出现数据中心的概念)
操作系统结构
分层的设计思路; 同时操作系统对资源进行了抽象:
- CPU 抽象成进程
- 磁盘 抽象成文件
- 内存 抽象成地址空间
层次结构: 硬件之上,应用程序之下
启动过程 & 中断/异常/系统调用
- 打开电源后, 计算机需要运行一个初始化程序(引导程序), 通常位于 ROM 中或者 EEPROM, 称为计算机硬件中的固件, 初始化系统中的所有部分, 包括 CPU 寄存器/设备控制器/内存内容; (CPU / 内存 / IO 通过总线连接, 接电之后 执行内存中特定地址的 BIOS 程序(自检显卡,外设正常和执行 BIOS(加载硬盘中 bootloader(第一个启动扇区))) 加载内核)
- 引导程序必须知道如何把系统内核装入内存中, 然后执行第一个进程如 init,
等待事件的发生
;
事件的发生通常通过硬件或软件
中断(interrupt)
表示, 硬件可以随时通过系统总线向 CPU 发出信号, 触发中断; 软件通过执行系统调用(system call)
(应用程序请求操作系统提供服务) 也能触发中断
内核
课程关注的是 kernel (内核) 不是 shell
内核的特征: 并发;共享资源(多个应用访问同一个资源);虚拟(多道程序设计技术,让用户觉得自己独占一台计算机); 异步(程序的执行并不是一贯到底,而是走走停停,向前推进的速度不可预知)
补充: 并发和并行的区别
并发(在一段时间内可以有多个程序运行; 而并行是在一个时间点上同时执行多个程序:要求计算机有多个 CPU)
并发是指一个处理器同时处理多个任务。 指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。
并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
当有多个线程在操作时,如果系统只有一个 CPU,则它根本不可能真正同时进行一个以上的线程,它只能把 CPU 运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状态.这种方式我们称之为并发(Concurrent)。
当系统有一个以上 CPU 时,则线程的操作有可能非并发.当一个 CPU 执行一个线程时,另一个 CPU 可以执行另一个线程,两个线程互不抢占 CPU 资源,可以同时进行,这种方式我们称之为并行(Parallel)。
来个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
内核的设计思路:
- 内核: 函数调用,耦合性很高, 越来越大
- 微内核: 尽可能的小,可移植的方式存在,只保留基本的功能比如消息传递,进程控制,中断处理,(之前的设计都是耦合大,通过函数调用的方式,未模块化)把 内存管理,网络管理,IO ,设备驱动 放在外围,以服务
(运行在用户态)
的形式存在,服务与服务之间通讯通过内核的消息传递机制,松耦合,代价是性能(数据 -》内核 -》服务) 当然也能获得更好的可靠性 - 外核: 内核分成两个部分,内核面向硬件,外核是应用自己定制的 lib core -》内核-》硬件
VMM(虚拟机监视器)在操作系统和硬件之间多了一层
现代操作系统设计方法是: 用面向对象编程技术来生成模块化的内核,有一个核心内核,动态加载其他内核模块,每个内核模块都有被定义和保护的接口,任意模块都能调用任何其他模块,采用方法调用,比消息传递通信给为高效
JVM

双重模式操作
由于操作系统和用户共享了计算机的硬件和软件, 必须保证用户程序的一个 BUG 仅影响自己, 不能影响其他程序和操作系统, 为了确保操作系统的正常执行, 必须区分用户代码和操作系统代码的执行, 许多操作系统采取的方法是提供硬件支持以允许区分各种执行模式
- 用户模式
- 特权模式
在计算机硬件中添加一个模式位(mode bit)
表示当前的模式: 特权 0 用户 1 , 就可以区分是 OS 执行的任务还是用户执行的任务
用户模式下试图执行特权指令, 那么硬件不执行指令, 认为是非法指令, 抛出异常通知操作系统, 硬件从用户模式切换成特权模式

系统调用
API :
- win32 API 用于 windows
- POSIX API 用于 linux unix macos ,
- javaapi 用于 jvm (jvm 在调用 win32 或者 posix)
分为五大类: 进程控制 文件管理 设备控制 信息维护 通信
- 操作系统用户态: cpu 不能执行一些特殊的指令 特权指令 io 处理
- 操作系统内核态: cpu 可以执行任何指令
用户程序调用操作系统系统调用的时候,将从用户态转为内核态,控制权从应用程序交给操作系统,完成堆栈的转换【内核堆栈和应用程序堆栈】(如果是函数调用(应用程序直接调用内核的函数)的化就只有一个堆栈)
CPU 处理中断
暂停正在做的事情并立即转到固定的位置去执行, 该位置通常是中断服务程序开始位置的地址, 执行完中断处理程序后 CPU 中心执行被中断的计算
- 硬件操作:设置中断标记(CPU 初始化)/ 中断事件 ID CPU 会根据中断表寻找对应 ID 的中断处理程序执行
- 软件操作: 保存当前处理状态;中断服务程序处理;清除中断标记;恢复之前保存的处理状态

处理中断要快, 通常使用中断处理子程序的指针表, 指针表存储在低地址内存, 这些位置存放了各种设备中断处理子程序的
地址
中断事件总是由
中断
(外设)或者异常
(应用程序意想不到的行为)触发,
为了降低用户程序异常对其他程序和操作系统的影响,引入了双重模式操作(用户态和内核态)
将能引起危害的机器指令作为特权指令
(priviliged instruction),如果用户程序执行特权指令,那么硬件并不执行该指令,而是认为指令非法,以异常的形式统治到操作系统,如果想执行,则只能使用系统调用
为了确保操作系统能维持对 CPU 的控制,防止用户程序陷入死循环并且不将控制权返回给操作系统,使用定时器 在给定时间后中断计算机,控制权自动交给操作系统
I/O 结构
操作系统大部分代码都用来进行 IO 的管理; 一是因为设备变化的特性二是 IO 对系统的可靠性和性能很重要.
通常 OS 由一个 CPU 和多个设备控制器
组成, 他们通过总线连接, 每个设备控制器负责多个特定类型的设备;
// 设备控制器维护一定量的本地缓存存储器和一组特定用于的寄存器, 负责与其所控制的外部设备与本地缓冲存储器交换数据
// 操作系统给设备控制器提供设备驱动程序, 能够理解设备控制器,并提供设备与其余操作系统的统一接口
系统调用和函数调用的区别
操作系统有自己的堆栈, 执行时间上慢于函数调用
系统调用需要建立中断/异常/调用号和对应服务历程的影射关系
系统调用会验证 参数
内核态到用户态的转换
系统生成
OS 通常采用磁盘或者 CD-ROM 来发布, 目前系统生成的方式是所有的代码都是系统的组成部分, 针对不同的硬件情况 选择在代码执行时(而不是代码编译或连接时) 创建适当的表以描述系统
操作系统体系结构/内存分层
操作系统把内存抽象成 逻辑地址空间(使得编程看到的内存是连续的空间)保护独立地址空间(硬盘 内存条的地址)
内核占用的内存 程序 1 占用的内存 程序 2 内存 ... 不够了的话使用虚拟内存
内存里面存放什么?? 代码区 堆栈数据区 共享程序段 等
我们自己写的代码处理的是逻辑地址,编译(将基于符号的地址空间转换成逻辑地址空间),汇编,链接,都是逻辑地址 CPU 通过 ALU 查询 MMU 地址影射关系表找到真实地址 提取指令执行。 操作系统要做的是做好地址影射表
连续内存空间分配 ->算法
- 第一匹配 寻找到第一块空闲空间》需要的空间大小的时候,就分配给他 优点:简单,方便产生结尾的大空间块 缺点:随着时间推移,容易产生外碎片,不同程序之间的地址块越来越小
- 最优匹配 寻找最适合的一块空间 找到与需要的空间大小最接近的内存块
- 最差匹配 与最优相反
内存整理:
压缩式碎片整理: 把分离开的内存片段 移动到一起,就把碎片空间整理出来了,但是移动的时机?? 开销也比较大
交换式碎片整理: CPU 处理的的程序需要更多的内存时候,但是没有空闲的物理内存,这时候把停止的等待 IO,中断的程序所占用的内存放到硬盘上 腾出空间(当程序大的时候耗费的开销也大)
缺点:
- 分配给一个程序的物理内存是连续的
- 内存利用率比较低
- 有外碎片/内碎片的问题
非连续内存分配
优点:
- 更好的内存利用和管理
- 允许共享代码和数据(共享库等等)
- 支持动态加载和动态链接
缺点:
如何建立虚拟地址和物理地址的转换 ?
分段和分页 物理硬件支持
分段: 把逻辑地址空间分段,比如程序段,程序数据段,运行栈, 运行堆 分配到不同的物理地址上,就是内存块
这样的话 逻辑地址怎么映射到物理地址中?? 段地址(段号)+段内偏移 (段寄存器寻址+地址寄存器寻址)
段表: 存放段号和物理地址的影射关系,段号长度的限制 由操作系统设置
分页 也是建立计算关系 页码+ 偏移
页表: 操作系统建立
性能存在问题: 页表可能非常大/访问内存单元需要两次计算
解决: 缓存 cpu 中快表 TLB 缓存近期访问的
多级页表 节约空间
虚拟内存
基于分段或者分页硬件支持的技术 + 操作系统有效的管理 实现以更小的力度为单位采用自动的虚拟存储技术
早前技术:
覆盖技术: 分时间实现小内存运行大软件,不运行的代码,后调用的代码存放在硬盘,运行后的代码被后来的硬盘中取出的代码覆盖 对程序元要求较高,书写的代码需要分块调用(一个程序之内)
交换技术: 多道程序在跑的时候,暂时不运行的程序送到硬盘中;
操作系统把一个进程的整个地址空间保存到硬盘上,将硬盘某个进程的地址空间读入内存中(多个程序之间,操作系统完成)
虚拟内存技术: 程序局部性原理 + 分段分页机制 + 不连续内存管理
计算机利用局部性原理读入某段或某页数据 程序的其它内容在硬盘上; CPU 丢失数据指令了 发出异常,操作系统处理判断内存空间是否够用? 并读入新的数据 : CPU 判断不使用的数据村入硬盘,腾出新的内存在读入缺失的数据
替换算法:
- 最优页面置换算法(需要知道程序未来执行的步骤,不容易实现)
- 先进先出算法
- 最近最久未使用算法(开销比较大)
- 时钟页面置换算法 周期性页表中把页访问表示位置 0 然后程序访问了页就会硬件置页访问 1 ,这样就知道了那些页最近在使用
- 二次机会法 页表中还有个标志位是读写位标记
- 最不常用算法
belady 现象: 采用 fifo 算法,有时候出现分配的物理页面越多,缺页率反而提高了的异常现象
进程管理
单线程进程具有一个程序计数器
确定下一个执行的指令
多线程进程具有多个程序计数器,每一个指向下一个给定的线程要 i 执行的指令
操作系统负责下述与进程管理的活动:
1 创建和删除用户进程和系统进程
2 挂起和重启进程
3 提供进程同步机制
4 提供进程通信机制
5 提供死锁处理机制
系统由一组进程组成,操作系统进程执行系统代码而用户进程执行用户代码
进程描述 processes
进程包括执行中的程序代码,当前活动状态(程序计数器的值/寄存器内容,进程堆栈段,数据段,堆)
运行中的程序就是进程 代表程序的执行过程,消耗 CPU 网络 内存资源 IO 等资源
从历史来看 一开始计算机只能跑一个程序 后面 CPU 发展迅速 出现了多个程序也可能是一个程序的多个实例
定义: 一个具有一定独立功能的程序在一个数据集和上的一次动态执行过程
组成: 程序的代码;程序处理的数据;程序计数器中的值,指示下一条将运行的指令;一组通用的寄存器的当前值,堆栈;一组系统资源
进程与程序的联系
进程是操作系统处于执行状态程序的抽象
- 程序 = 文件 (静态的可执行文件)
- 进程 = 执行中的程序 = 程序 + 执行状态
同一个程序的多次执行过程对应为不同进程 如命令“ls”的多次执行对应多个进程
进程执行需要的资源
进程与程序的区别
。。。参见 PPT
进程是程序的执行,有核心态(调用操作系统某些操作的过程)和用户态
进程的特点
动态性: 可动态的创建,结束进程
并发性:进程可以被独立调度并占用处理器运行
独立性:不同进程的工作不相互影响 (进程执行的数据是不受影响的)
制约性:因访问共享数据/资源或者进程间同步而产生制约
进程控制块 PCB(Process control block) 描述进程的数据结构
PCB 描述了进程的基本情况以及运行变化过程 是进程存在的唯一标志
包括三大类信息:
- 进程标识信息: 进程标志/ 父进程/用户标识等
- 处理器状态信息保存区(寄存器信息): 保存进程的运行现场信息 (用户数据 地址等寄存器信息,控制和状态寄存器信息,栈指针(执行到什么地方))
- 进程控制信息:调度和状态信息,进程通信信息,存储管理信息,所用资源信息
进程的状态
生命周期:
创建: 系统初始化创建 init ; 用户请求创建新进程; 正在运行的进程执行了创建进程的系统调用
就绪:
运行: 内核选择一个就绪的进程,让他占用 CPU 执行
等待:也称阻塞,无法立即完成的操作(IO 处理/协同其他进程/需要的数据还没有返回等) 需要进程自己阻塞自己,只有自己知道什么时候需要阻塞 操作系统不知道
唤醒: 进程只能被别的进程或者操作系统唤醒 转换为就绪状态
结束:正常退出,错误退出(自愿的退出),致命错误(强制退出),被其他进程杀死(强制的)
多个就绪的进程都需要被执行 --操作系统使用时间片 将运行态转就绪态
进程挂起: 把一个进程从内存转到外存,进程没有占用内存空间,而是映射到磁盘上 ,达到充分利用内存资源
状态队列: 把同一种状态的进程放到同一个队列中
进程调度
就绪或者等待运行的进程保存在就绪队列中,该队列通常通过链表来实现(PCB1 <-> PCB2 <-> PCB3)
每个设备也有可能忙碌在不同的进程请求中,进程有设备请求,就把进程存在设备队列
上下文切换:
CPU 切换到另一个进程需要保存当前进程的状态(PCB 的进程控制块中)并恢复到另一个进程的状态,这一任务称为上下文切换 content switch
进程创建
进程在执行过程中,能通过创建进程系统调用创建多个新进程,创建进程称为父进程,而新进程称为子进程,每个新进程在创建其他进程,从而形成进程树.
进程需要一定的资源(CPU 内存 文件 IO)来完成任务,一个进程创建子进程的时候,子进程可能从操作系统那里直接获取资源 也可能只从父进程那里获取资源
进程创建时候,处理需要资源还需要初始化数据,这部分数据由父进程传递给子进程
fork 生成子进程 返回值是 0 的是子进程 >0 的是父进程 父子进程都从 fork()下一行代码执行
wait() 父进程等待子进程的结束
exit() 进程结束
exec() 当进程调用一种 exec 函数时,该进程完全由新程序代换
进程终止

进程通信(IPC)
一个进程和另一个进程交换信息,主要有两种,一台计算机运行的两个进程之间,另一种在由网络连接起来的不同的计算机上的进程之间。通信可以通过共享内存实现,也可以通过消息交换技术
消息传送
通信之前,打开连接,接收方进程通过接受连接来允许通信,组成客户机--服务器架构,通过写消息和读消息交互消息,消息传送系统使用系统调用来实现, 需要消耗内核资源
共享内存
我们知道进程之间应该向不打扰,不能获得其他进程的内存访问权限,共享内存需要多个进程都同意取消这一限制,通过读写公共区域来交换信息. 进程也负责不会同时向同一个地方写
管道、信号量、Socket 等
比较:
少数据量使用消息传递很有效果,不必避免冲突,
共享内存在同一个计算机之间的进程通信优势很大,能以内存的速度通信

客户端和服务器系统通信
之前介绍了利用共享内存和消息传递进行进程间通信, 这些技术也能用于客户端和服务器通信;
还有三种方式: socket 远程过程调用(rpc) java 的远程方法调用(RMI)
socket
对 TCP / UDP 封住的一层接口,由 IP 地址+端口组成
主机 A(192.168.1.100:1314) -------- web 主机(192.168.1.200:80)
java 提供了三种不同类型的 Socket 接口,面向连接的 TCP Socket 类 面向无连接(UDP)的 Datagramsocket 类 多点传送的 MulticastSocket(允许将数据发给多个接受者)socket 通信虽然常用且高效, 但是属于比较地接的分布式进程通信, socket 只允许在通信线程之间
交换无结构的字节流
, 所以有了更高级的方式: 远程过程调用(RPC)和远程方法调用(RMI)RPC
调用其他机器进程的方式有两种,一个是 Http 方式,另一种就是 RPC.
每个消息传递给远程系统上监听端口号的 RPC 服务器,每个都要包含要执行的函数名称和传递给函数的参数,该函数根据请求执行,任何记过通过消息返回给调用者
对实现分布式文件系统非常有用,RPC 采用二进制字节码传输,相对于 Http 更高效更安全RPC 要解决几个问题
- 客户机怎么知道服务机的端口问题 --> os 提供一个固定 RPC 端口上提供集合点服务程序
matchmarker
; 客户端发送带 RPC 名称的消息给 matchmarker, 以请求需要执行的 RPC 端口地址; 得到端口地址, 客户端在请求端口地址,知道服务结束; - 客户端与服务端系统数据表示差异问题 -> 定义数据格式: 外部数据表示 XDR 对于参数和返回值都先编组成 XDR
- 客户机怎么知道服务机的端口问题 --> os 提供一个固定 RPC 端口上提供集合点服务程序
信号量
用来通知进程某个特定事件的发生
- 信号由特定的事件发生所产生
- 产生的信号需要发送到进程
- 一旦发送,信号必须处理
进程同步
有的时候,比如同时都要写入一个文件时候,需要进程同步
临界区问题 是设计一个以便进程协作的协议,每个进程必须请求进入临界区
信号量
信号 S 是一个整型数据,只能通过两个原子操作改写数值 wait()和 signal()
这两个操作不可分割开执行
wait(S){
while(S <=0){
//啥也不做
}
S--
}
signal(S){
S++
}
每个进程需要使用资源的时候,需要对信号量执行 wait 操作(减少信号量的值),当进程释放资源的时候需要执行 signal 操作
信号量为 0 的时候,说明资源被占用,其他进程需要等到信号量大于 0
可以使用信号量解决各种同步问题
上述信号量,主要缺点是要求忙等待,一个进程进入了临界区,其他进程都在
while(S <=0){
//啥也不做
}
的循环里,称为自旋锁 但是这段时间的 CPU 完全可以让给其他进程
优化:
修改 wait 和 signal 定义,一个进程执行 wait 操作时候,发现信号量不为正,则必须等待,这时候不让他忙等而是进入阻塞
将这个进程放入到与信号量相关的等待队列,并将该线程切换到等待状态.控制转到 CPU 调度程序
等到占用信号量的进程执行了 signal()后,该线程通过 wakeup()切换到就绪态
wait 和 signal 的操作怎么原子化?? 执行的时候禁止 CPU 中断即可
死锁
两个或者多个的进程等待着一个事件,而该事件只能由这些等待进程之一产生
设置两个信号量 S Q
P0 P1
wait(S) wait(Q)
wait(Q) wait(S)
... ...
signal()
signal()
先执行 P0 的 waits 在执行 P1 的 waitQ ,这种情况下,程序死锁
信号量能在一定程度解决同步问题,但是复杂情况,或者顺序写错的话仍然会有问题出现,更高级的同步构造: 管程
必要条件
- 互斥: 至少有一个资源处于非共享模式,一次只能被一个进程使用
- 占有并等待: 一个进程必须占有至少一个资源,并等待另一个资源,而该资源被其他进程占有
- 非抢占: 资源不可被抢占(剥夺),只能在进程完成后自动释放
- 循环等待: 一组进程循环等待资源
死锁的处理办法
- 可使用协议以预防或者避免死锁,确保系统不会进入死锁状态
死锁预防是一组方法,通过限制如何申请资源的方法来预防死锁
最简单的模型: 每个进程说明可能需要的每种资源类型实例的最大需求,根据这个事先信息,构造算法 - 可允许系统进入死锁状态,然后检测他,并加以恢复
- 可忽视这个问题,认为死锁不可能发生在系统中
死锁预防
预防占有并等待: -- 效率、资源利用率低
- 保证一个进程申请资源的时候,不能占用资源
- 进程申请资源的时候,先申请所需要的所有资源,在执行进程
预防非抢占: - 如果进程占用一个资源,并申请一个不能被立即分配的资源,那么期现已分配得资源都可被抢占
- 常应用在 状态可以保存和恢复的资源上,寄存器、内存等,不适合其他资源(打印机,磁带等)
预防循环等待 - 对所有资源完全排序,要求每个进程按递增顺序来申请资源,申请资源类型序号大的时候,必须先释放占有的资源序号小的资源,这样就构不成循环
死锁避免
采用算法计算是否会产生死锁,如果会就不分配资源给他
线程
80 年代之前,操作系统一直都是以进程作为独立运行的基本单位,后来提出了更小的独立运行单位 -- 线程
进程之间共享数据太麻烦,只能通过操作系统进程间通信 出现了线程
线程: 进程当中的一条执行流程 共享进程资源
一个进程可以存在多个线程,同时可以并发执行;
一个线程崩溃,会导致所属进程的所有线程崩溃;
什么时候使用进程? 线程?
强调性能时候 -- 线程
打开一个 chrome 标签使用线程 ? 如果某个标签页存在恶意代码,崩溃 那么 chrome 将会崩溃 所以现在都是使用进程替代
单线程: 资源管理只有 代码 数据 IO 资源等 寄存器 堆栈
多线程: 共享的代码 数据 IO 资源 分别管理寄存器和自己的堆栈
多线程优点
- 响应度高
- 资源共享
- 经济
- 多处理器体系结构的利用: 充分利用多处理器的体系结构,以便每个进程能并行运行在不同处理器上,不管有多少 CPU,单线程进程只能运行在一个 CPU 上,(在多核硬件结构中,如果要充分发挥硬件的性能,必须采用多线程(或者多进程)执行,使得每个 CPU 核在同一时刻都有线程在执行,和单核的多线程不同,多核上多个线程真的是物理上并行执行的)
进程和线程区别
进程是资源分配的最小单位 ; 线程是 CPU 调度的最小单位
进程拥有一个完整的资源平台;线程只独享必不可少的资源如寄存器/堆栈
线程同样具有就绪 阻塞 执行状态,同样具有状态转换关系
线程能减少并发执行的时间和空间开销
线程的实现
- 用户线程:在用户空间实现 POSIX-Pthreads mach-c-threads solaris-threads 操作系统看不到 TCB 在应用态中 应用态的应用程序库(用户线程库管理) 如果一个线程系统调用而阻塞,则整个进程等待,一个线程开始运行后,要主动交出 CPU 使用权,否则其他进程无法执行(没有时钟中断打断 CPU),多线程执行时候,线程获取的时间比内核线程少
- 内核线程: 在内核中实现 操作系统管理起来 内核维护 PCB TCB 线程的创建终止调度通过系统调用/内核函数方式进行,系统开销较大
轻量级进程: 内核中实现,支持用户线程 linux 使用
用户线程和内核线程的对应关系:
多对一 如果一个线程阻塞系统调用, 整个进程会阻塞;
一对一
多对多
上下文(Context)切换: 就是 CPU 在切换进程的时候(运行态转其他状态)需要保存进程信息,必须能够在下次恢复他们
需要存储什么东西? 寄存器(PC SP ...)CPU 状态 等等保存到进程控制块某个地方
fork() fork 一个子进程出来 复制父进程内存和寄存器信息到子进程
exec() 开启一个新的程序,清空之前的进程的数据
wait() 等待子进程的结束 子进程执行结束后,释放资源,操作系统回收资源等等,但是 PCB 是子进程自己释放不了的,父进程使用 wait 等待操作系统通知 i 他子进程执行结束了
进程结束了但是 PCB 还没有清理被称为 僵尸进程 如果父进程存在 wait 后进程结束,父进程不存在,init 进程会把子进程释放所有的数据结构,进程结束
exit 进程退出
出现了 写时复制 fork 时候不真实复制内存数据,而是简单复制内存元数据信息,只有在进程/子进程写入的时候,才复制内存数据
CPU 调度
调度程序
进程的选择由短期调度程序或者 CPU 调度程序执行,
就绪队列不必是先进先出,就绪队列可实现为 FIFO /优先队列/树/无序链表
非抢占式调度: 一旦 CPU 分配给某个进程,那么该进程会一直占用 CPU,知道进程终止或者切换为等待状态
抢占式: 才是主流
调度准则
对于调度算法的评判准则
- CPU 使用率
- 吞吐量,(一个时间单位完成的进程数量)
- 周转时间(完成该进程需要的时间)
- 等待时间
- 响应时间
调度算法
- 先到先服务 FCFS first come first served
- 最短作业优先 SJF shortest-job-first
- 优先级调度
- 轮转法调度 时间片
- 多级队列调度
内存管理
进程访问内存范围使用两个寄存器保存: 基地址寄存器 -- 界限地址寄存器
操作系统监视 进程是否访问了非法地址 : 使用特权指令取得上述寄存器的值,只有操作系统才能修改两个寄存器