计算机操作系统理论学习
(持续更新版)
操作系统
作用:管理软硬件资源
启动过程:
按下电源按钮,会向CPU发一个reset信号
BIOS程序存在 ROM 里面
BIOS启动
步骤:
1.上电自检
2.初始化硬件设备
3.搜索一个操作系统来启动,例如:硬盘(硬盘第一个扇区称为主引导记录 MBR [分为引导程序 boot loader] 、分区表)、U盘
4.找到有效设备后,把第一个扇区的内容拷贝到内存中,起始地址是 0x00007c00,然后跳转到这个地址处
PS:异常源自 处理器内部,中断源自处理器芯片外部
陷阱和系统调用
在8086用户模式中 执行syscall 指令就可以调用内核模式的系统调用的功能
中断、异常、陷阱的作用可以切换用户态和内核态,让用户态可以去访问硬盘
在多个进程中,每一个进程都有内核虚拟内存,这样方便在你用户态代码下直接系统调用 转换为内核态,也可以被称为切换进程
进程和线程
进程:就是一个正在运行程序的抽象
在Linux 中 创建进程使用 系统调用 fork() + execve()
进程的创建
什么时候创建?
1.系统初始化(操作系统启动)
2.正在运行的程序执行了创建进程的系统调用 (fork)
3.用户请求创建一个新进程
4.一个批处理作业的初始化
进程状态
进程被创建后,进入就绪状态,等待被调度执行
进程三种状态:运行、就绪、阻塞
进程终止: 正常退出,出错退出,严重错误,被其他竞争kill掉
进程调度算法
1.先来先服务 (弊端:如果在前面的任务比较长的,那他的平均周转时间比较高)
2.最短任务优先(非抢占) 先运行最短任务,然后是次短的任务
3.抢占式最短完成时间优先 每当有新的任务到达时,会确定剩余任务和新任务中,谁的剩余时间最少,然后调度该时间最少的任务
4.高响应比【(等待时间 + 执行时间) / 执行时间】 优先调度算法
看响应比高的 优先执行(看公式判断即可)
交互式系统调度
时间片调度 给固定时间,如果在固定时间内没有执行完,那么就换另一个进程
进程切换(上下文切换)
切换需要干什么?
1.保存当前进程的上下文 (保存通用寄存器、浮点寄存器、程序计数器(PC)、程序状态字、用户栈、内核栈、描述地址空间的页表、当前进程信息的进程表)
2.恢复某个之前被抢占的进程的被保存的上下文
3.将控制传递给这个新恢复的进程
线程
许多应用中,存在许多同时发生的多种活动
线程包实现
1.用户进程管理线程(主要是将线程包放到用户空间中)
2.内核管理线程
3.混合实现 (了解即可)
临界区
预防临界区出现的差池:
1.任何两个进程不能同时处于临界区
2.不应对CPU的速度和数量做任何假设
3.临界区外运行的进程不得阻塞其他进程
4.不得使进程无限期等待进入临界区
解决临界区的办法:
1.屏蔽中断 对于内核态比较方便 原理:不让发生时钟中断 或者 其他中断 不让切换内核态,也就不会切换进程
2.Peterson 算法 原理:将进程数量存在一个数组内,然后让另一个忙等,等在CPU的进程执行完后另一个忙等的才有机会进入执行
3.TSL指令(锁住内存总线) 步骤:读内存字(将内存读入寄存器),写内存字(写入内存)
4.Swap指令上述方法弊端:总是等待
信号量(是一个整形计数器)
定义:用于控制多进程或多线程并发访问共享资源的同步机制
为什么要信号量?
因为在多进程或多线程程序中,共享资源的并发访问可能导致竞争条件,信号量可以协调这个竞争
信号量主要操作: P(用于请求资源) 和 V(用于释放资源)
//整数型信号量
int s = 1void wait(int s)
{while(s <= 0){}//弊端:一直在等待s--;
}
//这个表示 如果当前资源可用,那就占用这一块 s--void signal(int s)
{s++;
}
//最后占用完需要释放掉这个资源
//记录型信号量
typedef struct
{int value;Struct process *L;
}semaphorevoid wait(semaphore S)
{S.value --;if(S.value < 0){block(S.L);//进入阻塞态}
}void signal(semaphore S)
{S.value ++;if(S.value <= 0){wakeup(S.L);}
}
经典同步问题
应用:
1.读者——写者问题
原理:允许多个用户同时查看座位的分配,但是正在预定座位的客户必须拥有对数据库的独占访问
2.哲学家就餐问题
管程 (monitor)
原理:把信号量和操作封装在一个对象的内部(也就是把共享变量共享在一个模块下)
PS:注意:确保每次只有一个进程在管程内处于活动状态
实现方法
需要用条件变量和 wait 和 signal condition x具体流程:
1.当操作 x.wait()调用这个操作的进程会被挂起
2.当操作 x.signal() 才会把这个调用x.wait()的进程停止
如果没有前面的x.wait()这一操作,那么后面的x.signal()就没有什么用了
管程中的wait()和signal()方法 都跟信号量中的不一样!!!!
死锁
定义:指多个进程在运行过程中争取资源而造成一种僵局,当进程处于这种状态时,若无外力作用,这些进程都无法再向前推进
发生死锁的条件:(需要通过同时满足)
1.互斥条件
2.请求和保持条件
3.不可抢占资源 (进程已获得的资源不能被强制性抢占)
4.环路等待资源
死锁检测和恢复
检测的算法实现:
将数据放入链表中,一个个去遍历,如果节点相同证明有环路,所以就检测到死锁的产生
死锁的恢复
1.利用抢占恢复
2.利用回滚时间 (周期性对进程进行检查点检查) 相当于存档,可以分析发生死锁的原因
3.通过杀死进程恢复
安全状态和不安全状态
区分这两个的最主要就是 看进程是否能全部执行完
死锁避免——银行家算法(伪算法)
主要是对请求的满足是否会导致进入不安全状态(不是死锁哦),如果导致不安全状态就拒绝该请求,否则就满足该请求
死锁预防
1.破防互斥条件 比如假脱机打印技术,可以允许多个进程同时产生输出
2.破坏请求和保持 请求发放所有资源,如果一个资源或者多个资源正在被使用,那么就不进行使用,进程进行等待
3.破坏不可抢占条件 通过将设备虚拟化,避免发生混乱
4.破坏环路等待条件 当一个进程请求资源时,先暂停释放其当前占用的全部资源,可以对资源编号,通过对序列判断再决定资源分配
虚拟内存
有一个一开始让我很困惑的问题,就是为什么操作系统需要使用虚拟内存印射到物理内存这一方案?
因为当可用内存完全消耗完时,操作系统可以通过磁盘空间扩展可用内存,也就是将数据交换到硬盘,为新进程提供相应的空间
虚拟空间的大小取决于虚拟空间的位数
将虚拟空间划分以下就叫页
记录虚拟地址和物理地址的关系,就用页表(存在内存中)
虚拟页和物理页大小是一样的
虚拟页已经缓存在内存中叫 cache,不在内存中但在磁盘上分配了就是Uncatched
虚拟页都不在磁盘中的叫 Unallocated
虚拟地址拆分为:
虚拟页号 (剩余bit - 12bit) 虚拟页偏移量(12bit)
因为每个进程会有一张单独的页表,而进程又是数不胜数的,于是为了优化页表在内存中的存储范围,我们采用多级页表以此来减少内存
多级页表结构:
目录号 页号 页偏移量
多级页表的优势:比单级页表更节省空间,因为在单级页表中,你的每个进程独占一个页表,那么有N个进程就有N个页表;而多级页表就可以只有一张表,但是在上面集成了很多很多的页,工作流程就是从初始页表出发,一直不断的迭代去找相应页的位置,直到最后一张页访问完就拿到真实的内容了
