【操作系统】学习

来源于王道计算机考研——操作系统

目录

1.概论

1_1操作系统的概念:

操作系统是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配;以提供给用户和其他软件方便的接口和环境;它是计算机系统中最基本的系统软件。

1_2操作系统的特征:

在这里插入图片描述

1)并发:两个或多个事件在同一个时间间隔内发生。宏观上同时发生,微观上交替发生。区别并行,同时发生

  • 对于单核CPU,同一时刻只能执行一个程序,各个程序只能并发执行。
  • 对于多核CPU,同一时间可以同时执行多个程序,多个程序并行得执行。
    2)共享:即资源共享,是指系统中的资源可供内存中多个并发执行的进程共同使用。
    资源共享方式
    3)虚拟:是指把一个物理上的实体变为若干个逻辑上的对应物。物理实体是实际存在的,而逻辑上对应物是用户感受到的。(空分复用/时分复用)
    4)异步:多道程序环境下,允许多个程序并发执行,但由于资源有限,进程的执行不是一贯到底,而是走走停停,以不可预知的速度向前推进,这就是进程的异步性。

1_3操作系统的运行机制

在这里插入图片描述

区分:指令(二进制机器指令)与命令(shell,对话框),内核(kernel)程序(操作系统的最核心的实现程序)vs应用程序(操作系统之上)
cpu有两种状态:“内核态”和“用户态”
中断是让操作系统内核夺回CPU使用权的唯一途径,让CPU从用户态转化为内核态,进而执行内核中断处理程序。
CPU从内核态转化为用户态,需要操作系统主动交出使用权,通过一条修改PSW(内核态/用户态的标志位)的特权指令,来实现变态。
在这里插入图片描述
linux的系统调用就是通过陷入指令完成的,执行陷入指令,意味着应用程序主动将CPU控制权还给操作系统内核,会触发内部中断。
中断机制的原理:
不同的中断信号需要用不同的中断处理程序来处理,当CPU检测到中断信号后,会根据中断信号的类型去查询“中断向量表”,以此来找到相应的中断处理程序在内存中的存放位置。
在这里插入图片描述

1.4 系统调用

在这里插入图片描述
系统调用是操作系统提供给应用程序(程序员/编程人员)使用的接口,可以理解为一种可供应用程序调用的特殊函数,应用程序可以通过系统调用来请求获得操作系统内核的服务
在这里插入图片描述
不涉及系统调用的库函数:如,“取绝对值”函数。
涉及系统调用的库函数:如“创建一个新文件”的函数。
在这里插入图片描述
凡是与共享资源有关的操作(如存储分配、I/O操作、文件管理等),都必须通过系统调用的方式向操作系统内核提出服务请求,可以保证系统的稳定性和安全性。
在这里插入图片描述

2.进程管理

2_1进程的概念

程序:静态的,存放在磁盘里的可执行文件.exe。
进程:是动态的,是程序的一次执行过程。PID(process ID,进程ID)
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
进程控制块(PCB)
在这里插入图片描述

在这里插入图片描述

2_2进程的状态与状态转化

在这里插入图片描述
在这里插入图片描述
进程的组织——链式方式
在这里插入图片描述

2_3进程控制

原语(具有原子性,不可被中断),关/开中断原语通过开中断指令、关中断指令这两个特权指令实现原子性。
进程控制就是要实现进程的状态转换
在这里插入图片描述

2_4进程之间的通信(IPC)

进程是操作系统资源分配的基本单位,因此,各进程拥有的内存地址空间相互独立。
出于安全考虑,进程只能访问自己的进程地址空间,不能访问另一个进程的地址空间。
映射使用的是mmap系统调用。
在这里插入图片描述
在这里插入图片描述
消息传递,通过send/receive系统调用,实现两个进程之间的通信。
在这里插入图片描述
管道通信与共享内存通信的区别在于,管道是FIFO(先进先出,有限制),而共享内存是把内存空间给两个进程,让他们自己玩,随便他们怎么分配。
管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。
管道通信中的数据一旦被读出,就彻底消失,当多个进程读同一个管道时,可能会错乱。
两个解决方案:1、一个管道允许多个写进程,一个读进程。2、允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据(linux的方案)。

2_5线程

有的进程可能需要“同时”做很多事情,而传统的进程只能串行地执行一系列程序,为此而引入了“线程”。
线程:程序执行流的最小单位。是一个基本的CPU执行单元。(一个进程内并发处理多个线程)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
线程的状态转换 类似于 进程的状态转换

2_6进程的调度

作业:一个具体的任务
当系统内存不足时,操作系统把部分进程从内存调入外存,称为挂起。
在这里插入图片描述

2_7进程调度算法的评价指标

在这里插入图片描述

2_8调度算法

作业调度:将作业从外存调入到内存。
饥饿:一个算法或一个文件长期得不到调度,成为饥饿。
算法1,2,3适用于批处理系统,用户交互性比较差。
在这里插入图片描述

1)先来先服务算法(FCFS,first come first serve)
按照到达的先后顺序调度,事实上就是等待时间越久的越优先得到服务。
优点:公平、算法实现简单
缺点:对于排在长作业(进程)后面的短作业需要等待很长时间,带权周转时间很大,对短作业来说用户体验不好。即FSFS算法对长作业有利,对短作业不利。
不会导致饥饿。
在这里插入图片描述

2)短作业优先(SJF,Shortest Job First)
在这里插入图片描述
追求最少的平均等待时间,最少的平均周转时间、最少的平均带权周转时间。
即可用于作业调度,也可用于进程调度。用于进程调度时,称为“短进程优先(SPF,Shortest Process First)”算法。
非抢占式的短作业优先:每次调度时选择当前已到达且运行时间最短的作业/进程
抢占式的短作业优先:每当有进程进入,就绪队列改变时,就需要调度。如果新到达的进程剩余时间比当前运行的进程剩余时间更短,则新进程抢占处理机当前运行进程重新回到就绪队列。另外,当一个进程完成时也需要调度。
优点:最短的平均等待时间、平均周转时间
缺点:不公平。对短作业有利,对长作业不利。
可能产生饥饿现象。
如果有源源不断的短作业,可能导致长作业长期得不到服务。
3)高响应比优先(HRRN,Highest Response Ratio Next)
在这里插入图片描述
响应比=(已经等待时间+要求服务时间)/要求服务时间
综合考虑作业/进程的等待时间和要求服务的时间
算法规则:在每次调度时先计算各个作业/进程的响应比,选择响应比最高的作业/进程为其服务。
一般为非抢占式的调度算法。
不会导致饥饿。
4)时间片轮转调度算法
在这里插入图片描述
常用于分时操作系统,更注重“响应时间”,因此一般不计算周转时间
算法思想:公平、轮流为各个进程服务,让每个进程在一定时间间隔内都可以得到响应。
算法规则:按照各进程到达就绪队列的顺序,轮流让各个进程执行一个时间片。若进程未在一个时间片内执行完,则剥夺处理机,将进程重新放到就绪队列队尾,重新排队。
因此,时间片轮转调度算法属于抢占式
优点:公平;响应快,适用于分时操作系统(允许多个用户同时与操作系统进行交互);
缺点:由于高频率的进程切换,因此有一定的开销;不区分任务的紧急程度
不会导致饥饿
5)优先级调度算法
在这里插入图片描述
算法思想:随着计算机的发展,特别是实时操作系统的出现,越来越多的应用场景需要根据任务的紧急程度来决定处理顺序。
算法规则:每个作业/进程有各自的优先级,调度时选择优先级最高的作业/进程,若就绪队列的作业/进程优先级相同,则先到先服务。
有抢占式和非抢占式两种
有可能导致饥饿,(当有源源不断高优先级进程到来时)
6)多级反馈队列调度算法
在这里插入图片描述

算法思想:对其他调度算法的折中权衡。
算法规则:
设置多级就绪队列,各级队列优先级从高到低,时间片从小到大。
新进程到达时,先进入第一级队列,按照FCFS原则排队等待被分配时间片。若用完时间片,进程还未结束,则进程进入下一级队列队尾。如果此时已经在最下级的队列,则重新放回最下级队列队尾。只有第k级队列为空时,才会为k+1级队头的进程分配时间片。被抢占处理机的进程重新放回原队列队尾。
属于抢占式的算法。
优点:对各类型进程相对公平(FCFS的优点);每个新到达的进程都可以很快得到响应(RR的优点);短进程只用较少的时间就可完成(SPF的优点),不必实现估计进程的运行时间(避免用户造假);可灵活地调整对各类进程的偏好程度,比如CPU密集型进程、I/O密集型进程(拓展:可以将因I/O而阻塞的进程重新放回原队列,这样I/O型进程就可以保持较高优先级)
会导致饥饿。(源源不断的短进程到达)
7)多级队列调度算法
在这里插入图片描述

2_9 进程的同步,进程的互斥

进程的异步性是指,各并发执行的进程以各自独立的、不可预知的速度向前推进。
进程的同步性:同步,也称为直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调他们的工作次序而产生的制约关系。进程间的直接制约关系就是源于它们之间的互相合作。
临界资源:我们把一个时间段内只允许一个进程使用的资源称为临界资源。许多物理设备(比如摄像头,打印机)都属于临界资源。此外,还有许多变量、数据、内存缓冲区等都属于临界资源。
对于临界资源的访问,必须互斥地进行。互斥,也称间接制约关系。进程互斥指当一个进程访问某临界资源时,另一个想要访问该临界资源的进程必须等待。当前访问临界资源的进程访问结束,释放该资源之后,另一个进程才能去访问该临界资源。
在这里插入图片描述

2_10进程互斥的软件实现方法

在这里插入图片描述
单标志法
算法思想:两个进程在访问完临界区后会把使用临界区的权限转交给另一个进程。也就是说每个进程进入临界区的权限只能被另一个进程赋予,只能轮流访问,如果对方一直不使用,则自己也不能使用,违背了空闲让进的原则。
在这里插入图片描述
双标志先检查法
算法思想:设置一个布尔型数组flag[],数组中各个元素用来标记各进程想进入临界区的意愿,比如“flag[0] = ture”意味着0号进程P0现在想要进入临界区。每个进程在进入临界区之前先检查当前有没有别的进程想要进入临界区,如果没有,则把自身对应的标志flag[i]设为true,之后开始访问临界区。
在这里插入图片描述
因此,双标志先检查法的主要问题是:违反“忙则等待”原则。
原因在于,进入区的“检查”和“上锁”两个处理不是一气呵成的。“检查”后,“上锁”前可能发生进程切换。
双标志后检查法
算法思想:双标志先检测法的改版。前一个算法的问题是先“检查”后“上锁”,但是这两个操作又无法一气呵成,因此导致了两个进程同时进入临界区的问题。因此,人们又想到先“上锁”后“检查”的方法,来避免上述问题。
在这里插入图片描述
因此,双标志后检查法虽然解决了“忙则等待”的问题,但是又违背了“空闲让进”和“有限等待”原则,会因各进程都长期无法访问临界资源而产生“饥饿”现象
Peterson算法
算法思想:结合双标志法、单标志法的思想。如果双方都争着想进入临界区,那可以让进程尝试“孔融让梨”(谦让)。做一个有礼貌的进程。
在这里插入图片描述

2_11进程互斥的硬件实现方法

在这里插入图片描述
中断屏蔽方法
利用“开/关中断指令”(与原语的实现思想相同,即在某进程开始访问临界区到结束访问为止都不允许被中断,也就不能发生进程切换,因此也不可能发生两个同时访问临界区的情况)
在这里插入图片描述
优点:简单、高效
缺点:不适用于多处理机;只适用于操作系统内核进程,不适用于用户进程(因为开/关中断指令只能运行在内核态,这组指令如果能让用户随意使用会很危险)
TestAndSet指令
简称TS指令,或者TSL指令
TSL指令用硬件实现的,执行的过程不允许被中断,只能一气呵成。以下是用C语言描述的逻辑
在这里插入图片描述
优点:实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞;适用于多处理机环境。
缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致“忙等”。
Swap指令
也叫Exchange指令,或者XCHG指令
在这里插入图片描述
优点:实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞;适用于多处理机环境。
缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致“忙等”。
需要连续循环忙等的互斥锁,都可称为自旋锁,如TSL指令、Swap指令、单标志法。
进程互斥:锁

2_12 信号量机制(重要)

用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程互斥、进程同步。
信号量其实就是一个变量(可以是一个整数,也可以是更复杂的记录型变量),可以用一个信号量来表示系统中某种资源的数量,比如:系统中只有一台打印机,就可以设置一个初值为1的信号量。
一对原语:wait(S)原语和signal(S)原语。也可以简称为P、V操作。
所以也写作P(S),V(S)。
在这里插入图片描述
整型信号量:用一个整数型的变量作为信号量,用来表示系统中某种资源的数量。可能导致忙等。

int S = 1; //初始化整型信号量S,表示当前系统中可用的打印机资源数

void wait (int S)
{					//wait原语,相当于“进入区”
	while(S <= 0 );	//如果资源数不够,就一直循环等待
	S = S-1;		//若果资源数够,则占用一个资源
}

void signal (int S )//signal 原语,相当于“退出区”
{
	S = S+1;	//使用完资源后,在退出区释放资源
}

/*进程P0*/
···
wait(S);//进入区,申请资源
使用打印机资源 //临界区,访问资源
signal(S);	//退出区,释放资源
···
/*进程P1*/
···
wait(S);//进入区,申请资源
使用打印机资源 //临界区,访问资源
signal(S);	//退出区,释放资源
···
/*进程Pn*/
···
wait(S);//进入区,申请资源
使用打印机资源 //临界区,访问资源
signal(S);	//退出区,释放资源
···

记录型信号量

/*记录型信号量的定义*/
typedef struct
{
	int value;			//剩余资源数
	struct process *L;	//等待队列
}semaphore;

/*某进程需要使用资源时,通过wait原语申请*/
void wait (semaphore S)
{
	S.value--;
	if (S.value <=0)
	{
		block(S.L); //block原语,将当前进程从运行态进入阻塞态,并把挂到信号量S的等待队列(即阻塞队列)中
	}
}
/*进程使用完资源后,通过signal原语释放*/
void signal (semaphore S)
{
	S.value++;
	if (S.value <= 0 )
	{
		wakeup(S.L); 	//wakeup原语,唤醒等待队列中的一个进程,让其从阻塞态变为就绪态
	}
}

在这里插入图片描述
信号量机制实现进程互斥
1、分析并发进程的关键活动,划定临界区(如:对临界资源打印机的访问就应放在临界区)
2、设置互斥信号量mutex,初值为1
3、在进入区 P(mutex)——申请资源,阻塞进程
4、在退出区 V(mutex)——释放资源,将进程从阻塞态转换为就绪态,唤醒进程
注意:对不同的临界资源需要设置不同的互斥信号量。P、V操作必须成对出现。缺少P(mutex)就不能保证临界资源的互斥访问。缺少V(mutex)会导致资源永不被释放,等待进程永不被唤醒。

信号量机制实现进程同步
用信号量实现进程同步:
1、分析什么地方需要实现“同步关系”,即必须保证“一前一后”执行的两个操作(或两句代码)
2、设置同步信号量S,初始为0
3、在“前操作”之后执行V(S)
4、在“后操作”之前执行P(S)

信号量机制实现进程前驱关系

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2_13生产者——消费者问题(第一次提到了死锁问题)

系统中有一组生产者进程和一组消费者进程。生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。(注:这里的“产品”理解为某种数据)
在这里插入图片描述
分析
1、生产者进程之间必须互斥的访问缓冲区的资源。 => 设置信息量mutex实现互斥访问,初始值为1
2、当缓冲区没满时,生产者进程才能进行生产 => 进程同步问题,设置empty信息量(空闲缓冲区),初始值设置为n
3、当缓冲区为非空时,消费者才能进行消费 => 进程同步问题,设置full信息量(非空闲缓冲区),初始值设置为0
在这里插入图片描述
在这里插入图片描述

/*信号量定义*/
semaphore mutex = 1; //互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n; //同步信号量,表示空闲缓冲区的数量
semaphore full = 0; //同步信号量,表示产品的数量,也即非空闲缓冲区的数量

/*生产者进程*/
producer()
{
	while(1)
	{
		生产一个产品;
		P(empty);	//放入产品之前,消耗一个空闲缓冲区
		P(mutex);	//对缓冲区资源的互斥访问,两个P操作的顺序不能反,如果反了则会导致死锁
		把产品放入缓冲区;
		V(mutex);
		V(full); 	//放入之后,增加一个非空闲缓冲区
	}
}
/* 消费者进程*/
consumer()
{
	while(1)
	{
		P(full);  //拿出之前,消耗一个非空闲缓冲区
		P(mutex); //对缓冲区资源的互斥访问
		把产品从缓冲区拿出;
		V(mutex);
		V(empty); //拿出之后,增加一个空闲缓冲区
		使用产品;
	}
}

如果两个P操作顺序不对,将导致死锁,即造成了生产者等待消费者释放空闲缓冲区,而消费者又等待生产者释放非空闲缓冲区的情况,生产者和消费者循环等待被对方唤醒,出现“死锁”。

因此,实现互斥的P操作一定要在实现同步的P操作之后
V操作不会导致进程阻塞,因此两个V操作顺序可以交换。

2_14多类生产者——多类消费者问题

桌上有一只盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等着吃盘子中的橘子,女儿专等着吃盘子中的苹果。只有盘子空时,爸爸或妈妈才可向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或者女儿可以从盘子中取出水果。用PV操作实现上述过程。
在这里插入图片描述

2_15吸烟者问题(可生产多种产品的单生产者—多消费者)

假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放桌子上,拥有身下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料在桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)
在这里插入图片描述

2_16 读者写者问题

有读者和写者并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时,不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:1)允许多个读者可以同时对文件执行读操作;2)只允许一个写者往文件中写信息;3)任一写者在完成写操作之前不允许其他读者或写者工作;4)写者执行写操作前,应让已有的读者和写者全部退出。
在这里插入图片描述

2_17哲学家进餐问题

一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家 饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。
在这里插入图片描述
在这里插入图片描述

2_18管程

用来实现进程的互斥和同步。

2_19 死锁

死锁:在并发环境下,各进程因竞争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,都无法向前推进的想象,这就是“死锁”。发生死锁后,若无外力干涉,这些进程将无法向前推进。
饥饿: 由于长期得不到想要的资源,某进程无法向前继续推进的现象。
死循环:某进程执行过程中一直跳不出某个循环的现象。有时是bug,有时是故意而为之。

死锁的产生的必要条件
1) 互斥条件:只有对互斥使用的资源的争夺才会导致死锁。像内存、扬声器这样可以同时让多个进程使用的资源是不会导致死锁的(因为进程不用阻塞等待这种资源)
2) 不剥夺条件: 进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
3) 请求和保持条件: 进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
4) 循环等待条件: 存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
注意!发生死锁时一定有循环等待,但是发生循环等待时未必死锁。
当对不可剥夺资源分配不合理时就会发生死锁。
在这里插入图片描述
死锁的处理策略——预防死锁
1)破坏互斥条件
如果把只能互斥使用的资源改造为允许共享使用,则系统不会进入死锁状态。比如:SPOOLing技术。
缺点:并不是所有的资源都可以改造成可共享使用的资源。并且为了系统安全,很多地方还必须保护这种互斥性。因此,很多时候都无法破坏互斥条件。
2)破坏不剥夺条件
方案一:当某个进程请求新的资源得不到满足时,它必须立即释放保持的所有资源,待以后需要时再重新申请。也就是说,即使某些资源尚未使用完,也需要主动释放,从而破坏了不可剥夺条件。
方案二: 当某个进程需要的资源被其他进程所占有的时候,可以由操作系统协助,将想要的资源强行剥夺。这种方式一般需要考虑各进程的优先级(比如:剥夺调度方式,就是将处理机资源强行剥夺给优先级更高的进程使用)
缺点:实现复杂,释放已获得的资源可能造成前一阶段工作的失效。因此,改方法只适用于易保存和恢复状态的资源,如cpu。反复申请和释放资源会增加系统开销。
3) 破坏请求和保持条件
可以采用静态分配方法,即进程在运行前一次申请完它所需要的全部资源,在它的资源未满足前,不让它投入运行。一旦投入运行后,这些资源就一直归他所有,该进程就不会再请求别的任何资源。
缺点:资源利用率极低。也可能导致某些进程的饥饿。
4)破坏循环等待条件
可采用顺序资源分配法。首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源(即编号相同的资源)一次申请完。
原理分析:一个进程只有已占有小编号的资源时,才有资格申请更大编号的资源。按此规则,已有大编号资源的进程不可能逆向地回来申请小编号的资源,从而就不会产生循环等待的现象。
缺点:不方便添加新的设备,因为可能需要重新分配所有的编号;
进程实际使用资源的顺序可能和编号递增顺序不一致,会导致资源的浪费。

死锁的处理策略——避免死锁
安全序列:就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能有很多个。
因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是“银行家算法”的核心思想。
在这里插入图片描述

银行家算法
银行家算法步骤
1)检查此次申请是否超过了之前声明的最大需求数
2)检查此时系统剩余的可用资源是否还能满足这次请求
3)试探着分配,更改各数据结构
4)用安全性算法检查此次分配是否会导致系统进入不安全状态
安全性算法步骤:
检查当前的剩余可用资源是否能满足某个进程的最大需求。如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收。
不断重复上述过程,看最终是否能让所有进程都加入安全序列。
死锁的处理策略——死锁的监测和解除
为了能对系统是否已经发生了死锁进程检测,必须:
1)用某种数据结构来保存资源的请求和分配信息;
2)提供一种算法,利用上述信息来检测系统是否已经进入死锁状态。
在这里插入图片描述
死锁定理:如果某时刻系统的资源分配图是不可完全简化的,那么此时系统死锁。
死锁的解除
1、资源剥夺法。挂起(暂时放到外存上)某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但是应防止被挂起的进程长时间得不到资源而饥饿
2、撤销进程法(或终止进程法)。强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源。这种方式的优点是实现简单,但所付出的代价可能会很大。因为有些进程可能已经运行了很长时间,已经接近结束了,一旦被终止可谓功亏一篑,以后还得从头再来。
3、进程回退法。让一个或多个死锁进程回退到足以避免死锁的地步。这就要求系统要记录进程的历史信息,设置还原点。
在这里插入图片描述

3 内存

逻辑地址(相对地址) 与 物理地址(绝对地址)
从写程序到程序运行
在这里插入图片描述
三种装入方式
1)绝对装入——编译时产生绝对地址 (单道程序阶段,此时还没有产生操作系统)
2)可重定位装入——装入时将逻辑地址转换为物理地址(用于早起的多道批处理操作系统)
3)动态运行时装入——运行时将逻辑地址转化为物理地址,需设置重定位寄存器。(现代操作系统)
在这里插入图片描述

3_1 覆盖与交换

覆盖技术的思想:将程序分为多个段(多个模块)。常用的段常驻内存,不常用的段在需要时调入内存。内存中分为一个“固定区”和若干个“覆盖区”。需要常驻内存的段放在“固定区”中,调入后就不再调出(除非运行结束)。不常用的段放在“覆盖区”,需要用到时调入内存,用不到时调出内存。
在这里插入图片描述
必须由程序员声明覆盖结构,缺点:对用户不透明。已经成为历史
交换技术的设计思想
内存空间紧张时,系统对内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)
在这里插入图片描述
1、具有对换功能的操作系统中,通常把磁盘空间分为文件区和对换区两部分。文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式。总之,对换区的I/O速度比文件区更快。
2、交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时进程发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率明显下降,就可以暂停换出。
3、可优先换出阻塞进程;可换出优先级低的进程;为了防止优先级低的进程在被调入内存后很快又被换出,有的系统还会考虑进程在内存的驻留时间。
(注意:PCB会常驻内存,不会被换出外存,以便记录下换出外存的位置)

3_2连续分配管理方式

内部碎片:分配给某进程的内存区域中,如果有些部分没有用上,就是内部碎片。
外部碎片:是指内存中的某些空闲分区由于太小而难以利用。
固定分区分配
将整个用户空间划分为若干个固定大小的分区,在每个分区中只装入一道作业。这样就形成了最早的、最简单的一种可运行多道程序的内存管理方式。
在这里插入图片描述
操作系统需要建立一个数据结构——分区说明表,来实现各个分区的分配与回收。每个表现对应一个分区,通常按分区大小排列。每个表项包括对应分区的大小、起始地址、状态(是否已分配)
在这里插入图片描述
优点:实现简单,无外部碎片
缺点:当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采用覆盖技术来解决,但这又会减低性能。会产生内部碎片,内存利用率低。
动态分区分配
又称为可变分区分配。这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需求。因此系统分区的大小和数目是可变的。
可能产生外部碎片。
可以通过紧凑技术来解决外部碎片。
在这里插入图片描述

3_3 动态分区分配算法

首次适应算法
算法思想:每次都从低地址开始查找,找到第一个能满足大小的空闲分区。
如何实现:空闲分区以地址递增的次序排列。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。

最佳适应算法
算法思想:由于动态分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时有连续的大片空间,可以尽可能多地留下大片的空闲区,即优先使用更小的空闲区。
如何实现:空闲分区按容量递增次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
在这里插入图片描述
最坏适应算法(又称最大适应算法)
算法思想:为了解决最佳适应算法的问题——即留下太多难以利用的小碎片,可以在每次分配时有限使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用。
如何实现:空闲分区按容量递减次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
缺点:之后有“大进程”到达时,无法安放。
邻近适应算法
算法思想:首次适应算法每次都从链头开始查找。这可能会导致低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。如果每次都从上次查找结束的位置开始检索,就能解决上述问题。
如何实现:空闲分区以地址递增的顺序排序(可排成一个循环链表)。每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
在这里插入图片描述
综合来看首次适应算法效果最好。

3_4基本分页存储管理的概念

进程的页/页面(进程在逻辑上被划分为的一个一个部分)与内存的页框有一一对应的关系。
数据结构——页表(操作系统为每个进程建立一张页表)
页表通常存放在PCB中,页表存储逻辑上的页面与物理上的页框之间的映射关系。
1、一个进程对应一个页表
2、进程的每个页面对应一个页表项
3、每个页表项由“页号”和“块号”组成
4、页表记录进程页面和实际存放的内存块之间的映射关系。

在这里插入图片描述
由于页号是隐含的,因此每个页表项占3B,存储整个页表至少需要3*(n+1)B
注意:页表记录的只是内存块号,而不是内存块的起始地址!J号内存块的起始地址=J*内存块大小
页号 = 逻辑地址 / 页面长度 (取除法的整数部分)
页内偏移量 = 逻辑地址 % 页面长度 (取除法的余数部分)
假设某计算机用32个二进制位表示逻辑地址,页面大小为4KB = 2^12B = 4096 B
0号页的逻辑地址范围就应该是0~4095
在这里插入图片描述

3_5 基本地址变换机构

在这里插入图片描述
逻辑地址到物理地址的转换
程序计数器PC:指向下一条指令的逻辑地址A
在这里插入图片描述
基本地址变换机构可以借助进程的页表将逻辑地址转换为物理地址。
通常会在系统中设置一个页表寄存器(PTR),存放页表在内存中的起始地址F和页表长度M。进程未执行时,页表的始址和页表长度放在进程控制块中,当进程被调度时,操作系统内核会把它们放到页表寄存器中。
注意:页面大小是2的整数幂
在这里插入图片描述

3_6 基本分段存储管理

进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从0开始编址
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。

分段系统的地址逻辑结构由段号(段名)和段内地址(段内偏移量)所组成。

段号的位数决定了每个进程最多可以分几个段。
段内地址位数决定了每个段的最大长度是多少。

在这里插入图片描述
页是信息的物理单位。分页的主要目的是为了实现离散分配,提高内存利用率。分页仅仅是系统管理上的需要,完全是系统行为,对用户是不可见的
段是信息的逻辑单位。分段的主要目的是更好地满足用户需求。一个段通常包含着一组属于一个逻辑模块的信息。分段对用户是可见的,用户编程时需要显式地给出段名。
页的大小固定且由系统决定。段的长度不固定,决定于用户编写的程序。
分页的用户进程地址空间是一维的,程序员只需给出一个记忆符即可表示一个地址。
分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名,也要给出段内地址。
分段比分页更容易实现信息的共享和保护。

段页式管理方式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3_7虚拟内存

基于局部性原理,在程序装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行。
在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。
若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。
在操作系统的管理下,在用户看来似乎有一个比实际内存大很多的内存,这就是虚拟内存。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
请求分页管理方式
请求分页管理方式与基本分页存储管理的主要区别:
在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。
若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。
在这里插入图片描述

在这里插入图片描述
在请求分页系统中,每当要访问的页面不在内存时,便产生一个缺页中断,然后由操作系统的缺页中断处理程序处理中断。
此时缺页的进程阻塞,放入阻塞队列,调页完成后再将其唤醒,放回就绪队列中。
1)如果内存中有空闲块,则为进程分配一个空闲块,将所缺页面装入该块,并修改页表中相应的页表项。
2)如果内存中没有空闲块。则由页面置换算法选择一个页面淘汰,若该页面在内存期间被修改过,则要将其写回外存。未修改的页面不用写回外存。
地址变换机构:
在这里插入图片描述
页面置换算法
1)最佳置换算法(OPT)
每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面,这样可以保证最低的缺页率。
操作系统必须提前知道页面访问次序,但实际中是无法得知的,因此,最佳置换算法是无法实现的。
2)先进先出置换算法(FIFO)
每次选择淘汰的页面是最早进入内存的页面

3)最近最久未使用置换算法(LRU,least recently uesd)
每次淘汰的页面是最近最久未使用的页面
实现方法:赋予每个页面对应的页表项中,用访问字段记录该页面自上次被访问以来所经历的时间t,当需要淘汰一个页面时,选择现有页面中t值最大的,即最近最久未使用的页面。
该算法的实现需要专门的硬件支持,虽然算法性能好,但是实现困难,开销大。
4)时钟置换算法(CLOCK,最近未用算法)
为每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列。当某页被访问时,其访问位置为1。当需要淘汰一个页面时,只需检查页的访问位。如果是0,就选择该页换出,如果是1,则将它置为0,暂不换出,继续检查下一个页面,若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,因此简单的CLOCK算法选择一个淘汰页面最多会经历两轮扫描)
在这里插入图片描述
5)改进型的时钟置换算法
简单的时钟置换算法仅考虑到一个页面最近是否被访问过。事实上,如果被淘汰的页面没有被修改过,就不需要执行I/O操作写回外存。只有被淘汰的页面被修改过时,才需要写回外存
因此,除了考虑一个页面最近有没有被访问过之外,操作系统还应考虑页面有没有被修改过。在其他条件都相同时,优先淘汰没有修改过的页面,避免I/O操作。这就是改进型的时钟置换算法的思想。
修改位=0,表示页面没有被修改过;修改位=1,表示页面被修改过。
算法规则:将所有可能被置换的页面排成一个循环队列
(访问位,修改位)= (1,1)
第一轮:从当前位置开始扫描到第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
第二轮:若第一轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换。本轮将所有扫描过的帧访问位设为0。
第三轮:若第二轮扫描失败,则重新扫描,查找第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位。
第四轮:若第三轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换。
由于第二轮将所有帧的访问位设为0,因此经过第三轮、第四轮扫描一定会有一个帧被选中,因此,改进型CLOCK置换算法选择一个淘汰页面最多会进行四轮扫描。
第一优先级:最近没访问。且没修改的页面。
第二优先级:最近没访问,但修改过的页面。并标记为访问过。
第三优先级:最近访问过,但没修改过的页面
第四优先级:最近访问过且修改过的页面。
页面分配策略、抖动、工作集
驻留集:指请求分页存储管理中给进程分配的物理块的集合。
工作集:指某段时间间隔里,进程实际访问页面的集合。

在采用了虚拟存储技术的系统中,驻留集大小一般小于进程的总大小。
抖动(颠簸)现象:刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存。产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数==(分配给进程的物理块不够)==

一般来说,驻留集的大小不能小于工作集大小,否则进程运行过程中将频繁缺页。

内存映射文件
操作系统向上层程序员提供的功能(系统调用)
在这里插入图片描述

4 文件管理系统

4_1 文件的逻辑结构

指在用户看来,文件内部的数据应该是如何组织起来的。
在这里插入图片描述
顺序文件:文件中的记录一个接一个地顺序排列(逻辑上),记录可以是定长的或可变长的。各个记录在物理上可以顺序存储或链式存储
在这里插入图片描述
在这里插入图片描述
索引文件:
在这里插入图片描述
索引顺序文件:
并不是每个记录对应一个索引表项,而是一组记录对应一个索引表项。
在这里插入图片描述

4_2 文件目录

在这里插入图片描述
多级目录结构:
在这里插入图片描述
从根目录出发的路径称为绝对路径
可以从当前目录出发的“相对路径
“./2015-08/自拍.jpg”
树形目录结构不便于实现文件的共享。为此提出了“无环图目录结构”
在这里插入图片描述

4_3 文件的物理结构(文件分配方式)

在这里插入图片描述
在内存管理中,进程的逻辑地址空间被分为一个一个页面。
同样的,在外存管理中,为了方便对文件数据的管理,文件的逻辑地址空间也被分为了一个一个的文件“块”,于是文件的逻辑地址也可以表示为(逻辑块号,块内地址)的形式
连续分配
要求每个文件在磁盘上占用一组连续的块。
文件目录中记录存放的起始块号和长度(总共占用几个块)
连续分配的文件在顺序读/写时速度最快
物理上采用连续分配的文件不便于拓展。
存储空间利用率低,会产生难以利用的磁盘碎片,可以用紧凑来处理碎片,但消耗很大的时间代价。
链接分配
隐私链接:
目录中记录了文件存放的起始块号和结束块号。
显示链接:
目录中只需记录文件的起始块号
把用于链接文件各物理块的指针显示地存放在一张表中。即文件分配表。
一个磁盘对应一个文件分配表(FAT)
索引分配
索引分配允许文件离散地分配在各个磁盘块中,系统会为每个文件建立一张索引表(保存在外存中),索引表中记录了文件的各个逻辑块对应的物理块(索引表的功能类似于内存管理中的页表——建立逻辑页面到物理页之间的映射关系)。索引表存放的磁盘块称为索引块。文件数据存放的磁盘块称为数据块。
若索引表太大,一个索引块装不下
1)链接方案。可以将多个索引块链接起来存放。
2)多层索引。建立多层索引。使第一层索引块指向第二层的索引块。还可根据文件大小的要求再建立第三层、第四层。
在这里插入图片描述

3)混合索引。多种索引分配方式的结合。例如:一个文件的顶级索引表中,既包含直接地址索引(直接指向数据块),又包含一级间接索引(指向单层索引表)、还包含两级间接索引(指向两层索引表)
在这里插入图片描述
在这里插入图片描述

4_4 逻辑结构VS物理结构

在这里插入图片描述
在这里插入图片描述

/*C语言创建无结构文件*/
FILE *fp = fopen("test.txt","w");//打开文件
if (fp == null)
{
	printf("打开文件失败");
	exit(0);
}
//写入1w个Hello World
for (int i =0; i<10000; i++)
	fputs("Hello world!",fp);
fclose(fp);		//关闭文件




/*C语言创建顺序文件*/
typedef struct
{
	int number;	//学号
	char name[30]; //姓名
	char major[30];	//专业
}Student_info;
//以写方式打开文件
FILE *fp = fopen("students.info","w");
if (fp == null)
{
	printf("打开文件失败!");
	exit(0);
}
Student_info student[N];	//用数组保存N个学生信息
for (int i = 0; i<N; i++)
{
	student[i].number = i;
	student[i].name[0] = '?';
	student[i].major[0] = '?';
}
//将N个学生的信息写入文件
fwirte(student, sizeof(Student_info),N,fp);
fclose(fp);

//以读方式打开文件
FILE *fp = fopen("students.info","r");
if (fp == NULL)
{
	printf("打开文件失败!");
	exit(0);
}
//文件读写指针指向编号为5的学生记录
fseek(fp,5*sizeof(Student_info),SEEK_SET);
Student_info stu;
//从文件读出1条记录,记录大小为 sizeof(Student_info)
fread(&stu,sizeof(Student_info),1,fp);
printf("学生编号:%d\n",stu.number);
fclose(fp);

在这里插入图片描述

4_4 文件存储空间的管理(对空闲空间的管理)

在这里插入图片描述
在这里插入图片描述
回收之前要注意表项的合并问题。
在这里插入图片描述
在这里插入图片描述
存储空间管理——位示图法
在这里插入图片描述

4_5文件共享

在这里插入图片描述
索引结点,是一种文件目录瘦身策略。由于索引文件时只需用到文件名,因此可以将除了文件名之外的其它信息放到索引结点中。这样目录项就只需要包含文件名、索引结点指针。
在这里插入图片描述
在这里插入图片描述
创建桌面快捷方式的文件存储方式。
在这里插入图片描述

4_6文件保护

在这里插入图片描述
口令:用户请求访问文件时提供“口令”
开销不多。缺点:口令存放在系统内部,不安全。
加密保护:访问文件时需要提供正确的密码。通过加密算法。
系统中保存对密码进行加密后的密码。
访问控制:在每个文件的FCB中增加一个访问控制列表。
在这里插入图片描述

4_7文件系统的全局结构(布局)

物理格式化,即低级格式化——划分扇区,检测坏扇区,并用扇区替换坏扇区。
逻辑格式化:
在这里插入图片描述
逻辑格式化后,磁盘分区(分卷),完成各分区的文件系统初始化。
(注,逻辑格式化后,灰色区域就有实际数据了,白色部分还没有数据)

4_8虚拟文件系统

5 I/O设备

5_1 I/O设备的基本概念和分类

在这里插入图片描述
在这里插入图片描述

5_2 I/O控制器

在这里插入图片描述
在这里插入图片描述
一个I/O控制器可能对应多个设备。
数据寄存器、控制寄存器、状态寄存器可能有多个。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值