读100本书,走万里路-1-操作系统导论-20220322

操作系统导论-笔记

  • 1.虚拟化
    • 1.1 虚拟化cpu
      • 进程和程序
      • 如何制造出多个CPU的假象?
      • 进程相关的方法
      • 进程API
      • 进程状态模型
      • 如何不增加OS开销而实现虚拟化CPU?如何有效运行进程同时操作系统也要自己保留对CPU的控制权力?!
      • 用户态不能发出I/0请求,那如果用户想读取磁盘中的内容怎么办?
      • 陷阱如何知道在OS内运行哪些代码?
      • 上面我们知道进程是受限执行的,上述通过系统调用从而使得 控制权转交给 操作系统的方式是协作方式。如果没有系统调用,没有任何非法操作,那么操作系统能夺回控制权么?
      • 上述如何破解?-时钟中断!
      • 调度算法考虑的指标!重要!!!!
      • 调度算法
        • 一、考虑 【==周转时间==】性能指标:
        • 二、考虑【==响应时间==】指标:
        • 三、==综合考虑【周转时间】+【响应时间】==?
          • 多级反馈队列的基本原则如上,那么如何针对细节制定对应规则?
        • 四、考虑【==公平性==】
      • 多处理器调度
        • 两种局部性原理
          • 时间局部性
          • 空间局部性
        • 多CPU处理器的缓存一致性(cache coherence)问题
    • 1.2 内存的虚拟化
  • 2.并发
  • 3.持久性

1.虚拟化

1.1 虚拟化cpu


进程和程序

进程: 跑起来的程序、加载进内存的程序、动态的概念。
程序:代码指令、放在硬盘里、静态概念。


如何制造出多个CPU的假象?

计算机cpu通过时分共享, 让一个进程只运行一个较小的时间片,然后切换到其他进程上,从而提供了多个虚拟cpu的假象。

这么做好处在:
可以多并发,可以让多个用户看起来都在==近乎同时==使用电脑,增强用户体验。即基本无延时体验。
但是,cpu共享逃不脱地让每个进程运行都慢一点。仔细想想,为什么?

要想实现进程间的切换需要一些底层机制,例如:上下文切换…


进程相关的方法

	1.创建create
	2.销毁destroy
	3.等待wait
	4.其他控制
	5.状态status

进程创建过程

1.将磁盘中的【代码】+【静态数据】load进内存中,加载到进程的地址空间中。
	注意:现代操作系统是【惰性】执行Load过程:仅在程序执行期间需要加载的代码或数据片才会加载。
2.分配栈和堆内存;
3.分配与输入输出相关的任务:进程的三个文件描述符,标准输入、输出和错误。

创建后的,进程【启动】。


进程API

  1. fork()
功能:几乎克隆父进程
不同:返回值;父进程:返回子进程的PID;子进程:返回0
注意:子进程先运行完还是父进程先运行完,要看本身和OS的调度。
  1. wait()
功能:阻塞当前进程
  1. exec()
功能:让子进程执行与父进程不同的程序。
不同:他只是将父进程中的代码和静态数据覆盖为自己的,堆和栈也会重新初始化;其实是并没有创建新进程,而是直接运行当前的进程。

进程状态模型

  1. 运行running
  2. 就绪ready
  3. 阻塞blocked
  4. 初始态initial
  5. 僵尸态

只有就绪和运行能相互可达:

就绪------调度--------> 运行
就绪<----时间片到-------就绪

举个例子:
Process0 需要从磁盘读取数据或者等待网络数据包,进程就会被blocked; OS发现Procees0不使用CPU并开始Process1;当Process1运行时,I/O完成,那么请问OS要切换回process0么?这涉及到什么?

OS的进程调度算法

如何不增加OS开销而实现虚拟化CPU?如何有效运行进程同时操作系统也要自己保留对CPU的控制权力?!

答案是 【受限直接执行机制

为什么要保持操作系统控制权?不放开权限给用户们的应用程序?

因为操作系统负责资源管理,如果没有控制权,一个进程可以简单地无限制运行接管机器,或访问没有权限访问的信息。这不就乱套了。
因此确保应用程序不做任何我们不希望他做的事,同时仍高效地运行它。

拆分概念

  1. 直接执行:在cpu上直接运行程序
  2. 受限:对硬件资源的访问权限时受限的;因此诞生了两种工作模式!

用户模式:用户态受限访问硬件资源。比如,不能发出I/O请求。

内核模式:内核态可访问全部资源。


用户态不能发出I/0请求,那如果用户想读取磁盘中的内容怎么办?

trap】陷阱指令:该指令使得可以跳入内核,并将特权级别提升到内核态。
完成后,OS调用【return-from-trap】指令,返回到发起调用的用户程序中,同时将特权级别降低,回到用户模式。


陷阱如何知道在OS内运行哪些代码?

内核在启动的时会设置【陷阱表】.


上面我们知道进程是受限执行的,上述通过系统调用从而使得 控制权转交给 操作系统的方式是协作方式。如果没有系统调用,没有任何非法操作,那么操作系统能夺回控制权么?

答案是【不能
这就是协作方式的问题,这种条件下只有一种方式:
重启计算机。


上述如何破解?-时钟中断!

时钟每隔几毫秒产生中断,操作系统重新获得CPU控制权,然后调度算法决定接下来跑哪些应用进程。


调度算法考虑的指标!重要!!!!

  1. 周转时间:T周转时间= T完成时间−T到达时间
  2. 公平 fairness:确保每个工作获得一定比例的CPU时间,而不是优化周转时间和响应时间。
  3. 响应时间:T响应时间= T首次运行−T到达时间

调度算法

一、考虑 【周转时间】性能指标:
  1. 先进先出(First In First Out或FIFO): 先到先得CPU,哪怕先来的时间运行时间很长,比如100秒;B,C任务运行时间尽管只需要10s,也必须等100s。
  2. 最短任务优先(Shortest Job First,SJF):为了解决上面的问题,可以先处理最短的任务。平均周转时间会变短;
    • 事实上,考虑到所有工作同时到达的假设,我们可以证明SJF确实是一个最优(optimal)调度算法。但是,实际上任务不是同时到达的…
    • SJF是非抢占的
  3. 最短完成时间优先(STCF)
    • 抢占式:每当新工作进入系统时,它就会确定剩余工作和新工作中,谁的剩余时间最少,然后调度该工作。
二、考虑【响应时间】指标:

以上的算法有个缺点,任务到来后必须要等待一段未知的运行时间。想想,你跑个程序,却要等别人跑一段时间后才轮到你,这互动性有多差。所有有以下算法,

  • 轮转(Round-Robin,RR)调度

基本思想很简单:RR在一个时间片(time slice,有时称为调度量子,scheduling quantum)内运行一个工作,然后切换到运行队列中的下一个任务,而不是运行一个任务直到结束。它反复执行,直到所有任务完成。因此,RR有时被称为时间切片(time-slicing)。请注意,时间片长度必须是时钟中断周期的倍数。因此,如果时钟中断是每10ms中断一次,则时间片可以是10ms、20ms或10ms的任何其他倍数。

  • 不足:轮转尽管降低了响应时间,但是直观上是推延了每个任务的结束时间,那么一般来说【周转时间】性能指标会很糟糕!

那么如何优化?

三、综合考虑【周转时间】+【响应时间】
  • 多级反馈队列 = 优先级 + 轮转
    • MLFQ中有许多独立的队列(queue)
    • 每个队列有不同的优先级(prioritylevel)
    • 任何时刻,一个工作只能存在于一个队列中。
    • MLFQ总是优先执行较高优先级的工作(即在较高级队列中的工作)。当然,每个队列中可能会有多个工作,因此具有同样的优先级。在这种情况下,我们就对这些工作采用轮转调度。

多级反馈队列的基本原则如上,那么如何针对细节制定对应规则?

● 规则1:如果A的优先级 > B的优先级,运行A(不运行B)。
● 规则2:如果A的优先级 = B的优先级,轮转运行A和B。
● 规则3:工作进入系统时,放在最高优先级(最上层队列)。
● 规则 4:一旦工作用完了其在某一层中的时间配额(无论中间主动放弃了多少次CPU),就降低其优先级(移入低一级队列)。
● 规则5:经过一段时间S,就将系统中所有工作重新加入最高优先级队列。

思考下,为什么要规则5?

设想下:
一个长工作,cpu密集型工作,在消耗几个时间片之后,优先级会降级再降低,但是假如仍旧还有很久的剩余时间才能完成,那么怎么办?那不得饿死?…

因此好处如下:

  • 首先,进程不会饿死——在最高优先级队列中,它会以轮转的方式,与其他高优先级工作分享CPU,从而最终获得执行。
  • 其次,如果一个CPU密集型工作变成了交互型,当它优先级提升时,调度程序会正确对待它。
四、考虑【公平性
  • 彩票调度算法

    1. 彩票数,(ticket)代表了进程(或用户或其他)占有某个资源的份额。
    2. 通过不断定时地(比如,每个时间片)抽取彩票,彩票调度从概率上(但不是确定的)获得这种份额比例。
    	步骤:
    	1. 分配每个任务彩票数:A:100 B:50 D:250
    	2. 生成随机数,比如300
    	3. 设置一个柜面变量:counter = 0 
    	4. 遍历任务列表,coutner += 任务X的tickets,只有counter >= 300者,中奖!跑它!
    
    1. 彩票调度还提供了一些机制,以不同且有效的方式来调度彩票。
      • 彩票货币(ticket currency)

        这种方式允许拥有一组彩票的用户以他们喜欢的某种货币,将彩票分给自己的不同工作。之后操作系统再自动将这种货币兑换为正确的全局彩票。

      • 彩票转让(ticket transfer)

        通过转让,一个进程可以临时将自己的彩票交给另一个进程。
        这种机制在客户端/服务端交互的场景中尤其有用,在这种场景中,客户端进程向服务端发送消息,请求其按自己的需求执行工作,为了加速服务端的执行,客户端可以将自己的彩票转让给服务端,从而尽可能加速服务端执行自己请求的速度。
        服务端执行结束后会将这部分彩票归还给客户端。

      • 彩票通胀(ticket inflation)

        利用通胀,一个进程可以临时提升或降低自己拥有的彩票数量。当然在竞争环境中,进程之间互相不信任,这种机制就没什么意义。一个贪婪的进程可能给自己非常多的彩票,从而接管机器。但是,通胀可以用于进程之间相互信任的环境。在这种情况下,如果一个进程知道它需要更多CPU时间,就可以增加自己的彩票,从而将自己的需求告知操作系统,这一切不需要与任何其他进程通信。

  • 缺点:

    • 不能很好地适合I/O;
    • 票数分配问题

多处理器调度

  • 单CPU系统内存缓存结构

在单CPU系统中,存在多级的硬件缓存(hardware cache),一般来说会让处理器更快地执行程序。缓存是很小但很快的存储设备,通常拥有内存中最热的数据的备份。相比之下,内存很大且拥有所有的数据,但访问速度较慢。通过将频繁访问的数据放在缓存中,系统似乎拥有又大又快的内存。

两种局部性原理

程序第一次读取数据时,数据在内存中,因此需要花费较长的时间(可能数十或数百纳秒)。处理器判断该数据很可能会被再次使用,因此将其放入CPU缓存中。如果之后程序再次需要使用同样的数据,CPU会先查找缓存。因为在缓存中找到了数据,所以取数据快得多(比如几纳秒),程序也就运行更快。

缓存是基于局部性(locality)的概念,局部性有两种,即时间局部性和空间局部性。

时间局部性
  • 举例:for 循环中的i

时间局部性是指当一个数据被访问后,它很有可能会在不久的将来被再次访问,比如循环代码中的数据或指令本身。

空间局部性
  • 举例:数组

空间局部性指的是,当程序访问地址为x的数据时,很有可能会紧接着访问x周围的数据,比如遍历数组或指令的顺序执行。
由于这两种局部性存在于大多数的程序中,硬件系统可以很好地预测哪些数据可以放入缓存,从而运行得很好。

多CPU处理器的缓存一致性(cache coherence)问题

核心冲突在:cpu修改之后的内容如果不及时写回内存,只放缓存。

例如,假设一个运行在CPU 1上的程序从内存地址A读取数据。由于不在CPU 1的缓存中,所以系统直接访问内存,得到值D。
程序然后修改了地址A处的值,只是将它的缓存更新为新值D’。将数据写回内存比较慢,因此系统(通常)会稍后再做。
假设这时操作系统中断了该程序的运行,并将其交给CPU 2,重新读取地址A的数据,由于CPU 2的缓存中并没有该数据,所以会直接从内存中读取,得到了旧值D,而不是正确的值D’。


1.2 内存的虚拟化

2.并发

3.持久性

你可能感兴趣的:(笔记,操作系统,操作系统)