操作系统
操作系统综合
操作系统的特征:
并发、共享、虚拟、异步
进程管理
进程与程序
什么是进程
1.进程是指在系统中正在运行的一个应用程序,程序一旦运行就是进程;
2.进程可以认为是程序运行的一个实例,进程是系统进行资源分配的最小单位,但每个进程都有独立的地址空间;
3.一个进程无法直接访问另一个进程的变量和数据结构,如果希望一个进程去访问另一个进程的资源,需要进程间的通信,比如管道、消息队列等;
4.线程是进程的一个实体,是进程的一条执行路径;比进程更小的独立运行的基本单位,线程也被称为轻量级进程,一个程序至少有一个进程,一个进程至少有一个线程;
总的来说,进程就是一个具有一定功能的程序在一个数据集合上的动态执行过程。
一个进程的组成
程序的代码
程序处理的数据
程序计数器
一组通用寄存器,堆、栈
一组系统资源(如打开的文件)
总之,进程包括了正在运行的一个程序的状态信息
进程与程序的联系
程序是产生进程的基础,程序的每次运行构成不同的进程,进程是程序功能的体现,通过多次执行,一个程序可对应多个进程;
通过调用关系,一个进程包括多个程序;
进程和程序的区别
1.进程是动态的,程序是静态的:程序是有序代码的集合,进程是程序的执行,进程有核心态、用户态;
2.进程是短暂的,程序是永久的:进程是一个状态的变化过程,程序可以长久保存;
3.进程与程序的组成不同:进程的组成包括程序、数据和进程控制块(即进程状态信息);
进程为什么有核心态
核心态在操作系统中运行,进程在运行的过程中需要完成一些特定的功能,这些功能只有操作系统可以实现,比如说读文件,我们需要从磁盘中读入信息,此时进程需要向操作系统发出请求,操作系统会帮我们完成功能。
进程的特点
动态性:可动态的创建、结束进程;
并发性:进程可以被独立调度并占用处理机运行;
并发并行独立性:不同进程的工作互不影响;
制约性:因访问共享数据、资源或进程间同步而产生制约;
进程控制块(PCB)
概念:操作系统管理控制进程运行所用的信息集合。
操作系统用PCB来描述进程的基本情况以及运行变化的过程,PCB是进程存在的唯一标志。
使用其可以用来:
进程的创建:为该进程生成一个PCB
进程的终止:回收他的PCB
进程的组织控制:通过对PCB的组织管理来实现
进程控制块包括哪些信息:
主要包含三大信息
(一)进程标识信息:
- 如本进程的标识,本进程的产生者标识(父进程标识)、用户标识。
(二)处理机状态信息保存区。保存进程的运行现场信息:
用户可见寄存器,用户程序可以使用的数据,地址等寄存器。
控制和状态寄存器,如程序计数器(PC),程序状态字(PSW)。
栈指针,过程调用/系统调用/中断处理和返回时需要用到它。
(三)进程控制信息
- 调度和状态信息,用于操作系统调度进程并占用处理机使用。
- 进程间通信信息,为支持进程间的与通信相关的各种标识、信号、信件等,这些信息存在接收方的进程控制块中。
- 存储管理信息,包含有指向本进程映像存储空间的数据结构。
- 进程所用资源,说明由进程打开、使用的系统资源,如打开的文件等。
- 有关数据结构连接信息,进程可以连接到一个进程队列中,或连接到相关的其他进程的电路板
PCB的组织方式:
目前主要采用链表
进程的生命周期
进程创建:引起进程创建的主要三个事件
- 系统初始化时
- 用户请求创建一个新进程
- 正在运行的程序执行了创建新进程的调用
进程运行:内核选择一个就绪的进程,让他占用CPU运行
- 进程调度算法
进程等待:以下情况,进程等待(阻塞) 一般只有进程自己可以阻塞自己
- 请求并等待系统服务,无法马上完成
- 启动某种操作,无法马上完成
- 需要的数据没有到达
进程唤醒:唤醒的原因 唤醒只能被别的进程或者操作系统唤醒
- 被阻塞的进程所需要的资源可被满足
- 被阻塞的进程等待的事件到达
- 将该进程的PCB插入到就绪队列
进程结束:以下的情况进程结束
- 正常退出(自愿)
- 错误退出(自愿)
- 致命错误(强制)
- 被其他进程杀死(强制)
进程的三种基本状态
进程在生命结束前处于且仅处于三种基本状态之一不用系统设置的进程状态数目不同。
- 运行状态:当一个进程正处处理机上运行时
- 就绪状态:一个进程获得了除处理机之外的一切所需资源,一旦得到处理机即可运行
- 等待状态:一个进程正在等待某一事件而暂停运行时,如等待资源,等待输入、输出完成
进程挂起状态:
进程挂起状态时,意味着进程没有占有内存空间,处在挂起装啊提的进程映象在磁盘上。
阻塞挂起状态:进程在外存,并等待某种事件
就绪挂起状态:进程在外存,但只要进入内存即可运行。
线程
什么是线程
线程是进程中的一条执行流程
从资源角度:进程把一组相关的资源组合起来,构成一个资源平台,包括地址空间、打卡的文件等各种资源
从运行的角度:代码在这个资源平台上的一条执行流程
线程控制块(TCB)
用来保持运行时线程状态的数据结构,在线程切换时用来保持线程信息
/* 线程控制块 */ struct rt_thread { /* rt 对象 */ char name[RT_NAME_MAX]; /* 线程名称 */ rt_uint8_t type; /* 对象类型 */ rt_uint8_t flags; /* 标志位 */ rt_list_t list; /* 对象列表 */ rt_list_t tlist; /* 线程列表 */ /* 栈指针与入口指针 */ void *sp; /* 栈指针 */ void *entry; /* 入口函数指针 */ void *parameter; /* 参数 */ void *stack_addr; /* 栈地址指针 */ rt_uint32_t stack_size; /* 栈大小 */ /* 错误代码 */ rt_err_t error; /* 线程错误代码 */ rt_uint8_t stat; /* 线程状态 */ /* 优先级 */ rt_uint8_t current_priority; /* 当前优先级 */ rt_uint8_t init_priority; /* 初始优先级 */ rt_uint32_t number_mask; ...... rt_ubase_t init_tick; /* 线程初始化计数值 */ rt_ubase_t remaining_tick; /* 线程剩余计数值 */ struct rt_timer thread_timer; /* 内置线程定时器 */ void (*cleanup)(struct rt_thread *tid); /* 线程退出清除函数 */ rt_uint32_t user_data; /* 用户数据 */ };
线程 = 进程 - 共享资源
线程的优点:
一个进程中可以存在多个线程;
各个线程之间可以并发执行;
各个线程之间可以共享地址空间和文件等资源
线程的缺点
一个线程崩溃,会导致所属进程的所有线程崩溃
什么时候使用多进程、什么时候使用多线程
- 需要频繁创建销毁的优先⽤线程;
- 需要进⾏⼤量计算的优先使⽤线程;
- 强相关的处理⽤线程,弱相关的处理⽤进程;
- 可能要扩展到多机分布的⽤进程,多核分布的⽤线程
- 强安全性采用多进程,弱安全性采用多线程
进程和线程的比较:
- 进程是资源分配单位,线程是CPU调度单位;
- 进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈;
- 线程同样具有就绪、阻塞和执行三种基本状态,同样具有状态之间的转换关系;
- 线程能减少并发执行的时间和空间开销:
- 线程的创建时间比进程短;
- 线程的终止时间比进程短;
- 同一进程内的线程切换时间比进程短;
- 由于同一进程的各线程间共享内存和文件资源,可直接进行不通 过内核的通信;
线程实现:
用户线程:在用户空间实现
内核线程:在内核中实现
轻量级进程:在内核中实现,支持用户线程
什么是用户线程:操作系统不可管理,由用户线程库管理
什么是内核线程:有操作系统本身来管理
用户级线程和内核级线程的区别
用户级线程仅存于用户空间中,其创建、撤销、线程之间的同步与通信功能,都无须利用系统调用来实现。总之,它的实现是和操作系统无关的,用户进程只需要通过线程库来完成线程控制;
相反,内核线程的建立和销毁都是由操作系统负责、通过系统调用完成的。
从描述的区别上就可以看出,用户级别的线程不用经过内核空间,效率肯定很高,而内核线程是驻留在内核空间的,用户进程在创建使用内核线程时访问内核空间是必不可少的工作,效率上相对较低。
既然内核线程效率低,那为什么还要引入他呐?我们知道在线程中经常会用到系统调用,对于用户线程来说,如果系统调用是阻塞的,那么意味着这个线程所属的整个进程都会阻塞住。
这种情况下还是需要请高人出山—内核线程就闪亮登场了,内核线程是在内核空间实现的机制,也就是说它在操作系统那排了号(有线程控制块TCB)。
用户线程缺点:
阻塞性的系统调用如何实现﹖如果一个线程发起系统调用而阻塞,则整个进程在等待;
当一个线程开始运行后,除非它主动地交出CPU的使用权,否则它所在的进程当中的其他线程将无法运行;
由于分配时间片分配给进程,故与其他进程比,在多线程执行时,每个线程得到的时间片较少,执行会较慢。
轻量级进程
它是内核支持的用户线程。一个进程可有一个或多个轻量级进程,每个量级进程由一个单独的内核线程来支持。(Solaris/Linux )
进程调度
调度原则:
调度策略
程序执行模型:程序在CPU突发和IO中交替
比较调度算法的准则
吞吐量vs延迟
公平的目标
评价指标:
CPU使用率:CPU处于忙状态所占的时分比
吞吐量:在单位时间内完成的进程数量
周转时间:一个进程从初始化到结束。包括所有等待时间所花费的数据
等待时间:进程在就绪队列中的总时间
响应时间:从一个请求被提交到第一次响应锁花费的总时间
进程调度算法
先来先服务调度算法(FCFS):
先到的进程先执行。
优点:简单
缺点是短作业需要等待。可能导致IO和CPU之间的重叠处理
短作业优先调度算法:(SJF):
优先让执行时间短的作业先完成,分为抢占式和非抢占式。
缺点:
可能会造成长作业饿死。平均周转时间最短。
需要预知进程运行时间,难以实现。
最⾼响应⽐优先调度算法(HRRN)
计算方式为优先级R = (w+s)/s ||w为等待时间、s为执行时间
特点:
在SPN的基础上改进
不可抢占
关注了进程的等待时间
防止无限期的推迟
时间⽚轮转法调度算法(RR)
让各个进程轮流占用CPU
总结:
RR花销:额外的上下文切换时间量子太大
等待时间过长
极限情况退化成FCFS
时间量子太小
反应迅速,但是……
吞吐量由于大量的上下文切换开销受到影响
目标:
选择一个合适的时间量子
经验规则:维持上下文切换开销处于1%以内
多级反馈队列算法:(MLFQ)
等待时间长的优先级高,执行时间长的优先级低
公平共享调度算法FFS:
在用户级别实现公平调度。
实时调度算法
定义:正确性依赖于其时间和功能两方面的一种操作系统
性能指标:
- 时间约束的及时性(deadlines)
- 速度和平均性能相对不重要
主要特性:时间约束的可预测性
分类:
强(硬)实时系统:需要在保证时间内完成重要的任务,必须完成。
弱(软)实时系统:要求重要的进程的优先级更高,尽量完成,非必须。
硬时限
如果错过了最后期限,可能会发生灾难性或非常严重的后果
必须验证:在最坏的情况下也能够满足时限吗﹖ 保证确定性
软时限
理想情况下,时限应该被最大满足。如果有时限没有被满足,那么就相应地降低要求。 尽最大努力去保证
实时调度算法类型:
静态优先级调度
动态优先级调度
优先级反转:
可以发生在任何基于优先级的可抢占的调度机制中 当系统内的环境强制使高优先级任务等待低优先级任务时发生
产生原因:因为低优先级任务访问了高优先级的共享资源,当低优先级访问没结束的时候,高优先级任务不可被调度,而导致中间优先级的执行时间高于高优先级的任务的执行时间。
解决方案:低优先级任务基层高优先级任务的由下级依赖于他们的共享资源
也就是说,当低优先级进程访问高优先级进程资源的时候,将低优先级进程的优先级提升到和高优先级一样。
优先级天花板:“资源”的优先级和“所有可以锁定该资源的任务中优先级最高的那个任务”的优先级相同 除非优先级高于系统中所有被锁定的资源的优先级上限,否则任务尝试执行临界区的时候会被阻塞 持有最高优先级上限信号量锁的任务,会继承被该锁所阻塞的任务的优先级
资源的同步与互斥
原子操作:
原子操作是指一次不存在任何中断或者失败的执行
该执行成功结束,或者根本没有执行,并且不应该发现任何部分执行的状态
临界区:
临界区是指进程中一段需要访问共享资源并且当另一个进程处于相应代码区域时便不会执行的代码区域
互斥:
当一个进程处于临界区并访问共享资源时,没有其他进程会处于临界区并且访问任何相同的共享资源
死锁:
两个或以上的进程,在相互等待完成特定任务,最终没法将自身任务进行下去
饥饿:
一个可执行的进程,被调度器持续忽略,以至于虽然处于可执行状态但不执行
临界区特点:
互斥:同一时间临界区最多存在一个线程
progress:如果一个线程想进入临界区,那么它最终会成功
有限等待:如果一个线程处于入口区,那么在它的请求被接受前,其他进程进入临界区的时间是有限制的
无忙等待(可选):如果一个进程正在等待进入临界区,那么在他进入之前会被挂起
对临界区保护的三种方案:
禁用硬件中断
基于软件的解决方法
更高级的抽象
信号量:
管程:
是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件设备或一群变量。管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。
与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程实现很大程度上简化了程序设计。
管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。即:在管程中的线程可以临时放弃管程的互斥访问,让其他线程进入到管程中来。
管程包含:
多个彼此可以交互并共用资源的线程多个与资源使用有关的变量
一个互斥锁
一个用来避免竞态条件的不变量
同步经典问题
读者-写着问题:
前提:读者不会修改数据,所以可以同时存在多个读者,但只可以同时存在一个写者,并且写者存在的时候,读者不能存在。
读者优先:基于读者优先策略的方法,只要有一个读者处于活动状态,后来的读者都会被接纳。如果读者源源不断地出现的话,那么写者就始终处于阻塞状态。(写者饿死)
写者优先:基于写者优先策略的方法: 一旦写者就绪,那么写者会尽可能快地执行写操作。如果写者源源不断地出现的话,那么读者就始终处于阻塞状态。(读者饿死 )
这边有一个比较不错的方案,就是按来的顺序排队,当出在写者的情况下,那么就将后续进来的读写都阻塞,直到写者写完来到下一个,判断来的是读者还是写者,如果是读者同时它的前面没有写者那就继续唤醒读者,否则就先写。
哲学家就餐问题:
问题:可能每个哲学家都拿了左手边的叉子,导致五个叉子都被那完了,就拿不到右边叉子。(死锁)
解决方案:
- 可以用条件变量,拿完左边后判断是否可以拿到右手边叉子,拿不到就吧叉子放下。问题:大家会同时拿叉子,然后又同时放下叉子的问题。
- 可以对叉子编号,只允许先拿小号叉子,才能拿大号叉子。
- 可以以哲学家的状态为资源,判断左右哲学家是否在吃饭,如果没有则拿起左右两根叉子,否则不拿叉子
如何解决死锁
确保系统永远不会进入死锁状态。(死锁预防和死锁避免)
运行系统进.入死锁状态,然后恢复。(死锁恢复)
忽略这个问题,假装系统中从来没有发生死锁;用于大多数操作系统,包括UNIX。(鸵鸟策略,实际操作系统中大多数采取这个策略。)
死锁预防
限制申请方式:
互斥-共享资源不是必须的,必须占用非共享资源。
占用并等待-必须保证当一个进程请求的资源,它不持有任何其他资源。
需要进程请求并分配其所有资源,它开始执行之前或允许进程请求资源仅当进程没有资源.
资源利用率低;可能发生饥饿。
无抢占:
- 如果进程占有某些资源,并请求其它不能被立即分配的资源,则释放当前正占有的资源
- 被抢占资源添加到资源列表中
- 只有当它能够获得旧的资源以及它请求新的资源,进程可以得到执行
循环等待:对所有资源类型进行排序,并要求每个进程按照资源的顺序进行申请.
死锁预防主要在嵌入式操作系统中可能会采用
死锁避免
- 需要系统具有一些额外的先验信息提供.
- 最简单和最有效的模式是要求每个进程声明它可能需要的每个类型资源的最大数目.
- 资源的分配状态是通过限定提供与分配的资源数量,和进程的最大需求.
- 死锁避免算法动态检查的资源分配状态,以确保永远不会有一个环形等待状态.
- 当一个进程请求可用资源,系统必须判断立即分配是否能使系统处于安全状态。
- 系统处于安全状态指:针对所有进程, 存在安全序列。
- 序列<P1,P2,.... PN>是安全的:针对每个P, P;要求的资源能够由当前可用的资源+所有的P持有的资源来满足,其中j<i。
- 如果P,资源的需求不是立即可用,那么P;可以等到所有P完成。
- 当P;完成后, P:,可以得到所需要的资源,执行,返回所分配的资源,并终 止。
- 用同样的方法,P:+2, P:+3 和P。能获得其所需的资源。
解决方案:银行家算法
死锁检测
允许系统进入死锁状态,隔断实际判断死锁,如果死锁了采用恢复机制
死锁恢复
终止所有的死锁进程 在一个时间内终止一个进程直到死锁消除 终止进程的顺序应该是
- 进程的优先级
- 进程运行了多久以及需要多少时间才能完成
- 进程占用的资源
- 进程完成需要的资源
- 多少进程需要被终止
- 进程是交互还是批处理
协程是什么
- 是⼀种⽐线程更加轻量级的存在。正如⼀个进程可以拥有多个线程⼀样,⼀个线程可以拥有多个协程;协程不是被操作系统内核管理,⽽完全是由程序所控制。
- 协程的开销远远⼩于线程;
- 协程拥有⾃⼰寄存器上下⽂和栈。协程调度切换时,将寄存器上下⽂和栈保存到其他地⽅,在切换回来的时候,恢复先前保存的寄存器上下⽂和栈.
- 每个协程表示⼀个执⾏单元,有⾃⼰的本地数据,与其他协程共享全局数据和其他资源。
- 跨平台、跨体系架构、⽆需线程上下⽂切换的开销、⽅便切换控制流,简化编程模型;
- 协程⼜称为微线程,协程的完成主要靠yeild关键字,协程执⾏过程中,在⼦程序内部可中断,然后转⽽执⾏别的⼦程序,在适当的时候再返回来接着执⾏;
- 协程极⾼的执⾏效率,和多线程相⽐,线程数越多,协程的性能优势就越明显
- 不需要多线程的锁机制
浙公网安备 33010602011771号