操作系统的特征:
操作系统的接口:
操作系统的基本类型:
中断处理过程:
子过程调用:
系统调用过程:
微内核:
进程:
进程状态:运行态;就绪态;阻塞态;挂起态;
进程控制:
进程通信:
处理器调度:
进程互斥同步:
线程的实现方式:
用户级线程:线程管理由应用程序完成,对内核透明;
内核级线程:线程管理由内核完成,应用程序只有一个到内核级线程的接口,内核为进程及内部每个线程维护上下文;
多线程模型:
死锁:
死锁预防:(破坏4个必要条件其中之一即可)
死锁避免:在系统进行资源分配之前,先计算此次分配的安全性,若此次分配会进入不安全状态(可能会死锁),则阻塞进程;
死锁检测和解除:
死锁定理:系统为死锁状态的充要条件为此时资源分配图是不可完全简化的;
简化资源分配图可以检测系统状态是否为死锁状态:
1)找到资源分配图中既不阻塞(系统有足够的空闲资源分配给它)又不孤立的进程Pi;
2)消去它所有的请求边和分配边;
3)重复1),2),直到找不到可以消除的进程Pi;
4)当且仅当所有进程都是孤立点时(完全简化),系统不处于死锁状态;
死锁解除的主要方法:
程序的链接和装入:
内存管理方式:
虚拟内存管理:
文件系统层次结构:
文件基本操作:
文件打开表:
文件控制块:
文件共享:
文件保护:口令保护;加密保护;访问控制;
文件磁盘块分配方式:
文件空闲块管理:
磁盘管理:
磁盘高速缓存:
缓冲区技术:
I/O控制方式:程序直接控制方式;中断驱动方式;DMA方式;通道方式;
I/O子系统的层次结构:
设备分配:
SPOOLing技术:
各种处理器调度算法的执行过程(甘特图)以及周转时间等指标的相关计算;
利用软件实现方法实现进程互斥:
单标志法:
process Pi{
while(turn!=i);
critical section;
turn = j;
remainder section;
}
双标志先检查法:
process Pi{
while(flag[j]);
flag[i] = TRUE;
critical section;
flag[i] = FALSE;
remainder section;
}
双标志后检查法:
process Pi{
flag[i] = TRUE;
while(flag[j]);
critical section;
flag[i] = FALSE;
remainder section;
}
Peterson算法:
process Pi{
flag[i] = TRUE;
turn = j;
while(flag[j]&&turn==j);
critical section;
flag[i] = FALSE;
remainder section;
}
利用硬件实现方法实现进程互斥:
硬件方法:也称低级方法,或称元方法;
硬件方法的优点:适合于任意数目的进程,支持进程内多个临界区,简单易用;
硬件方法的缺点:不能实现让权等待,由于随机选择等待进程,有的倒霉进程因此可能导致饥饿现象;
中断屏蔽方法:
T e s t A n d S e t TestAndSet TestAndSet指令法:
bool TestandSet(bool &lock){
bool old = lock;
lock = true;
return old;
}
process Pi{
while(TestandSet(&lock));
critical section;
lock = false;
remainder section;
}
S w a p Swap Swap指令法:
Swap(bool &a,bool &b){
bool tmp = a;
a = b;
b = tmp;
}
process Pi{
key = true;
while(key!=false)
Swap(&lock,&key);
critical section;
lock = false;
remainder section;
}
利用信号量解决进程互斥同步:
P P P操作 / w a i t wait wait操作:
void wait(semaphore S){
S.value--;
if(S.value < 0){
add this process to S.L;
block(S.L);
}
}
V V V操作 / s i g n a l signal signal操作:
void signal(semaphore S){
S.value++;
if(S.value <= 0){
remove a process P from S.L;
wakeup(P);
}
}
进程同步:
进程互斥:
前驱关系:
利用管程解决进程同步/进程互斥等问题:
monitor Demo{
share S;
condition c;
init_code(){
S = n;
}
take_away(){
if(S<=0)
c.wait();
several taking operations on S;
S--;
}
give_back(){
several returning operations on S;
S++;
if(process is waiting)
c.signal();
}
}
经典进程同步互斥问题:
生产者-消费者问题:
问题描述:一组生产者进程和一组消费者进程共享一个初始为空、大小为n的缓冲区,只有缓冲区不满时生产者才能把消息放入缓冲区,否则等待;只有缓冲区不空时,消费者才能从中取出消息,否则等待;缓冲区是临界区,一次只允许一个生产者或者一个消费者操作;
并发代码:
semaphore mutex = 1; // 临界区互斥信号量
semaphore empty = n; // 缓冲区空闲资源,生产者消耗这个资源,消费者归还这个资源
semaphore full = 0; // 缓冲区满资源,生产者归还这个资源,消费者消耗这个资源
// 生产者进程
process producer(){
while(1){
produce(); // 生产数据
P(empty); // 获取空闲资源
P(mutex); put(); V(mutex); // 互斥访问,把数据放入缓冲区
V(full); // 归还满资源
}
}
// 消费者进程
process consumer(){
while(1){
P(full); // 获取满资源
P(mutex); get(); V(mutex); // 互斥访问,把数据从缓冲区取出
V(empty); // 归还空闲资源
consume(); // 消费数据
}
}
读者-写者问题:
问题描述:一组读者进程和一组写者进程共享一个文件,读者和读者可以同时访问,写者只有在没有读者读且没有其他写者写时,才能访问文件;
并发代码:
读者优先版:只要有一个读者访问文件,其他读者可以纷至沓来,写者可能饥饿;
semaphore rw = 1; // 互斥读者写者、写者写者访问文件
int count = 0; // 记录正在访问文件的读者数量
semaphore cmutex = 1; // 互斥对count值的操作
// 写者进程
process writer(){
while(1){
P(rw); // 获取文件访问权力,有其他写者或者读者先进入则阻塞自己,否则阻塞其他写者或读者
write(); // 写文件
V(rw); // 归还文件访问权力
}
}
// 读者进程
process reader(){
while(1){
P(cmutex); // 互斥操作count
if(count == 0) // 若自己是第一个进入的读者,则自任“管理员”
// 若之前有写者,阻塞等待;当写者退出后,占据文件访问权力,
// 后续写者会阻塞,读者不会,即只允许且无限允许读者进入,直到所有读者退出
P(rw);
count++; // 读之前,读者数量+1
V(cmutex);
read(); // 读文件
P(cmutex); // 互斥操作count
count++; // 读完后,读者数量-1
if(count == 0) // 若自己是最后一个退出的读者,则自任“管理员”
// 归还文件访问权力,允许后续写者或读者进入
V(rw);
V(cmutex);
}
}
读写公平版:写者要求访问文件时,阻塞后续的读者或写者到同一个队列,直到该写者退出,然后按队列先后次序接纳读者或写者;
semaphore rw = 1; // 互斥读者写者、写者写者访问文件
int count = 0; // 记录正在访问文件的读者数量
semaphore cmutex = 1; // 互斥对count值的操作
semaphore q = 1; // 读者写者共同排队的阻塞队列
// 写者进程
process writer(){
while(1){
P(q); // 入队,看有没有其他读者或者写者排在前面,若有则等待
P(rw); // 获取文件访问权力,有其他写者或者读者先进入则阻塞自己,否则阻塞其他写者或读者
write(); // 写文件
V(rw); // 归还文件访问权力
V(q); // 出队,后续读者写者按顺序访问
}
}
// 读者进程
process reader(){
while(1){
// 入队,看有没有其他读者或者写者排在前面
// 若是写者则等其写完再进,若是读者则等其检查”管理员“身份之后放自己一起进入
P(q);
P(cmutex); // 互斥操作count
if(count == 0) // 若自己是第一个进入的读者,则自任“管理员”
// 若之前有写者,阻塞等待;当写者退出后,占据文件访问权力,
// 后续写者会阻塞在rw队列,等待已进入的读者全退出,读者不会,可以同时进入
// 但是若后续写者w在后续若干读者之前,这些读者会阻塞在q队列,等待w写完
P(rw);
count++; // 读之前,读者数量+1
V(cmutex);
V(q); // 出队,后续被阻塞在q队列的第一个写者之前的读者可以一起进入
read(); // 读文件
P(cmutex); // 互斥操作count
count++; // 读完后,读者数量-1
if(count == 0) // 若自己是最后一个退出的读者,则自任“管理员”
// 归还文件访问权力,允许后续写者或读者进入
V(rw);
V(cmutex);
}
}
写者优先版:写者要求访问文件时,分别阻塞后续的读者写者到不同队列,写者可以依次排队进入,但只有所有写者访问完后才打开读者阻塞队列,让读者依次进入,直到又一个写者到来,再次关闭读者阻塞队列;
int wcount = 0; // 记录正在排队或者访问文件的写者数量
int rcount = 0; // 记录正在访问文件的读者数量
semaphore wmutex = 1; // 互斥对wcount的操作
semaphore rmutex = 1; // 互斥对rcount的操作
semaphore rw = 1; // 互斥读者写者、写者写者访问文件
semaphore rq = 1; // 读者阻塞队列
// 写者进程
process writer(){
while(1){
P(wmutex); // 互斥操作wcount
if(wcount == 0) // 若前面没有其他写者在排队或已进入,则自任“管理员”
// 关闭读者阻塞队列,此时后续读者只能排队,直到没有写者在访问或者排队才能开放队列
P(rq);
wcount++; // 写者排队或进入数量+1
V(wmutex);
P(rw); // 获取文件访问权力,有其他写者或者读者先进入则阻塞自己,否则阻塞其他写者或读者
write(); // 写文件
V(rw); // 归还文件访问权力
P(wmutex); // 互斥操作wcount
wcount--; // 写者排队或进入数量-1
if(wcount == 0) // 若自己退出后暂无其他写者排队,则自任“管理员”
// 开放读者阻塞队列,此时后续读者可以排队进入,直到下一个写者来任“管理员”关闭队列
V(rq);
V(wmutex);
}
}
// 读者进程
process reader(){
while(1){
P(rq); // 入队,看看有没有其他读者排在前面,或者此通道已经被前面的写者关闭
// 此时确定没有写者在自己之前排队或者访问文件
P(rmutex); // 互斥操作rcount
if(rcount == 0) // 若自己是第一个进入的读者,则自任“管理员”
// 后续连续到达的读者可以同时进入,直到下一个写者w到达
// w会阻塞在rw队列,等待已进入的读者全退出才能进入,同时关闭rq队列
// w之后到来的读者只能等再次没有写者在排队或访问文件后,才能进入
P(rw);
rcount++; // 读之前,读者数量+1
V(rmutex);
V(rq); // 出队,让下一个写者w到来前的后续读者一起进入
read(); // 读文件
P(rmutex); // 互斥操作rcount
rcount--; // 读之后,读者数量-1
if(rcount == 0) // 若自己是最后一个离开的读者,则自任“管理员”
// 归还文件访问权力,允许后续写者或读者进入
V(rw);
V(rmutex);
}
}
哲学家就餐问题:
问题描述:一张圆桌边围坐着5个哲学家,每两个哲学家之间有一根筷子,圆桌中间是米饭,每个哲学家每次思考之后需要进餐,此时需要使用左右两根筷子,就餐后放下该两根筷子继续思考;
并发代码:
方案①:至多允许4个哲学家同时入座;
semaphore chopsticks[5] = {1,1,1,1,1}; // 5根筷子资源
semaphore seat = 4; // 4个座位资源,至多允许4个哲学家入座
// 哲学家i进程
process Pi(){
while(1){
think(); // 思考,此时相邻哲学家才可能进餐
P(seat); // 获取座位资源
P(chopsticks[i]); // 拿左边筷子
P(chopsticks[(i+1)%5]); // 拿右边筷子
eat(); // 进餐
V(chopsticks[i]); // 放下左边筷子
V(chopsticks[(i+1)%5]); // 放下右边筷子
V(seat); // 归还座位资源
}
}
方案②:仅当一个哲学家左右筷子都可用且没有其他哲学家也准备拿筷子时,才允许拿筷子;
semaphore chopsticks[5] = {1,1,1,1,1}; // 5根筷子资源
semaphore mutex = 1; // 互斥哲学家拿筷子权力
// 哲学家i进程
process Pi(){
while(1){
think(); // 思考,此时相邻哲学家才可能进餐
P(mutex); // 获取拿筷子权力
// 拿左边筷子,若已经被拿,说明左邻哲学家正在进餐,此时其余4个哲学家或在思考或被阻塞
P(chopsticks[i]);
// 拿右边筷子,同上理
P(chopsticks[(i+1)%5]);
V(mutex); // 归还拿筷子权力,此时哲学家i同时得到了两根筷子
eat(); // 进餐
V(chopsticks[i]); // 放下左边筷子
V(chopsticks[(i+1)%5]); // 放下右边筷子
}
}
方案③:奇数号哲学家先拿左筷子后拿右筷子,偶数号哲学家相反;
semaphore chopsticks[5] = {1,1,1,1,1}; // 5根筷子资源
// 哲学家i进程
process Pi(){
while(1){
think(); // 思考,此时相邻哲学家才可能进餐
if(i%2){ // 奇数号哲学家
// 先拿左边筷子
P(chopsticks[i]);
// 再拿右边筷子
P(chopsticks[(i+1)%5]);
}
else{ // 偶数号哲学家
// 先拿右边筷子
P(chopsticks[(i+1)%5]);
// 再拿左边筷子
P(chopsticks[i]);
}
eat(); // 进餐
V(chopsticks[i]); // 放下左边筷子
V(chopsticks[(i+1)%5]); // 放下右边筷子
}
}
吸烟者问题:
问题描述:有三个抽烟者和一个供应者,总共三种材料:烟草、纸和胶水,每个抽烟者各自无限拥有其中一种资源,但没有任何另外两种资源,供应者无限随机生产其中两种资源并放在桌子上,并等待缺乏该两种资源的抽烟者取走并抽完烟,再开始生产下一轮;
并发代码:
semaphore material[3] = {0,0,0}; // 三种材料的资源量,初始均为0
semaphore finish = 0; // 取走材料并抽完烟资源,初始为0
// 供应者进程
process offer(){
while(1){
rand = rand()%3; // 随机选择两种材料生产
if(rand == 0){
offer(1); offer(2); // 生产1,2号材料
V(material[0]); // 允许需要1,2号材料的抽烟者前来抽烟
}
else if(rand == 1){
offer(0); offer(2); // 生产0,2号材料
V(material[1]); // 允许需要0,2号材料的抽烟者前来抽烟
}
else if(rand == 2){
offer(0); offer(1); // 生产0,1号材料
V(material[2]); // 允许需要0,1号材料的抽烟者前来抽烟
}
P(finish); // 等待相应抽烟者取走材料并抽完烟
}
}
// 抽烟者i进程
process smoker_i(){
while(1){
P(material[i]); // 判断是否刚好有需要的两种材料
get(); smoke(); // 取走材料、卷成烟并抽完
V(finish); // 提示供应者可以继续供应下一批材料
}
}
利用银行家算法进行死锁避免:
1)检查请求向量是否不大于请求进程Pi的需求向量和可利用资源向量;
2)检查成功后预分配,然后执行安全性算法,判断此次分配是否会让系统进入不安全状态;
3)安全性算法:
① 从所有进程的需求向量中选择一个不大于可利用资源向量;
② 将其对应进程加入安全序列,删去该进程,并且将可资源向量加上该进程的已分配向量;
③ 重复①、②,直到所有进程都加入安全序列(此时判断处于安全状态),或者无法找到可加入安全序列的进程 (此时判断处于不安全状态);
计算必不会发生死锁的最少资源数/最大进程数:
动态分区分配策略:
分页/分段/段页式管理方式下逻辑地址到物理地址的转换过程;
请求分页虚拟管理方式下虚实地址的转换过程;
需要几级页表的计算:
页面置换算法:
最佳置换算法:选择以后最长时间不再使用的页面淘汰;可以保证最低的缺页率
FIFO置换算法:优先淘汰最早进入内存的页面;(可能会出现Belady现象:缺页次数随着分配给进程的页框个数增加而增加)
LRU置换算法:选择最近最长时间没被访问的页面进行淘汰,需要每个页面设置一个访问字段,标识上次被访问的时间戳;
CLOCK置换算法:
1)将页面按照访问顺序构成环,每一个新调入的页面的访问位A的初始值为1;
2)时针从上一次换出的下一个页面开始转动,直到找到访问位A=0的页面,进行替换;
3)每当遇到一个访问位A=1的页面,则将其访问位置0;
4)最多转过一圈,则所有页面的访问位均置0,因此重复2即可;
改进CLOCK置换算法:
1)将页面按照访问顺序构成环,每一个新调入的页面的访问位A的初始值为1,修改位M的初始值为0;
2)时针从上一次换出的下一个页面开始转动,寻找(A,M)=(0,0)的页面,进行替换(此过程不修改访问位);
3)若转过一圈仍没有(A,M)=(0,0)的页面,则寻找访问位(A,M)=(0,1)的页面,进行替换(此过程需要将跳过的访问位置0);
4)若转过一圈仍没有(A,M)=(0,1)的页面,则由于上一圈已经将所有的访问位置0,因此一定有(A,M)=(0,0)或(0,1)的页面,因此重复2,有必要再重复3即可;
页面分配策略:
索引结点相关计算:
位示图法空闲块管理方式地址转换:
空闲块成组链接法查找过程;
磁盘读写时间计算:
磁盘物理地址转换:
磁盘调度算法:
缓冲区机制下磁盘I/O操作的时间计算: