进程:正在运行的应用程序 (同一时刻一个cpu只能执行一条指令)
顺序进程—简称进程
多道程序设计–各个程序之间快速切换可以实现我们现在认为的同时运行(或者多核cpu)
/**********/
静态- 程序
动态-进程 进程包括程序、数据、参数、状态
进程创建
(问题:所有进程的创建是不是都需要系统调用,都是产生一个中断,系统初始化的是不是不产生中断,就是刚刚开机的时候)
进程创建的四大原因
1.系统的初始化(系统自动开始进行系统调用)
2.正在运行的进程执行了创建进程的系统调用(就是一个进程打开了他的子进程)
3.用户请求打开新的进程
4.批处理作业的初始化(批处理由用户发起)
总结:进程的创建的起点不是操作系统本身,就是用户。
但是都要通过系统调用来实现,都要中断当前进程,等候是由已经存在的进程执行了进程创建的系统调用而创建的。(不对!等候的中断,有可能是io等候等原因引起)
最后总结成两类:
系统自身调用下一进程
用户提出请求创建进程
进程终止
分为两类
自愿:正常退出 、出错退出
非自愿:严重错误、被其他进程杀死(如果说cpu里面只有一条进程,怎么杀死,肯定这个先挂起,第二个执行,将其杀死,或者多核cpu运行的进程被其他进程杀死,但是前一种情况不是自愿挂起的吗??,被杀死的进程什么状态?)
——整个调用都是以系统调用的过程,用户发起请求,那么处理机就会先把原来进程挂起,再执行另一个进程把他杀死)
进程的层次结构
进程只能有一个父进程,父进程会向下建立多个子进程,子进程再向下,最后形成进程树,形成进程组。
(进程组的动作一部分由用户发出的信号决定,用户信号,先通知系统,系统再决定进程组的动作)
进程的状态
进程之间的数据交换,有时候,一个进程的输入,是上一个进程的输出。–进程之间的通讯?
阻塞的两种状态(当时的初步理解)
阻塞–在逻辑上不能继续运行(这种情况怎么会经常出现呢?程序被创造出来,不是为了制造死机的。。。)
另一种-在逻辑上可以运行,但是被迫停止,此时操作系统调用另一个进程占用处理机。(IO中断等。。)
1.运行态——占用处理机
2.就绪态—可运行,因为其他程序正在运行,暂时被挂起
3.阻塞态—除非发生外部事件,否则不能运行(不在处理机中,不可以运行—线程要是在运行中转换为阻塞态怎么办)
,处理机会将阻塞态的进程调离出处理机,再掉一个新进程进来,假如不掉出怎么办?调出后,不再调用其他什么情况?必须调出啊,我们理解的阻塞,本身是存在阻塞队列的,他就是不在处理机中)
**
面试常考
**
1.从运行态到阻塞态
(通过某种系统调用,block或者pause;或者自动转换为阻塞态)(+阻塞处理机将等待外部事件相应的进程移动至阻塞队列中)
2.运行态与就绪态的互换
(由进程调度器引起—系统调用的一种,调度器决定哪个进程运行,运行多长时间)
(调度器只能调用就绪态的进程进入处理机,也就是说阻塞态不能直接变为运行态!!!!)
3.从阻塞态到就绪态
阻塞态因为是一个等待外部事件发生的进程,当外部事件发生了以后,我们就可以将阻塞态变为就绪态。(后续等待调度器的指令,转为2转换)
4.从运行态到阻塞态(吐槽一下,这个应该在3转换前面好吗?。。。。。整个顺序都是乱的,应该先有就绪态、运行、阻塞、就绪、、)
(阻塞态的产生就是等待用户的输入,或者等待外部事件的发生?我们常说的卡了,怎么产生的 是阻塞态吗??)
进程的实现
基础:1.进程表–一个进程占用一个进程表项,包含状态、程序寄数器等等吧,以便于再次启动的时候好像从未中断一样(诗意啊,从未离开过一样???从未离开过就是能继续执行,知道了!!)
2.中断向量–位于靠近内存底部的位置(内存底部什么意思,物理层面上吗?内存上部是啥啊?)包含中断服务器的入口地址
中断向量就是他要跳的位置(怎么知道他要跳哪里?这不是提前写好的吧?不是 这个中断向量是我们要去的地方,他由提出中断的程序给出)
3。我们把所有的状态压到内存中,在留一个指针方便我们寻找。
4.处理完3以后我们去向中断向量发一个消息说,ok可以了,然后阻塞态变为就绪态,中断服务器之二是调用调度器,看看优先级然后这个就绪态没准就能变为运行态)
线程的概念
在传统的os中,每个进程只存在一个地址空间和一个控制流。
线程其实是控制流,他们共享一个内存空间。
资源模型去理解线程,很多资源组成一个大的集合—进程,然后每个小的资源又需要管理,那么对每个小资源的管理的就是线程。又叫执行流。
1.线程必须在进程中执行
2.线程是cpu中调度的实体
线程不一定执行一个动作,但是是在同一块内存中,其实理解为操作同一文件、同一资源。
3.线程表与进程表类似
二者区别,线程更加轻量级
进程间的通信(IPC)
涉及到三个问题:
1.进程如何向另一个进程传送消息
2.保证多个进程活动及临界活动不会彼此影响
3.需要等待另一个进程的结果作为输入
竞争条件
共享资源—守护进程
多个进程读写某些共享数据,最后的结果取决于进程运行的精确时序,就称为竞争条件。
临界区
(需要互斥!一个进程在访问当前资源,其他进程不能进行访问。)
对-共享内存-进行访问的程序片段称为临界区或者临界段
(说的是一段代码,就是相邻的、共享同一数据的位置—数据的位置)
使得两个进程不可能同时存在在临界区–能够避免竞争条件
对临界区产生问题的四个解决条件
1.任何两个进程不能同时处于临界区
2.不应对cpu的速度和数目做任何假设
3.临界区外的进程不得阻塞其他进程
4.不得使进程在临界区外无休止的等待
忙等待形式的互斥
1.关闭中断
每个进程在进入临界区后先关中断,在离开前在开中断。
中断关闭,时钟中断被屏蔽,cpu不能切换到其他进程,所以不用担心其他进程的介入。
缺点:
1.它只是锁住当前的cpu,其他还是不能锁住,进程还是会进入临界区。
2.用户进程本身决定开关中断,这很危险
2.锁变量
进入之前看下共享锁的状态,如果锁为0,进程进入临界区并且把锁状态变为1.
缺点:存在一种情况可以让两个进程同时进入临界区
3.严格交替法
多个进程严格轮流进入他们的临界区,交替法。
缺点:
1.顺序都是由系统决定的,存在顺序不正确的情况,有一些进程没有之前报备,可能会安排不上。
2.存在忙等待。进程不断读取turn值,但是什么也没做。。。
(忙等待,持续地检测一个变量知道它具有某一特定值就称为忙等待)
(自旋锁,等待时间很短的忙等待)
4.dekker算法
没有欲通知
5.peterson算法
加上欲通知!(加上警告变量),会感知其他进程是不是也想进去临界区。
如果其他线程也想进,再判断turn值。如果turn值轮到自己把其他变量的警告变量赋值成0,自己先做,做完了再回复其他变量为1。
硬件解决方案
TSL指令(测试并上锁)
进程在进入临界区之前先调用 enter——region。
这将导致忙等待,直到锁空闲为止
忙等待会浪费cpu时间,造成优先级反转。
睡眠和唤醒
另一种替代忙等待的方式,是睡眠和唤醒。
sleep系统调用使得进程阻塞,直到有另一个进程将其唤醒。
wakeup调用一个参数,既要被唤醒的进程。
另一个方法是,两个都有一个参数,用于匹配相应的内存地址。
生产者-消费者模式(有界缓冲区问题)
两个进程共享一个公共的、固定大小的缓冲区。其中一个是生产者(理解为即将使用完缓冲区的进程),负责将信息放入缓冲区;另一个是消费者(理解为即将使用缓冲区的进程),负责从缓冲区取出信息。谁越界取缓冲区资源的时候,都会睡眠。直到缓冲区有空间资源被调用或者放入。
机制历程
设置一个count,最大为N,生产者将首先检查是否到达N,若是生产者睡眠;否则,生产者向缓冲区放入一个数据项,并将N值增加1;
消费者过程类似,首先检查N是否为0,若是则消费者睡眠,否则取出一个数据项,并将N值减1。
另外进程同时也应检测另一个是否应睡眠,若不应睡眠则唤醒它。
存在问题,临界值的时候存在两者都会睡眠,并且一直睡眠。------唤醒信号量丢失问题。
解决问题,加上唤醒等待位。
信号量
它使用一个整形变量来累积唤醒次数—信号量
一个信号量值可以为0,表示没有积累下来的唤醒操作。或者为正值,表示有一个或者多个被积累下来的唤醒操作。
设置两种操作 down和up(一般化后为sleep和wakeup)
对于一个信号量执行down操作,先检查信号量是否大于0,如果是这样,则将其值减1(用掉一个保存唤醒的信号)并继续,如果为零,则进程将睡眠。这个过程的每一步是原子操作不可分割的。检查数值、、改变数值、、可能的睡眠操作均为单一的,保证一旦一个信号量操作开始,则在操作完成或阻塞之前其他进程不许访问信号量。
up操作递增信号量的值。如果一个或多个进程在该信号量上睡眠,无法完成一个先前的down操作,则由系统让其完成。
down、up都是由系统调用来完成,还因为不可分割,所以解决了唤醒信号量丢失的问题。
两种操作
用信号量就解决生产者消费者问题。
该解决方案使用了三个信号量:full用来记录慢的缓冲槽数目,empty用来记录空的缓冲槽总数,mutex用来确保生产者和消费者不会同时访问缓冲区。full的初值为0,empty的初值为缓冲内槽的数目,mutex的初值为1。两个或者多个进程使用的初值为1的信号量保证同时只有一个进程可以进入临界区,它被称为二进制信号量。
信号量的两种用法 1.互斥 2.同步
互斥
不需要信号量的计数能力,我们使用一种简单的信号量,互斥。互斥是一个处于两态之一的变量:解锁和加锁。
0表示解锁,其他值表示加锁。
:::mutex___unlock,线程进入临界区前检查这个互斥值,若是大于0则为加锁,继续阻塞。如果为0线程直接进入临界区。
管程
当我们使用信号量的时候,存在一种糟糕的状况,死锁。
更高级的同步原语—管程。
管程是由过程、变量及数据结构的集合。
(用类似于java单例模式的方法,随时调用管程的过程,但是不能直接调用管程的数据结构)。
管程特性:任意时刻管程中只能有一个活跃进程。对于管程的操作的自我理解:将一个进程放进管程中,其他进程再想进入临界区的话,那么其他进程不会直接向临界区访问,而是交给管程。管程来操作。管程由编译器来完成,而非程序员。
消息传递
两条原语-----Send receive(系统调用) 就是一个调用给另一个调用发信息的过程。
小路问题 哲学家进餐问题()读者、写者问题
饥饿问题,有些进程一直在等待资源。
--------以上进程解决,临界区的问题,下面解决的是进程调度问题,并发问题。
调度器、调度算法
cpu调度
进程调度的情况
调度种类
1.内外存调度—中级调度 内外存交换,将部分内存中,换进外存
2.cpu-低级调度 微观调度–本质进程调度,准备、运行、阻塞这三个微观操作–时间上是毫秒级
3.高级调度—整个线程与线程之间的调度,谁创建、谁终止。
从用户工作流的角度。时间角度,比较大,分钟、小时之类的。
调度性能的评价与目标
公平 —给每个进程公平CPU份额
平衡
响应时间
周转时间
吞吐量
Cpu利用率
调度分类—可剥夺、不可剥夺
批处理系统中的调度
调度算法
先讲两个概念
----平均等待时间—所有进程的等待时间/进程个数
----周转时间—等待时间加上运行的时间的和的时间–(简单说,就是进程进场到执行完毕的时间)
1**
** first-come-first-server
几个涉及到计算的概念问题
burst-time 区间时间
等待时间–到达就绪队列以后,等什么时间开始执行,看前一个进程(或者多个)执行时间–进入就绪队列时间–到达时间
特点:有利于长作业,而不利于短作业
(这块说的有问题,先到先得算法,难道是长进程放在前面吗?那怎么提现先到先服务呢??)(这块的逻辑是这样的,上面说的就是长时间片轮转优先,同时基于先到先服务,下面的是基于先到先服务的最短作业优先)
小的轮转时间放在前面 大的轮转放在后面 这样整体轮转时间就会缩小 等待时间也会缩小。—解决护航效应(短进程放在后面,这就是护航效应,想象航母舰队,航母最大,在最前面!)
(为什么不能忽略短进程,更深入的原因是因为往往短进程都是用户提出的想要及时响应的进程)
**
剥夺和不剥(区别在于是否剥夺当前进程占有的处理机)
最短作业优先(基于先到先服务,考虑区间时间轮转)
不剥夺的话,谁先进来先执行,后面进来的,看看是不是有在执行的,等着,然后后面等着的,再重现按照区间时间重新排列,如果区间相同,看谁先进来的。
最短剩余优先
剥夺当前cpu使用权,根据区间时间。
sjf优点 改善轮转 消除护航效应
Sjf存在问题 不容易知道cpu需要的轮转时间,对长进程运行不利,长进程可能一直处于饥饿状态,未能够考虑优先级。
交互式系统中的调度
时间片轮转法–基于先到先服务。时间片轮转法中,需要等待一轮执行结束才能计算,比如说 A B C中 A在这一轮最先执行完,但是A要等到c执行完之后才结束,这样计算执行时间。
(这块会有很多人进这个坑,以为自己进程结束了,就结束了,要看这一组进程这一轮全部结束,才能算周转时间)
通过时间片轮转,提高进程并发性和响应时间,从而提高资源利用率。
1.考虑上下文的切换问题(进程切换)
过长的话,退化成先到先服务
过短的话,上下文切换过于频繁
优先级算法
1.静态优先级
优先级可以同级 ,同级之间可以自定义。
静态存在饥饿问题,低优先级的进程一直得不到执行
(饥饿问题,长长出现这个名词,就是有的进程一直得不到资源)
2.动态优先级
随着时间的推移,低优先级的进程逐步提高(每一个进程执行完毕以后,后面的会加权,保证最后会执行到)
多重队列调度
前端进程—重响应
后端进程—重执行