操作系统是一个搞管理的软件。
计算机操作系统:
手机操作系统:
硬件设备就是像鼠标。键盘这样看得见,摸得着的。
比如我们的笔记本打开后盖后,看到的就都是硬件设备
驱动程序是硬件设备的一种,硬件厂商在开发硬件的同时会提供驱动。
电脑装了对应的驱动,才能让系统正确的识别硬件设备。
操作系统内核是操作系统的核心功能,对上,对下进行管理。
系统调用是操作系统给应用程序提供的 API
比如:有个程序想操作一下硬件设备,就需要先通过系统调用,把系统命令告诉给系统内核,
内核调用驱动程序,进一步操作硬件设备。
应用程序 - 我们学的 java 就是在应用程序这一层。
一个跑起来的程序就是进程,如果没有跑起来就不是进程!!!
idea64.exe文件此时还没有运行,它就不是一个进程,但它是一个应用程序。
Ctrl + Shift + Esc 启动任务管理器
总结就是:跑起来的叫进程,没跑起来的叫应用程序。
进程(process) 也叫做任务(task)
每一个进程都对应这一些资源
MB 表示的是Byte,Mb 表示的是Bit。
磁盘和网络组成了IO,衡量IO的时候就会用到带宽这个词表示。
总结:
job术语:
job 比进程更加抽象,进程可以说job的一种具体实现,job也不一定全是进程。
句柄(handler)术语:
系统中包含很多的软件资源(进程就是一种软件资源)。
写代码时就需要用到一些软件资源。
软件资源是在操作系统内核里,在应用程序的代码中,不方便直接操作。
句柄就好比一个遥控器。(简单的整数/编号)
通过系统调用借助这个句柄就可以操作软件资源了。
借助遥控器就可以操作电视了。
进程是一个重要的软件资源,是由操作系统内核负责管理的!!!
操作系统是通过 描述 + 组织 来对软件资源进行管理的。
描述:使用结构体(C语言的结构体)来描述进程属性.
用来描述进程的这个结构体 叫做 PCB(进程控制块),并不是硬件的PCB板。
组织:通过多个双向链表来把多个 PCB 给串到一起。(并不是一个单纯的双向链表)
创建进程本质上就是创建了一个 PCB 这样的结构体对象,把它插入到链表中。
销毁进程本质上就是把链表上的 PCB 结点删除了。
任务管理器查看到进程列表,本质上就是遍历这个PCB链表。
1、PID 进程的身份表示符(唯一的数字)
2、内存指针,指向了说自己的内存是哪些。
3、文件描述符,硬盘上的文件等其他资源。
第2点和第3点描述了了进程持有了哪些资源。
资源分配的问题:
硬件资源、内存、硬盘、网卡都好分配,但是 CPU 不好分配。
我们是希望进程可以 “同时运行” “分时复用” 的。
这就提出了并行和并发的概念
并行:微观上同一时刻,两个核心上的进程就是同时执行的。
并发:微观上,同一时刻,一个核心只能运行一个进程,但是可以对进程快速的切换。
比如, CPU 这个核心上先运行一下QQ音乐、再运行一下画图板。
只要切换的速度快,还上人感知不到。
就像是电灯闪烁的频率太快了,人眼会觉得电灯是常亮的。
内核负责处理的应用程序,程序猿感知不到。
因此往往把并行和并发统称为并发!!!
1、进程的状态(较重要的)
2、优先级
进程是有优先级的!!!
有的进程先执行,有的进程后执行。操作系统进行调度并不是一碗水端平的。
比如,紧急的事情要先去做,不是很着急的事情往后面排排。
3、上下文
操作系统在进行进程切换的时候,就需要把进程执行的 “中间状态” 记录下来,保存好。
下次再执行这个进程的时候,就可以恢复到上次运行的状态,然后继续往下执行。
比如,玩游戏时的 存档 和 读档 。
上下文本质上就是存档的内容。
进程的上下文,就是 CPU 中各个寄存器的值。
寄存器是 CPU 内置的存储数据的模块。
保存的就是程序运行过程中的中间结果。
保存上下文,就是把这些 CPU 寄存器的值,记录保存到内存(PCB)中。
恢复上下文,就是把内存中的这些寄存器的值恢复回去。
4、记账信息
操作系统统计每个进程在 CPU 上占用的时间和执行的指令数目。
根据这个来决定下一阶段如何调度。
内存是一个虚拟的地址空间。
程序中所获取到的内存地址,并非是真实的物理内存的地址。
而是经过了一层抽象,虚拟出来的地址。
物理内存地址是什么?
内存就像是我们的宿舍楼一样。
有个大走廊,走廊上有很多房间,每个房间就是一个宿舍。
每个房间还有一个编号,通过编号就可以定位到房间的位置。
房间编号就是“地址”。
内存(物理上是个内存条)可以存很多数据。
内存就可以想象成是一个大走廊,
走廊非常长,有很多房间,每个房间大小1Byte。
每个房间还有个编号,从0开始依次累加。
这个内存编号,就是“地址”。
这个地址也就认为“物理地址”。
内存有一个了不起的特性,随机访问(闪现):
访问内存上的任意地址数据,速度都极快,时间上也差不多。
正是这个特点,造就了数据取下标操作是 O(1)。
程序并不能直接获取到物理内存的地址。
进程1的范围是 0x1010~0x1FFF。
进程2的范围是 0x3000~0x8FFF。
如果代码不小心出bug了,就可能导致访问的内存越界了。
比如,进程1出现了bug,进程1的指针变量指向了0x3000,这也就造成了进程2页出现bug了。
明明是进程1的bug,却把进程2也搞坏了。
解决办法:
针对进程使用的内存空间,进行 “隔离” ,引入了虚拟地空间!!!
此时代码例不在直接使用真实的物理地址,而是使用虚拟地址。
有操作系统和专门的硬件设备负责进行虚拟地址到物理地址的转换。
一旦进程1反生bug访问越界了,指针变成了 0x3000
当走到硬件设备时,由于硬件设备只能通过 0x1010 到 0x1FFF 之间的地址。
因此,此时进程1无法通过硬件设备到达物理内存。也就无法导致没有错误的进程2产生bug了。
当执行到操作系统内核发现当前这里的地址超出了进程1的访问范围
此时就会向进程反馈一个错误
(具体来说就是发送一个 SIGN SEGEMENT FAULT 信号,引起进程的崩溃)。
总之就是哪里有bug 哪里崩溃,其他位置不受影响!!!
进行隔离是解决进程之间相互影响的问题,但是又引来了新的问题:
有些时候,进程之间确是是需要一些数据的交互的(相互配合)。
如果完全隔离了就无法实现数据的交互了。
此时可以在隔离性的基础上。开个小口子。
通过这个小口子来实现一个 “公共空间” 借助这个公共空间来进行数据的交互即可。
这里的公共空间的有很多的体现形式,后面会主要提到两种方式:基于文件、基于网络