操作系统是程序与硬件交互的中间层,现代操作系统将程序的一次执行抽象为进程和线程的概念。
进程作为资源分配的基本单位,线程作为执行的基本单位。
进程和线程其实就是操作系统程序本身实现控制一个程序运行的数据项描述
所有的程序都是面向语言进行开发的,而语言本身是面向操作系统的,线程是操作系统对程序一次运行的抽象。
所有的多线程编程模型,必然遵从操作系统的大逻辑,必然是符合操作系统的对线程的抽象概念,操作系统在抽象之上提供了API供应用程序调用。
简言之
应用程序的底层是借助于操作系统来完成多线程编程模型的,所以怎么可能逾越系统这一根本?
本篇对操作系统中线程进程相关概念进行简单介绍
进程
在很久很久之前的串行执行时,程序按顺序加载到计算机中并运行,程序独占计算机的所有资源
程序具有顺序性,封闭性和可重现性
多道程序出现之后,程序需要并发的执行,计算机的资源是共享的,而不再是某一程序运行后独享
所以不再是顺序的,而是间断的,也不再是封闭,也不再具有可重现性
还是以做饭为例,当只有你一个人使用厨房时,你可以随便;
但是当多个人共享时,如果你还把你自己切了一半的菜扔到那边,可能会被扔掉,可能会被用掉,当然也可能没事。
为了解决程序并发执行的问题,进程的概念被抽象出来,其实就相当于“一个厨房使用规章”被制定出来
所以说进程和线程就是操作系统用来管理维护一个程序的运行于切换而设计出来的一个概念,然后通过各种数据结构以及值等实现描绘出来。
进程实体
一个程序的运行主要下面几个部分的数据
- 进程本身的信息(现在谁在用厨房?现在盆盆罐罐都被你放了什么?)
- 可执行的代码是哪些?(菜谱步骤是什么?)
- 程序运行所需要的数据是什么?(食材是什么?佐料又是什么?)
程序段、数据段、PCB(Process Control Block)三部分构成了进程实体
进程特征
程序的并发运行与之前的串行顺序运行有了很大的变化,主要有下面几个特征
进程状态
基本状态
多人轮流使用厨房时,某一时刻的你到底是做完饭了?还是还在排队?还是正在做?你会有一个状态用来表述你现在的这种情况
对于进程也有状态的概念
最基本的状态包括:
其中核心是:就绪、执行、阻塞
基本的状态切换如下
状态转变简介:
进程创建后,会创建PCB,以及相关的必须信息,然后就进入就绪状态,等待CPU的调度
一旦CPU对该进程调度执行,也就是该进程获得了时间片,那么就会进行执行
当时间片用完之后,如果任务还没有结束,那么就需要继续等待(比如你做饭需要5小时,然而每个人只允许2小时,如果2小时你做不完,你必须让别人先做,你重新排队来)
如果一个正在执行的程序遇到了IO请求,这通常是比较耗时的,进程会进入阻塞状态
进入阻塞状态的进程一旦获得了想要的结果,比如IO完成,那么就再次进入就绪状态,等待CPU的临幸
挂起状态
有些系统中,还会有挂起状态,可能系统需要让正在执行的程序暂停下来,也可能是资源不足了,将某些不重要的进程暂停。
挂起是更彻底的暂停,可以认为挂起是“暂时被淘汰出内存的进程”
阻塞状态获得资源后会进入就绪状态,而一旦挂起,除非解除这个状态,否则他将一直暂停,被抛出运行之外
阻塞是因为某些原因暂时不能被执行,挂起是直接将你暂停
当你做饭时等待水烧开,这就是阻塞,而如果是老大说我们几个人先来,于是他们几个轮流使用,然后你站门口看着,这就是挂起
包含挂起状态的系统基本情况如上图所示
就绪状态挂起后称之为静态就绪,阻塞状态挂起后称之为静态阻塞,挂起后的状态不能够直接转换到执行状态
活动状态经过挂起转换为静止状态,静止状态经过激活转换为活动状态
活动就绪与静止就绪通过挂起和激活转换;活动阻塞与静止阻塞通过挂起和激活转换;
活动就绪的状态经过进程调度获得CPU时间片,进入执行状态,执行状态遇到请求IO等阻塞操作进入活动阻塞状态,活动阻塞状态IO完成将会进入活动就绪状态,继续等待被CPU临幸,如上图蓝色三角区域
看起来复杂其实也很好理解,前提是要理解挂起的含义
比如资源不充足时,将一些不重要的进程暂时挂起,挂起是真正的暂停执行,是一种主动式的管理,阻塞则是被动的,挂起也意味着置换到外存中,而不是内存中
不管是活动阻塞还是活动就绪,他们都在内存中,具备了相关条件,IO完成或者获得CPU时间片,就可以进行执行
挂起(静止)状态,静止阻塞还是静止就绪,他们都是外存中,并不能够执行,他们还需要一个载入到内存的过程
一个静止阻塞的状态就相当于在外存中等待一个事件的完成,事件完成后,进入静止就绪状态,他此时还是不会得到CPU的调度,激活后才有机会得到CPU临幸
存在挂起状态的系统,经过创建后,可能进入活动就绪,也可能进入静止就绪,并不是一定进入活动就绪,然后再被挂起
在当前系统的性能和内存的容量均允许的情况下,完成对进程创建的必要操作后,相应的系统进程将进程的状态转换为活动就绪状态
考虑到系统当前资源状况和性能要求,并不分配给新建进程所需资源,主要是主存资源,相应的系统进程将进程状态转为静止就绪状态
终止状态的转换
终止状态通常是从执行状态进行转换,一般情况下不管一个什么状态的线程,他只有被执行时,才会可能进入终止状态
但是,在某些系统中,父进程有权利终止一个子进程,所以说这种情况下,就可能从阻塞或者就绪直接转换为终止状态
进程控制块
进程是对于程序执行的抽象描述,那么进程控制块,这个对进程的描述,就相当于进程的元数据,用于描述进程本身
尽管实现很复杂,但是我们应该想象得到,操作系统内核都是C/C++,毕竟也只是一种编程语言,编程语言对于抽象概念的描述也只能是通过语言本身
所以说,他就是一个数据结构,记录了用于控制管理进程的各个数据项。
PCB 中记录了操作系统所需的、用于描述进程的当前情况以及控制进程运行的全部信息。
进程控制块的作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位,一个能与其它进程并发执行的进程。
本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/
或者说,OS是根据 PCB来对并发执行的进程进行控制和管理的
例如,当OS要调度某进程执行时,要从该进程的 PCB中查出其现行状态及优先级;
在调度到某进程后,要根据其 PCB 中所保存的处理机状态信息,设置该进程恢复运行的现场,并根据其 PCB 中的程序和数据的内存始址,找到其程序和数据;
进程在执行过程中,当需要和与之合作的进程实现同步、通信或访问文件时,也都需要访问 PCB;
当进程由于某种原因而暂停执行时,又须将其断点的处理机环境保存在PCB中。
可见,在进程的整个生命期中,系统总是通过 PCB对进程进行控制的,亦即,系统是根据进程的PCB而不是任何别的什么而感知到该进程的存在的。
所以说,PCB是进程存在的惟一标志。 ”《计算机操作系统 第三版》
进程控制块主要包括:
- 进程标识符
- 计算机状态
- 进程调度信息
- 进程控制信息
进程用来管理程序运行,对于一个运行中的程序总归要有个名字,这就是进程标识符;
计算机运行时各个硬件设备寄存器保存的值就是计算机的状态(如同做饭时厨房盆盆罐罐里面的东西);
进程有状态,这些状态信息主要用来进行调度,也就是安排任务需要的信息(可能你长得好看,就能多一次机会使用厨房);
另外还有一些对进程的控制,比如进程(线程)同步数据、程序地址等
在一个系统中,通常可拥有数十个、 数百个乃至数千个 PCB。
为了能对它们加以有效的管理,应该用适当的方式将这些PCB组织起来。目前常用的组织方式有以下两种。
三座大山
进程作为操作系统对程序一次运行的抽象描述
进程的基本信息相当于元数据,就好像表结构一样以及一些必备的数据结构
对于进程的掌控主要有三座大山:
进程控制、进程同步、进程通信
进程控制:
一个进程从无到有,需要创建,创建之后因为调度而运行,因为撤销而消亡,需要有人管理他们,进程基本信息比如PCB从哪里来?都需要有人去做,这部分工作被称为进程控制
进程同步:
多进程并发执行,必然可能会出现竟态,比如同时访问某个共享资源,一个打印机不能同时打印语文和数学,所以必须做好顺序的调度,这部分的工作被称之为进程同步
进程通信:
多进程并发运行,如何进行进程间的联系,如何传递数据?不同计算机中的两个进程又是如何进行数据交互?这部分工作被称之为进程通信
如果一个员工是一个进程,进程控制相当于人事、财务部门,负责招聘薪资考勤等。
进程同步相当于项目经理,负责项目中各人员的任务分配调度。
进程通信就相当于一种工作方式、沟通形式,比如你给我一个SVN标签号并且告知我意图,我去库中检索指定标签修改的指定内容,就完成了一个任务的协作。
比喻或许不足够恰当,仅供个人理解。
总结
进程作为操作系统对程序执行的抽象,那么就使用了足够多的数据项对进程进行描述,所有的信息都是为了进程的管理、维护、调度、切换等
就比如你用一个数组以及一个栈顶标记来描述一个堆栈,如下图所示
数组a[]以及变量top就是使用数组对栈这种数据结构进行抽象描述的数据项,对于数据元素操作访问(入栈、出栈)限制就是规则。
进程看似复杂,原理也是如此,操作系统使用多个数据项(数据结构)对程序的执行进行描述,然后定义了一整套的操作逻辑与规则
这就是我们现在学习的进程的运行原理
通过编码将设计思路以及运行规则从抽象到具体数据结构以及编码的实现就完成了进程的实现。
操作系统想要管理程序的运行,需要指定一个唯一的标识符,既然放一群羊,还想对羊进行合理的管理识别,最简单的办法就是每只羊挂个项圈写上序号。
进程有状态信息,操作系统负责管理状态的切换,那么必然需要记录进程的状态信息
既然是轮流分配时间片,就好像去办理业务,排队等待一样,但是无数个场景下都有VIP的存在,进程也是,也有优先级的概念
计算机的程序最终会转换为一条条的指令,每一条指令的执行都需要借助于程序计数器,程序计数器是用于存放下一条指令所在单元的地址的地方,所以想要知道并且记住程序执行的进度位置,还需要掌握程序计数器的值
我们熟悉的x86和arm指令大多都是基于寄存器的(虚拟机VM是基于栈)基于寄存器的架构最明显的特征就在于指令的执行要依赖寄存器,如同厨房的盆盆罐罐,里面装着运行时的状态数据、值(好比计算1+2+3+4,1放到a寄存器,2放到b寄存器,add a,b 计算中间结果,这句纯属为了说明,具体不要较真)
想要更好地管理进程,那么必然还会有一些统计信息,比如某进程运行了多久等记录统计信息
这些重要的信息都保存在进程的PCB中,所以说PCB是进程概念的核心,有了PCB程序才有并发执行的能力,通常,通常情况下,创建进程指的就是创建PCB
简言之,操作系统对进程的抽象就是对于一组数据结构以及操作这些数据结构的规则逻辑的实现。
进程的核心状态有执行、就绪、阻塞。
就绪就是一切准备妥当,可以被执行,就差CPU时间片了,在执行过程中如果时间片用完,那么仍旧是转换为就绪状态,他什么都不差,只是被剥夺了时间片;
执行状态下,如果执行不下去了,比如需要等待IO,就会进入阻塞,阻塞必须要等到要等的事情发生才会解除阻塞,因为他差一个事件的发生和时间片,事件到达就解除阻塞,所以就差时间片了,所以转换为就绪状态;
比如你去银行办业务,人家手续都带好了在那边排队就是就绪,你身份证没复印好,就得先复印好身份证再过来排队,否则即使到你了,你也办不成;
就绪涉及到很多事情要处理,所以为了更加安全合理,有了创建的状态,创建就是为了保障就绪是真的就绪了,也就是检查你的确各种资料都带齐全了
终止状态也是类似为了更合理安排管理,结束后也有一些事情需要做,比如你把你的资料状态或者还可能要排队领取个什么别的东西,这些都不需要在柜台了,在大厅自助或者找大堂经理就好了
进程涉及到各种数据结构,各种规则处理,所以进程很复杂,但是进程又很简单,就好比单例模式的实现有多种方式有些比较复杂,但是逻辑上却又很清晰,保证唯一
转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-55-1-1.html