操作系统的定位:
操作系统是一个软件。操作系统是软件,硬件,用户之间交互的媒介。
对上:要给软件提供稳定的运行环境
对下:要对硬件设备进行管理
我们大家所熟知的操作系统有Windows,Linux,Mac,Android,IOS等,Android本质上也是Linux,IOS和Mac同宗同源
操作系统的结构:
1.应用程序 2.系统调用 3.操作系统内核 4.驱动程序 5.硬件设备
结构框图如下:
其中,系统调用,操作系统内核和驱动程序构成我们平时所说的操作系统。
驱动程序:厂商在开发硬件的时候会提供驱动,只有电脑装了对应的驱动,才能让系统正确识别硬件。
操作系统内核:这是操作系统的核心功能,对上对下都进行管理
系统调用:操作系统给应用程序提供的api,例如有一个程序想操作一下硬件设备,需要先经过系统调用,把操作的命令告诉给系统内核,内核再调用驱动程序,再进一步的操作硬件设备
进程:
一个跑起来的程序就是一个进程,没有运行的就不叫进程而叫做一个程序。进程是操作系统资源分配的基本单位,进程是一个重要的软件资源,是由操作系统内核负责管理。
因为进程非常多,操作系统需要通过描述+组织对多个进程进行管理。
描述主要指讲清楚进程有哪些属性和特征,主要使用PCB这样的结构体描述进程属性,PCB也被称为进程控制块。
组织是通过一定的数据结构(双向链表)把多个PCB串到一起。
创建一个进程:本质上就是创建一个PCB这样的结构体对象,把他插入到链表当中。
删除一个进程:本质上就是把链表上的PCB节点删除掉。
任务管理器查看到进程链表,本质上就是遍历这个PCB链表,我们可以通过任务管理器看见电脑正在运行的程序以及后台的进程。
PCB里有什么,描述了进程的特征:
1.pid:进程的身份标识符(唯一的数字)
2.内存指针(表明自己的内存是哪些)
3.文件描述符表(硬盘上的文件等其他资源)
4.进程调度相关的属性
进程调度的相关属性
目的:为了解决 "狼多肉少" 的问题,让大量的进程能在少数的CPU上同时运行。
1.进程的状态(主要的三个):
1)就绪状态:随叫随到,随时准备好去CPU上执行。
2)运行状态:正在CPU上执行。
3)阻塞状态:短时间内无法响应,短时间内无法在CPU上执行。
2.进程的优先级:先给谁排,给谁排多一些。
3.上下文:记住上次到什么程度了,是否有未完成的任务。
操作系统在进行进程切换的时候,需要把进程执行的"中间状态"记录下来保存好,下次这个进程再上CPU上运行的时候就可以恢复上次的状态。这里的上下文本质上就是存档的内容,进程的上下文就是CPU中的各个寄存器的值。
保存上下文:把这些CPU寄存器的值记录保存到内存中。
恢复上下文:把内存中的这些寄存器值恢复过去。
4.记账信息:操作系统统计每个进程在CPU上占用的时间和执行指令的数目,根据这个决定下阶段如何调度。
并行和并发
并行:微观上同一时刻,两个核心上的进程,是同时执行的。
并发:微观上同一时刻,一个核心上只能运行一个进程,但是它能够对进程快速的进行切换,所以宏观上是并行执行的。
因为并行和并发都是由操作系统内核负责处理,应用程序感知不到,因此并行和并发统称为并发,通过并发即可解决狼多肉少的问题,让很少的CPU服务多个进程。因此操作系统中有一个重要的模块调度器,负责让有限的CPU调度执行这么多进程。
进程的虚拟地址空间
目的:解决的是进程之间相互影响的问题。
内存管理
内存:物理上是内存条,可以存储很多数据,可以随机访问,访问内存上任意地址的数据,速度都极快,时间上也都差不多。
内存可以想象成一个大走廊,走廊非常长,有很多房间,每个房间大小一个Byte,每个房间还有自己的编号,从0开始累加类似于数组。这里的内存编号就是"地址",这个地址就被认为是"物理地址"
但如果每个进程都在分配的内存物理地址上运行,就会出现问题。如图所示,进程1在0x1000-0x1fff上,进程2在0x8000-0x8fff上,如果说进程1代码出了bug,可能导致访问内存就越界了,指针变量可能会变成0x8000,这样可能是进程1的bug却把进程2搞崩溃了。
因此为了对进程使用的内存空间进行隔离,引入了进程的虚拟地址空间。代码里不再直接使用真实的物理地址,而使用虚拟地址,由操作系统和专门的硬件设备(MMU)负责进行虚拟地址到物理地址的转换。如下图所示。
给每个进程分配一个虚拟空间,操作系统会通过MMU硬件设备把虚拟地址映射到真实的物理内存上,这时一旦进程访问越界,此时的MMU进行翻译时会认定地址越界,操作系统内核会发现当前这里的地址超出进程的访问范围了,此时就会直接向进程反馈错误,引起进程的崩溃。这样谁出了bug谁自己崩溃就不会影响到其他的进程。
进程间通信
解决了进程间相互影响的问题又会引入新的问题,有时候进程与进程之间确实需要相互通信,需要数据交互,而通过指针变量,我们无法修改另一个进程的数据,这时我们就需要在隔离性的基础上开个口子,也就是进程间通信,
这里需要一个多个进程都能访问到的公共空间,基于这个公共空间来交互数据。这个公共空间的体现形式有很多,主要是基于文件和基于网络(网卡)。