现充|junyu33

(已完结)OS笔记

只提供理论部分,供期末复习用。

Computer System Overview

偏计组,考试不考。

Operating System Overview

OS: a program that controls the execution of application programs, and acts as an interface between applications and the computer hardware.

Interrupts: Early computer models did not have this capability. This featuregives the OS more flexibility in relinquishing control to, and regaining control from, user programs.

Process Description and Control

process

交替执行(interleave)、策略(stategy)、死锁(deadlock).

definition:

component:

Threads

Thread: lightweighted process.

Linux has no kernel threads while Windows does, the advantanges of no threads are lightweighted and easy to manage.

Concurrency

terminology

单道程序的特点:

多道程序的特点:

algorithm

dekker

peterson

hardware disabling

disadvantage:

special instruction

优点:进程数目不限、简单易证、支持多临界区。

缺点:忙时等待、饥饿、死锁。

Semaphores

terminology

operation

step

Semaphore(a,b,c)
{
  a = 1;
  b = 2;
  c = 3;
}

void p_1...p_n()
{
  // do sth
}

void main()
{
  Parbegin(p(1),p(2), p3(3), ... p(n));
}

problems

生产者消费者问题:

void producer() {
  while (1) {
    produce();
    semwait(empty);
    semwait(mutex);
    // put item in store
    semsignal(mutex);
    semsignal(product);
  }
}
void consumer() {
  while(1) {
    semwait(product);
    semwait(mutex);
    // take item from store
    semsignal(mutex);
    semsignal(empty);
    use();
  }
}

读者写者问题:

rw,ww之间互斥

rr之间互斥(条件竞争)

读者优先:

Semphore mutex = 1; //文件互斥量
Semphore read_mutex = 1; //读进程计数互斥量

int counter = 0;

void reader() {
  int m;
  semwait(read_mutex);
  counter++;
  semsignal(read_mutex);
  if (counter == 1) semwait(mutex); // the first to read, compete with write
  read();
  semwait(read_mutex);
  counter--;
  semsignal(read_mutex);
  if (counter == 0) semsignal(mutex);
}

void writer() {
  semwait(mutex);
  write();
  semsignal(mutex);
}

读写平等:

Semphore mutex = 1; //文件互斥量
Semphore read_mutex = 1; //读进程计数互斥量
Semphore queue = 1; //读进程与写进程排队互斥量

int readcount = 0;

void reader()
{
  int m = 0;
  semwait(queue);
  semwait(read_mutex);
  readcount++;
  semsignal(read_mutex);
  if (readcount == 1) semwait(mutex)
  semsignal(queue)
  read();
  semwait(read_mutex)
  readcount--;
  semsigal(read_mutex)
  if (readcount == 0) semsignal(mutex)
}
void writer()
{
  semwait(queue);
  semwait(mutex);
  write();
  semsignal(mutex);
  semsignal(queue);
}

Monitors

composition

天生互斥。(同一时间最多只有一个活跃的procedure)

message passing

send(dest, msg)

receive(src, msg)

天生具有同步关系

同步阻塞:你饿了,叫你妈妈做饭,然后你一直等。

同步非阻塞:你饿了,叫你妈妈做饭,你去打游戏,时不时问妈妈饭做好没。

异步阻塞:你饿了,叫你妈妈做饭,你去打游戏,妈妈说饭做好了,你去吃饭。

异步非阻塞:你饿了,叫你妈妈做饭,你去打游戏,妈妈说饭做好了,把饭端给你。

优势:跨主机,可在网络编程使用。

Deadlock & starvation

没有通用有效解决方案。

why deadlock

recourses

requirement

resolve

allow deadlock

死锁检测具体算法:

例:

  1. Allocation中找零向量。
  2. w=v
  3. Request中找小于w的行向量,标识时清零A并更新w向量。
  4. 重复第三步,直到不能再比较(死锁)或者消除完为止(无死锁)。

don't allow

banker's algorithm details:

例:

  • a) 6+2+0+4+1+1+1=15,3+0+1+1+0+1+0=6 将ABCD都验证一遍就行。
  • b) need=CA
  • c) (安全检测算法)用V向量比较CA的某一行,如果大于,V=V+A,直到不能再比较(不安全)或者消除完为止(安全)。
  • d) (银行家算法)把新的请求加到A矩阵里(本题中加到P5变成[4,2,4,4]),然后V向量变成[3,1,2,1],再执行步骤c。

dining philosopher's problem

initial solution (incorrect)

problem: possible deadlock (all five philosophers take one fork)

sem p[i] = 1 from 0 to 4
void philosopher(int i) {
  think();
  wait(p[i]);
  wait(p[(i+1)%5]);
  eat();
  signal(p[(i+1)%5]); // routine
  signal(p[i]);
}

solution1:

sem p[i] = 1 from 0 to 4;
sem root = 4; // only four hilosophers won't have deadlock
void philosopher(int i) {
  think();
  wait(room);
  wait(p[i]);
  wait(p[(i+1)%5]);
  eat();
  signal(p[(i+1)%5]); // routine
  signal(p[i]);
  signal(room);
}

solution2:

sem p[i] = 1 from 0 to 4;
void philosopher(int i) {
  think();
  min = min(i, (i+1)%5); // confines philosopher 0 and 4 (won't eat together)
  max = max(i, (i+1)%5); // so at most 4 procs
  wait(p[min]);
  wait(p[max]);
  eat();
  signal(p[max]); 
  signal(p[min]);
}

Q: What about 2 dynamic algorithms?

(TODO)

设共有m个进程,每个进程最大请求为xi,则最少i=1m(xi1)+1个资源不会导致死锁。

memory management

固定分区

动态分区

产生外部碎片(使用压缩算法解决)

放置算法(最佳适配、首次适配、下次适配)

一个进程在生命周期内占的内存位置可能不相同(压缩和交换技术)

伙伴系统

类似于二叉树

优于固定和动态分区,存在内部碎片

简单分页

def: 页框、页、段

每个进程都有一个页表,页表给出了该进程的每页(逻辑,page)所对应页框(物理,frame)的位置。

逻辑地址包括一个页号和该页中的偏移量。通过页表查询页号的页框号(前6位),与偏移相拼接(10位)得到物理地址。

存在少量内部碎片。

简单分段

与进程页表只维护页框号不同,进程段表维护了长度与基地址。

逻辑地址包括一个段号和偏移量。通过段表查询段号对应基地址(16位),将其与偏移相加(12位)得到物理地址。

存在外部碎片。

内存管理需求

homework:

更正:由于存在一种机制叫重定位(如果 X 曾经就在原来那个位置,之后被 free 掉,之后重新 malloc 会仍然在该位置,而不会靠左),所以 7.6.a 仍然是 8MB,第二题则应更正为[3M, 8M].

Virtual Memory

  • 固定分区,动态分区:全部加载,不分区。
  • 简单分页,简单分段:全部加载,分区。
  • 虚存分页,虚存分段:部分加载,分区。

虚存分页

简单来说就是把一部分内存放到硬盘里,等到需要的时候再加载到内存。

如果读取的页位于磁盘,则产生一个缺页中断阻塞进程,等从磁盘加载到内存后再产生一个中断恢复执行。(因此一次缺页会产生两次中断,如果中断时间大于执行指令的时间就会产生“抖动”)

对于32位4GB内存的分页,操作系统在内存保存一个4KB根页表(每项4字节,共有1024个页表项),该根页表中的每一项对应一个4KB的页表(每项4字节,共有1024个页表项,因此一共有1048576个页表项,共计4MB,可以保留在虚存),这4MB的虚存空间每一项对应一个长度12位的虚存(因此这1048576项就可以映射到4GB的虚存中)。

for pwners: 这就是为什么地址ASLR的后三位不变,ASLR只会按页随机,不会随机页内部的偏移。

将虚拟地址转化为物理地址的方法如下:首先用前10位作为根页表的偏移,查到对应的根页表项,接着判断该根页表项对应的用户页表(4MB那个)是否在内存。如果在,就用接下来10位查找对应的对应的用户页表项,并用该项的页框号和偏移得到物理地址;如果不在,就产生缺页中断。

转换检测缓冲区可以可以缓存部分页号对应的页框号,从而减少中断,提高内存访问速度。

页尺寸与缺页率的关系是先增后减(局部性原理;整个进程都在页当中),页框数和缺页率的关系是单调递减(如果页和页框都一一对应就没有缺页了)

虚存分段

虚拟地址由页号和偏移量组成。转化为物理地址的方式是,页号作为偏移在段表内寻找对应的段号,将该段的基地址与虚拟地址的偏移相加得到物理地址。

OS层面

读取策略

放置策略(同前文放置算法)

决定一个进程块驻留的物理位置。

置换策略

页框锁定:不会被换出(如内核等)

CLK 实现代码:

pageSize = 3
pagePointer = 0
pages = [[0, 0] for i in range(pageSize)] # [page, used]

accessSeq = [2, 3, 2, 1, 5, 2, 4, 5, 3, 2, 5, 2]

def printPages():
    print('pagePointer:', pagePointer)
    for i in range(pageSize):
        print(pages[i][0], pages[i][1])

for newPage in accessSeq:
    hasPage = False
    for i in range(pageSize):
        if pages[i][0] == newPage:
            pages[i][1] = 1
            hasPage = True
            break

    if hasPage == True:
        printPages()
        continue
    
    times = 0
    for i in range(pageSize):
        times += 1
        if pages[pagePointer][1] == 0:
            pages[pagePointer][0] = newPage
            pages[pagePointer][1] = 1
            pagePointer += 1
            pagePointer %= pageSize
            break

        else:
            pages[pagePointer][1] = 0
            pagePointer += 1
            pagePointer %= pageSize

    if times == pageSize:
        pages[pagePointer][0] = newPage
        pages[pagePointer][1] = 1
        pagePointer += 1
        pagePointer %= pageSize

    printPages()

驻留集管理

清除策略

加载控制

影响到驻留在内存中的进程数量(系统并发度)与处理器利用率。

随着并发数量的增加,处理器利用率增加。达到一定程度后,平均驻留集大小不够,中断增加,处理器利用率降低。

processor scheduling

types

algo design

requirements

(non)preemptive: a process can be dispatched when the executing process is not finished.

performance:

fairness & starvation

algos

Real-time scheduling

EDF

earlist deadline first

for non-scheduled

RM

rate monotonic (higher rate/frequency first)

for scheduled

I/O

ALL OPERATIONS ARE NON-PREEMPTIVE!!

buffering

aim: reduce process stuck time to improve efficiency smooth I/O performance.

type

disk scheduling

T=Tseek+Trotation+Ttransfer

transfer (fixed)

Tr=12r

rotation (fixed)

b stands for number of bytes in a sector

Ns stands for number of sectors in a track

B=Nsb stands for number of bytes in a track

Td=bBr=1Nsr

seek

policies:

file

what

abstration to manage and access data.

components

field:

record:

file:

block:

requirements

file operation

basic: create, delete, open, close, read, write

directory

abstraction to organize files

single level

two-level

hierachical

access control

typical levels:

access rights:

in *nix system

  • access modes: Read Write eXecute (RWX)
  • access rights: ower, group, public
  • access control grant: rwxrwxrwx

file organization

criteria:

pile file

easy to insert, difficult to search

sequential file

performance is still low

index sequential

indexed

after an operation, all records need modification

direct file or hashed file

storage management

goal: manage the disk space to support a stream of file operations

two basic tasks:

file allocation

FAT: file allocation table

free space management

复习提纲

2

操作系统的概念,与应用程序的区别 操作系统的功能、目标 操作系统的构成模块,简述功能 操作系统的发展(批处理、分时、实时) 分时系统和批处理系统的概念,差异 中断和异常在分时系统和批处理系统的作用 如何理解操作系统是一个虚拟机

3

为了实现进程,CPU需要提供哪些硬件支持(中断和异常) 什么叫进程,和程序的关系和区别 PCB的概念及功能,描述其构成 从程序生命周期的角度描述程序怎么变成进程 跟进程控制相关的原语有哪些 进程的五状态图(图相关) 从三个角度回答进程的运行(研讨题) 进程的创建流程,并结合Linux的fork(一次调用两次返回) 进程切换的流程和时机(wait

4

线程的概念,进程和线程的区别 线程的三种实现方法 ULT和KLT的优缺点

5

并发需要解决的四个问题(同步、互斥、饥饿、死锁) 为什么引入同步互斥机制(race condition) 有哪些解决方案(软件类、硬件类、系统类(管程和信号量)) 硬件解决的缺点 信号量的概念、本质和实现 管程是什么,引入的原因是什么

6

死锁的概念和产生背景 死锁的条件(三个充分,四个充要) 死锁的解决方法(预防、避免、检测、不解决) 死锁和饥饿的关系

7

存储管理的7个方法 固定分区分页、动态分区分段的异同 段表和页表的构造和使用

8

实现虚拟存储要考虑的三个问题 实现虚拟存储的三个方法(虚拟分段,虚拟分页,段页式) 虚拟存储器 Page Fault 的处理流程 地址转化的计算 请简述缺页率和页尺寸和页框数的关系

9

长中短三个调度的描述(图相关)

10

IObuffer的产生背景和IObuffer的运行方式

12

文件系统的实现方式 逻辑层上有五种实现方式 物理层有三种实现方式

DIY笔记(第9章)

DIY教材:Operating Systems: Three Easy Pieces 的链接:https://pages.cs.wisc.edu/~remzi/OSTEP/

chapter 7: introduction

反应时间——一种新的度量方式

STCF 算法主要从周转时间(turnaround time)方面进行优化,这种方法主要针对于早期批处理系统。但对于当今的分时系统(time-shared)来说,响应时间(response time)才是决定即时性能的重要因素。

响应时间的计算公式如下:

Tresponse=TfirstrunTarrival

假设有三个进程A,B,C同时在t=0时刻进入队列,且每个进程的执行时间均为 5,则使用STCF/SJF的调度情况如下图:

故平均响应时间为:

Tresponse=0+5+33=5

Round Robin

Round-Robin 算法是抢占式(preemptive)算法,可以显著提高各个进程的响应时间。RR不是运行作业完成,而是运行时间片(有时称为调度量子)。值得注意的是,时间片的长度必须是计时器中断周期的倍数,因此,如果计时器每10毫秒中断一次,则时间片可以是10、20或任何其他10ms的倍数。

还是以上一个为例,使用Round-Robin调度的情况如下(单位时间片为1):

故平均响应时间为:

Tresponse=0+1+23=1

效果显著。

优点:

缺点:

考虑 I/O 的情况

结论:如果使用非抢占的调度算法(如STCF),应当将频繁使用I/O的进程的时间块分割开来,以提高资源利用率。

例,AB都是需要 50ms 的进程,但A每 10ms 就要调用一次 I/O,花费 10ms 的时间。

不分割时间块的情况,总用时 140ms:

分割时间块的情况,总用时 100ms:

通过将每个CPU突发作为一个作业来处理,调度程序可以确保“交互式”的进程频繁运行。当这些交互式作业执行I/O时,其他cpu密集型作业会运行,从而更好地利用处理器。

如果不能预知未来又该怎么办

STCF/SJF 算法在真实的操作系统作用有限,因为 OS 对每个进程的先验知识很少,改进方法可能有:

总结

本章我们学习了:

chapter 8:Multi-Level Feedback Queue

MLFQ试图优化的问题

MLFQ的基本概念

MLFQ有许多不同的队列,每个队列分配了不同的优先级。在任何给定的时间,一个准备好的工作运行的作业都在一个队列中,MLFQ使用优先级来决定哪个工作 。

MLFQ的基本规则

MLFQ的具体实现

第一步

作业一进来,会被放置在最高优先级的队列进行运行

第二步

每个队列都设置了一个特定长度的时间片,当该作业耗尽该时间片时,该作业将会被置于下一级队列中

第三步

经过特定的时间后,所有作业都将会被重新放置到最高优先级的队列之中

MLFQ存在问题

MLFQ的总结

chapter 9: proportional share

目的: 保证每个进程能获得一定的百分比的 CPU 时间。

彩票调度

每个进程拥有一些票据(ticket),系统随机选择所有票据中的一个,拥有该票据的进程执行命令。

根据大数定律,随着执行时间的增长,每个进程执行时间的百分比,最终会收敛于拥有票据占总票据的百分比。

基本概念

实现

利用随机数生成器来选择中奖彩票、记录系统中所有进程的数据结构(一个列表)、所有彩票的总数

假定我们用列表记录进程。有 A、B、C 这 3 个进程,在做出调度决策之前,首先要从彩票总数400中选择一个随机数(中奖号码)。假设选择了300。然后遍历链表,用一个简单的计数器帮助我们找到中奖者。

从前向后遍历进程列表,将每张票的值加到 counter 上,直到值超过 winner。 这时,当前的列表元素所对应的进程就是中奖者。例如,中奖彩票是300。首先,计A的票后,counter 增加到 100。因为100小于300,继续遍历。然后 counter 会增加到150(B的彩票),仍然小于300,继续遍历。最后,当counter增加到400(大于300)时,退出遍历,当前指针指向C就是中奖者。

步长调度

取消了随机性,以固定步长代替,是一个确定性的公平分配算法。

步骤

  1. 例如A,B,C的票据分别为100,50,250,这里设其步长为票据的反比(简单的做法是用一个大数分别除以它们),如果这个大数是 10000,那么最后A,B,C的步长分别为100,200,40.
  2. A,B,C的总步数均为0.
  3. 选取这三者中最小的,并加上它的步长。
  4. 重复步骤3.

最后的调度结果如下图:

Linux CFS (选讲)

time_slicek=weightki=0n1weightisched_latency

具体的权重常数参见以下定义:

// the map between nice param to weight
static const int prio_to_weight[40] = {
/* -20 */ 88761, 71755, 56483, 46273, 36291,
/* -15 */ 29154, 23254, 18705, 14949, 11916,
/* -10 */  9548,  7620,  6100,  4904,  3906,
/* -5 */   3121,  2501,  1991,  1586,  1277,
/* 0 */    1024,   820,   655,   526,   423,
/* 5 */     335,   272,   215,   172,   137,
/* 10 */    110,    87,    70,    56,    45,
/* 15 */     36,    29,    23,    18,    15,
};
vruntimei=vruntimei+weight0weightiruntimei

其中weight0的值可以通过查询上述代码得到,为1024.

总结

本章我们学习了: