操作系统引论
用户接口与作业管理
进程和线程的概念
存储器管理
文件管理
设备管理
硬件系统+软件系统(程序和文件)
软件分为:系统软件(操作系统)、支撑软件(数据库、编译程序)、应用软件
用户角度:是一个控制软件,管理应用程序,为应用程序提供服务,杀死应用程序。
资源分配角度:资源管理,管理外设,分配资源。
操作系统位于硬件之上 应用程序之下 操作系统为应用软件提供服务支撑,完成对硬件的控制与分配。
作用:
(1)管理系统中的资源
(2)提供良好的界面
方便性 有效性 可扩充性 开放性
1)无操作系统时代:
人工操作方式
脱机I/O方式:外围机 磁盘
2)单道批处理操作系统 :
作业成批处理 每次只处理一项作业
3)多道批处理操作系统 :
多道性、无序性、调度性(作业提交到完成经历两次调度 高级调度 低级调度)
**高级调度:作业调度 作业由外存调入内存
**低级调度:进程调度 内存中的作业分配给处理机
4)分时系统
5)实时系统
满足实时控制、实时信息处理
【批处理、分时、实时 是三种基本的操作系统类型 实际操作系统兼有两者或者此三者的功能】
1)微机操作系统
单用户单任务 MS-DOS
单用户多任务 Windows Linux
多用户多任务 UNIX
2)多处理机操作系统
3)网络操作系统
计算机技术和通信技术的产物
4)分布式操作系统
5)嵌入式操作系统
软件的观点
计算机资源管理的观点
进程的观点
用户与计算机硬件系统之间接口的观点
虚拟器的观点
服务提供者的观点-----程序执行、I/O操作、文件系统操控、进程通信、差错检测
简单说:
并发性
并行性是指在两个或者多个事件在同一时刻发生
并发性是指两个或者多个事件在同一时间间隔发生
共享性
指操作系统中的资源可供内存中的多个并发执行的进程共同使用
互斥共享资源:如打印机、磁带机
同时访问方式:如磁盘、重用代码编写的文件
并发和共享是操作系统最基本的两个特征
虚拟性
物理实体到逻辑对应物的转化。举例:多道程序设计,分时技术,虚拟内存技术
异步性
多道程序中允许多个程序并发执行 作业完成情况与进入内存时间不匹配 但是多次完成的结果一致。
(待续)
进程是具有独立功能的可并发执行的程序在一个数据集合上的运行过程,是系统进行资源分配和调度的独立单位。或者说,“进程”是进程实体的运行过程。
进程的特征
程序与进程的区别与联系
区别:
挂起状态的引入
进程状态的转换
进程控制的主要任务是创建和撤消进程以及实现进程的状态转换,进程控制一般由操作系统的内核来实现。操作系统内核通常是运行在系统态的。
先来先服务(FCFS)算法 :把处理机分配给最先进入就绪队列的进程
最短CPU运行期优先调度算法:从就绪队列中选出“下一个CPU执行期”最短的进程,为之分配处理机使之执行
最高响应比优先调度算法 :响应比=(等待时间+要求的服务时间)/要求的服务时间 ,每次选取响应比最高的进程调度
优先级调度算法 :将CPU分配给就绪队列中优先级最高的进程
前后台调度算法:该方法用在批处理和分时相结合的系统中。将分时用户作业放在前台,把批处理作业放在后台。系统对前台作业按照时间片轮转法进行调度,仅当前台无作业时,才把处理机分配给后台作业的进程。后台进程通常按先来先服务方式运行
多级反馈队列轮转算法
**进程调度的过程:
线程的引入
线程与进程的比较
线程具有许多传统进程所具有的特征,故又称为轻型进程(Light-Weight Process)或进程元;而把传统的进程称为重型进程(Heavy-Weight Process)。
线程的实现机制
用户级线程和内核支持线程的比较
对信号量的操作:信号量机制是一种非常有效的进程同步工具
记录型数据结构描述
typedef struct {
int value; //代表资源数
list of process *L; //进程链表L,用于链接所有等待该信号量代表资源的进程
} semaphore;
对信号量的操作:
信号量除初始化外,仅能通过两个标准的原子操作wait(s)和signal(s)来访问。这两个操作很长时间以来被称为P、V操作。
信号量的物理含义:
wait原语:申请一个资源,得到就继续,得不到就阻塞
void wait (static semaphore s)
{
s.value - -;
if (s.value<0) block(s.L);
}
singal原语: 释放一个资源,有进程等待则唤醒一个
void signal ( static semaphore s)
{
s.value++;
if (s.value <= 0) wackup(s.L);
}
利用信号量实现进程互斥的过程描述
为使多个进程互斥地访问某个临界资源,只需为该资源设置一个信号量,并设其初始值为1。此信号量可称为“互斥信号量”。
进程1,进程2 同时执行,并不存在顺序关系。
2.信号量集机制
Swait操作:
Swait(S1, S2, … , Sn)
{
if (S1≥1&&…&&Sn≥1)
for (i=1; i<=n; i++) Si -- ;
else {
阻塞该进程,并将其插入到 等待资源Si的阻塞队列中;
将程序计数器PC重新指向Swait 操作的第一条语句。
}
}
Ssignal操作:
Ssignal (S1, S2, … , Sn)
{
for (i=1; i<=n; i++)
{
Si++;
将所有等待Si资源的进程唤醒;
}
}
Swait操作
Swait(S1,t1,d1, S2,t2,d2, … , Sn,tn,dn)
{
if (S1>=t1 && … && Sn>=tn)
for (i=1;i<=n;i++) Si=Si-di;
else {
阻塞该进程,并将其插入到等待资源Si的阻塞队列中;
将程序计数器PC重新指向Swait 操作的第一条语句。
}
}
Ssignal操作
Ssignal(S1,t1,d1, S2,t2,d2, … , Sn,tn,dn)
{ for (i=1;i<=n;i++)
{ Si=Si+di;
唤醒所有等待资源 Si的进程;
}
}
黄色部分是循环使用互斥临界区的使用,标准写法:
1.进入区 申请资源
2. 临界区 使用临界区
3.退出区 释放资源
4.剩余区 退出临界区
注意:
- 实现互斥必须使 P操作-wait (mutex),V操作-signal(mutex) 成对出现
- mutex的变化:
- 初始状态:mutex:=1 表示没有并发进程使用临界区
- 一个进程申请成功 执行wait操作:mutex := 0
- 另一个进程也在申请 mutex:=-1; 则申请失败 而且此进程要进入阻塞队列
- 释放资源 mutex :=0 释放资源 并且从阻塞队列里找到想要使用资源的进程
生产者-消费者问题–
有一群生产者进程在生产产品,并将此产品提供给消费者进程去消费
利用记录型信号量解决生产者-消费者问题 —整型数和链表
初始的时候:full=0,empty=n
semaphore mutex=1, empty=n, full=0;
item buffer[n];
int in=out=0;
void producer(){
while (1) {
…
produce an item in nextp; //生产一个产品
...
wait(empty); //判断空缓冲区的个数 申请一个空缓冲区 empty-1
wait(mutex); //护持信号量 判断缓冲池是否可用
buffer[in]=nextp;//临界区 将下一个产品放入下标为in的数组
in=(in+1) % n;//临界区
signal(mutex);//释放缓冲池
signal(full);//将full+1
}
}
void consumer() //消费者 保证安全取出数据
{
while (1) {
...wait(full);//申请一个full 缓冲区
wait(mutex); //申请缓冲池 判断有没有生产者在用
nextc=buffer[out]; //取出 下标为OUT的 作为下一个消费品
out=(out+1)%n; //下标+1
signal(mutex); //退出区
signal(empty); //释放一个空缓冲区
...
consume the item in nextc; //消费
…}
}
main()
{
cobegin {
producer();
consumer();
}
}
利用AND信号量解决生产者-消费者问题
semaphore mutex1=1,
mutex2=1, empty=n,full=0;
item buffer[n];
int in=out=0;
void producer()
{
while (1)
{ …
produce an item in nextp;
...
Swait(empty, mutex1);
buffer[in]=nextp;
in=(in+1) mod n;
Ssignal(mutex1, full);
}
}
void consumer()
{
while (1)
{ ...
Swait(full, mutex2);
nextc=buffer[out];
out=(out+1) mod n;
Ssignal(mutex2, empty);
...
consume the item in nextc;
…
}
}
main()
{
cobegin {
producer();
consumer();
}
}
哲学家进餐问题
有五个哲学家,他们的生活方式是交替地进行思考和进餐。哲学家们共用一张圆桌,分别坐在周围的五张椅子上。在圆桌上有五个碗和五支筷子。平时,哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐毕,放下筷子继续思考。
semaphore chopstick[5]={1,1,1,1,1};/筷子
void process(int i)
{
while (1) {
wait(chopstick[i]); //拿起左边的筷子
wait(chopstick[(i+1) mod 5]);//拿起右边的筷子 这里左右不分 临近的加一就可以了
...
eat;//吃饭
...
signal(chopstick[i]);
signal(chopstick[(i+1) mod 5]); //释放筷子
...
think;
}
}
几种解决办法:
①至多只允许四个哲学家同时请求进餐,以保证至少有一个哲学家能够真正进餐,最终总会释放出他所使用过的两支筷子,从而可使更多的哲学家进餐。
②仅当哲学家的左、右两支筷子均可用时才允许他拿起筷子进餐。
③规定奇数号哲学家先拿他左边的筷子,然后再去拿他右边的筷子;而偶数号哲学家则相反。
利用AND信号量机制解决哲学家进餐问题
AND型信号量:宁可锦上添花,绝不雪中送炭**
semaphore chopstick[5]={1,1,1,1,1};
void process(int i)
{ while (1)
{ Swait(chopstick[(i+1) mod 5], chopstick[i]); //swit同时可以 奇数和偶数都满足才可以 采用原子操作
...
eat;
Ssignal(chopstick[(i+1) mod 5], chopstick[i]);
...
think; }
}
main()
{cobegin { process(0);
process(1);
process(2);
process(3);
process(4); }
}
读者写者问题
所谓读者-写者问题(The Reader-Writer Problem)是只保证一个writer进程必须与其他进程互斥地访问共享对象的同步问题
这里变量作为临界资源
semaphore rmutex=mutex=1;
int readcount=0;
void reader(int i)
{
while (1)
{ ...
wait(rmutex);
if (readcount==0) wait(wmutex);//==0表示无reader进程在读 reader进程才需要执行PC操作等待信号量 若P操作成功 reader便去读
readcount++;//readercount就是一个数
signal(rmutex);//读完之后释放
perform read operation; ……
wait(rmutex);
readcount--;
if(readcount==0)signal(wmutex);//当没有reader时可以执行writer操作
signal(rmutex);
...
}
}
void writer(int j)
{
while (1)
{ ...
wait(wmutex);
perform write operation;
signal(wmutex);
...
}
}
main()
{
cobegin {
reader(1);
…
reader(n);
writer(1);
...
writer(m);}
}
管程的定义
一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据.
管程的组成
直接通信方式是指发送进程利用操作系统所提供的发送命令直接把消息发送给目标进程。系统提供下述两条通信原语:
send(receiver,message); //receiver 表示的是进程标识符
receive(sender,message);
利用直接进程通信原语来解决生产者-消费者问题
void producer()
{ while (1)
{ …
produce an item in nextp;
…
send(consumer, nextp); //发送
}
}
void consumer()
{ while (1)
{ …
receive(producer, nextc); //接收
…
consume the item in nextc;
}
}
main()
{cobegin {
producer();
consumer();
}
}
消息缓冲区
struct message_buffer{
sender; // 发送者进程标识符
size; // 消息长度
text; // 消息正文
next; // 指向下一个消息缓冲区的指针
};
PCB中有关通信的数据项 在PCB中应增加的数据项可描述为
struct processcontrol_block{
…
mq; // 消息队列队首指针
mutex; // 消息队列互斥信号量
sm; // 消息队列资源信号量
…
};
预防死锁
通过设置某些限制条件,以破坏产生死锁的四个必要条件中的一个或几个,来防止发生死锁。
避免死锁
在资源的动态分配过程中,使用某种方法去防止系统进入不安全状态,从而避免了死锁的发生。
安全与不安全状态
安全状态:系统能按某种进程顺序 来为每个进程分配其所需资源,直至最大需求,使每个进程都可顺利完成。
银行家算法!!!重要!!!
基本思想
银行家算法中的数据结构(假设有n个进程、m类资源)
银行家算法
(1) 设Requesti是进程Pi的请求向量。如果Requesti[j]=k,表示进程Pi需要k个Rj类型的资源,要求:Requesti≤Needi &&Requesti≤Available
(2)系统试探把要求的资源分配给进程Pi并修改下面数据结构中的数值:
Available=Available–Requesti;
Allocationi=Allocationi+Requesti;
Needi=Needi–Requesti;
(3)系统执行安全性算法若安全,才正式将资源分配给进程Pi,以完成本次分配;否则,将试探分配作废,恢复原来的资源分配状态,让进程Pi等待,即:
Available=Available+Requesti;
Allocationi=Allocationi-Requesti;
Needi=Needi+Requesti;
安全性算法
① 设置并初始化两个向量:Work和Finish
工作向量Work,表示系统可提供给进程继续运行所需的各类资源数目;标志向量Finish,它表示系统是否有足够的资源分配给进程使之运行完成。
初始化:Work = Available; Finish = 0;
② 从进程集合中找到一个能满足下述条件的进程
Finish[i]==0 && Needi≤Work
如找到,则执行步骤③,否则执行步骤④。
③ 当进程Pi获得资源后,顺利执行,直至完成并释放出分配给它的资源,故应执行:
Work=Work+Allocationi;
Finish[i]=1;
go to step 2;
检测死锁
检测死锁方法允许系统运行过程中发生死锁。但通过系统所设置的检测机构,可以及时检测出死锁的发生,并精确地确定与死锁有关的进程和资源,然后采取适当措施,从系统中消除所发生的死锁