《Operating System —— Three Easy Pieces》笔记

1 概述

  《Operating System —— Three Easy Pieces》,相对简单的操作系统入门书,讲述3个方面:
  1)虚拟化:虚拟CPU,单个CPU使用像在运行多个CPU;虚拟存储器,各个程序像有私有的内存空间。
  2)并发:并发需要处理的问题。
  3)持久化:文件存储。
  操作系统的设计目标:
  1)抽象:方便使用,不需关注太多细节。
  2)高性能
  3)提供保护

2 虚拟化

2.1 虚拟CPU

  目标:同时运行多个程序,造成多个CPU可用的假象。
  方法:时分共享,一个进程运行一定的时间,然后切换给另一个进程。

2.1.1 进程

(1)抽象
  进程:正在执行中的程序
  创建进程,操作系统要为进程分配内存(分段、分页)、堆(malloc、free)、栈(局部变量、函数参数、返回地址等)等。
  进程3种基本状态:
在这里插入图片描述
  其他状态:
    初始状态:表示进程在创建时处于的状态
    最终状态:已退出但尚未清理
  操作系统调度进程,会进行上下文切换,为追踪进程,通过进程列表描述。例如struct proc。
(2)API
  fork:复制当前进程,
  返回值:
    父进程——子进程PID
    子进程——0
  wait:使用fork后,父进程调用wait,等待子进程结束
  exec:新进程替换当前进程。
  fork、exec设计原因:例如,shell可在fork之后,exec之前运行,进行某些操作。

wc p3.c > newfile.txt

  shell调用exec前,关闭标准输入,打开newfile.ext,之后将wc结果输入到该文件
在这里插入图片描述

2.1.2 Limited Direct Execution(受限直接执行)

  虚拟化考虑问题:
  1)性能
  2)控制权:有效运行进程,保留操作系统对CPU的控制权
  受限直接执行思想:直接创建进程,运行程序
  存在问题:
  1)问题:没有限制,用户可能随意操作。
  用户模式—>trap指令—>内核模式,限制用户行为
  2)问题2:trap如何知道OS内运行哪些代码
  异常向量表
  3)问题:进程切换
  两种方式:
    协作方式:等待系统调用
    非协作方式:由操作系统控制,时钟中断
  4)并发
  禁止中断:可能导致丢失中断
  加锁

2.1.3 进程调度

2.1.3.1 简介

(1)评价指标
  周转时间=完成时间-到达时间
  响应时间=首次运行时间-到达时间
(2)策略

策略内容优缺点
FIFO先到先执行护航效应,耗时长的任务占据大量时间,耗时短的得不到执行
SJF最短任务优先护航效应
STCF最短完成时间优先,添加抢占响应时间差
RR轮转,分配时间片优化响应时间,周转时间差

  IO问题:一个程序在执行IO操作时,可能耗时较长,此时CPU应调度其他任务。

2.1.3.2 多级反馈队列MLFQ

(1)目标:
  1)优化周转时间
  2)降低响应时间
(2)基本流程
  有多个不同优先级的队列,优先执行较高优先级队列,例
在这里插入图片描述

(3)基本规则
  1)如果A优先级>B优先级,运行A
  2)如果A优先级=B优先级,轮转A、B
  3)进入系统,首先放在最高优先级
  4)一旦进程用完了某一层中的时间配额,降低优先级
  5)经过一段时间S,系统所有进程重新加入最高优先级

2.1.3.3 比例份额

  目标:确保每个工作获得一定比例的CPU时间
(1)彩票调度
  彩票数代表了进程占据的比例。
(2)步长调度
  通过一个大数除以票数,得到每个进程的步长
  未被广泛应用:不能很好适应IO;票数分配难确定

2.1.3.4 多处理器调度

(1)多处理器架构
在这里插入图片描述
  缓存:基于时间局部性和空间局部性
  问题:
  1)缓存一致性——总线探针,缓存一致性协议
  2)同步——加锁
  3)缓存亲和度:进程在某个CPU上运行,下次再同一CPU上运行,因缓存中的数据可执行更快。
(2)单队列调度
  将原有策略用于多个CPU
  问题:
  1)缺乏可扩展性:加锁确保原子性
  2)缓存亲和性
(3)多队列调度
  包含多个调度队列,每个CPU调度之间相互独立。例如
在这里插入图片描述
  问题:负载不均
  解决:不断迁移

2.1.3.5 其他

  linux调度:O(1)调度、完全公平调度CFS、BF调度

2.2 虚拟存储器

2.2.1 抽象:地址空间

地址空间
在这里插入图片描述
  虚拟存储器,为每个进程构建一个私有的、可能很大的地址空间的抽象。目标:
  1)透明:程序像拥有自己的地址空间
  2)效率:时间上处理较快,空间上不需要太多额外的内存支持虚拟化
  3)保护

2.2.2 机制:地址转换

  基址加界限机制:地址转换,每次对内存的访问,将虚拟地址转换成物理地址。CPU需要有基址寄存器和界限寄存器。

physical address = virtual address + base

在这里插入图片描述
问题:
  1)进程创建时,要能找到空闲的内存
  2)进程终止时,回收内存
  3)上下文切换时,保存状态
  4)提供异常处理,例如程序越界访问内存,引发异常。
  5)内部碎片:从图中看到,有大量未使用的空间

2.2.3 分段

做法:每个逻辑段一对基址界限寄存器
在这里插入图片描述
问题:外部碎片
解决:空闲列表

2.3.4 分页

(1)基本方法
按照固定长度将内存分片,如
在这里插入图片描述
物理帧号PFN,虚拟页号VPN,页表项PTE。转换
在这里插入图片描述
问题:
  1)页表存储在内存中,查找较慢——TLB
  2)页表太大——多级页表
(2)TLB
将频繁发生的地址转换放在硬件cache中。
问题:
  1)上下文切换时,cache处理:添加地址空间标识符,标识不同进程的使用
  2)替换策略:最近最少使用LRU、随机策略
(3)多级页表
将页表分成页大小,如果整页的页表项无效,完全不分配该页的页表。
在这里插入图片描述

2.3.5 页面交换

交换空间:在磁盘上开辟空间,用于从内存移出的页面

策略内容评价
最优替换策略替换掉最远的将来才会访问的项目难以实现
FIFO替换掉最先进入的项目命中率低
随机随机替换掉一个项目命中率随机
最近最少使用LRU替换掉最长时间没有被使用的项目命中效果较好,实现成本高
近似LRU硬件设置使用位,系统定期清除,替换没有标记的页实现成本低

3 并发

3.1 简介

(1)线程

进程线程
地址空间相互独立同一进程的线程,共享地址空间
状态进程控制块PCB线程控制块TCB

多线程栈
在这里插入图片描述
(2)多线程问题
  共享数据——加锁
  执行顺序——条件变量
  术语:
  临界区:访问共享资源的代码
  竞态条件:多个执行线程同时进入临界区

3.2 锁

(1)基本用法

lock_t mutex;
...
lock(&mutex);
临界区
unlock(&mutex);

(2)如何评价锁效果
  1)完成基本任务
  2)公平性:保证每个线程有公平的机会抢到锁
  3)性能:只有一个锁时的开支;多个锁竞争时的开支
(3)实现方法
1)控制中断

void lock() {
	disableInterrupts();
}

void unlock() {
	enableInterrupts();
}

  问题:调用线程要有特权操作;不支持多处理器;中断丢失;
2)硬件支持
  硬件支持原子执行,例如test-and-set、compare-and-swap、fetch-and-add

int TestAndSet(int *old_ptr, int new) {
	int old = *old_ptr;
	*old_ptr = new;
	return old; 
}

  锁实现

typedef struct __lock_t {
	int flag;
} lock_t;

void init(lock_t *lock) {
	lock->flag = 0;
}

void lock(lock_t *lock) {
	while (TestAndSet(&lock->flag, 1) == 1)
	; // spin-wait (do nothing)
}

void unlock(lock_t *lock) {
	lock->flag = 0;
}

  评价:线程自旋浪费时间,只有fetch-and-add保证所有线程都能抢到锁
3)解决自旋问题
a 自旋时,放弃CPU
  上下文切换成本还是高
b 使用队列:休眠代替自旋

3.3 条件变量和信号量

区别:
  1)条件变量支持广播方式唤醒等待者,而信号机制不支持
  2)条件变量是无状态的,如果唤醒早于等待,则唤醒会丢失;信号机制是有状态的,可以记录唤醒的次数
  3)条件变量一般配合互斥量一起使用,例

int done = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;

void thr_exit() {
	Pthread_mutex_lock(&m);
	done = 1;
	Pthread_cond_signal(&c);
	Pthread_mutex_unlock(&m);
}

void *child(void *arg) {
	printf("child\n");
	thr_exit();
	return NULL;
}

void thr_join() {
	Pthread_mutex_lock(&m);
	while (done == 0)
		Pthread_cond_wait(&c, &m);
	Pthread_mutex_unlock(&m);
}

int main(int argc, char *argv[]) {
	printf("parent: begin\n");
	pthread_t p;
	Pthread_create(&p, NULL, child, NULL);
	thr_join();
	printf("parent: end\n");
	return 0;
}

3.4 常见并发问题

(1)非死锁缺陷
  违反原子性
  违法顺序:访问顺序被打乱
(2)死锁缺陷
条件:
  1)互斥:使用互斥锁,抢到资源的线程会阻止其他线程
  2)拥有并等待:线程有部分资源,但又在等待其他资源
  3)非抢占:已经获得的资源不能被抢占
  4)循环等待:存在一个环路,环路上每个线程拥有下一线程等待的某种资源
预防:
  1)顺序抢锁:所有线程都根据某种顺序获得锁
  2)原子抢锁:当一个线程开始抢锁,它会不被打断地运行至抢锁完成
  3)放弃已有锁:如果不能抢到所有需要的锁,那么就放弃已经抢到的锁,但是会产生活锁问题,即线程间一直互相测试并放弃锁
  4)使用无等待数据结构。

4 持久化

4.1 磁盘驱动器

(1)模型
  将磁盘看做由多个扇区组成,每个扇区大小512字节,图示
在这里插入图片描述
  基本的IO流程:寻道–>等待转动到对应位置–>传输。在此过程中的延时:
  单磁道延时:旋转延时
  多磁道延时:寻道时间
(2)调度
  1)最短寻道时间优先,问题:饥饿
  2)电梯调度
  3)最短定位时间优先:比较旋转和寻道时间

4.2 廉价冗余磁盘阵列RAID

  基本思想:将多个容量较小、相对廉价的磁盘进行有机组合,从而以较低的成本获得与昂贵大容量磁盘相当的容量、性能、可靠性。
  3个重要等级:RAID0(条带化)、RAID1(镜像)、RAID4/5(基于就校验的冗余)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值