1 进程的定义
进程由程序段+数据段+PCB构成的。PCB是进程存在的唯一标志。进程是进程实体的运行过程,是系统进行资源分配的一个独立单位。
2 进程控制
3 进程通信
(1) 共享存储
(2) 管道通信
4 进程调度算法
5 线程
线程是一个基本的cpu执行单位,也是程序执行流中的最小单位。引入线程后,线程作为cpu调度最小单位,而进程是资源分配最小单位。
6 线程实现方法
7 进程同步与互斥
**同步:**并发性带来了异步性,有时需要通过进程同步来解决这种异步问题。有的进程之间需要相互配合地完成工作,各进程的工作推进需要遵循一定的先后顺序。
**互斥:**对临界资源的访问,需要互斥的进行。即一段时间内只允许一个进程访问该资源。
8 信号量机制
记录型信号量。
typedef struct{
int value; //剩余资源数
struct process *L; // 等待队列
}semaphore;
void
wait( semaphore S ){ //对信号量S一次p操作表示进程请求一个
S.value--; //单位的的该类资源,S.value<0时表示该类资
if( S.value < 0 ){ //源分配完毕,因此进程调用block原语进行自
block( S.L ); //我阻塞,并插入该类资源的等待队列S.L中
}
}
void
signal( semaphore S ){ //对信号量进行一次v操作表示进程释放一个该单
S.value++; //位的资源,若++后S.value<=0,表示依然有进
if( S.value <= 0 ){ //程等待该类资源,因此调用wakeup唤醒等待队
wakeup( S.L ); //列中第一个进程
}
}
信号量实现进程同步、互斥和前驱。
互斥:确定临界区;
设置互斥信号量,初值为1(对于不同的临界区需要设置不同的互斥信号量)
临界区之前对信号量执行p操作
临界区之后对信号量执行v操作
同步:找出需要实现的“一前一后”关系
设置同步信号量,初值为0(同步信号量的初值要看对应资源的初始值为多少)
在“前操作”之后执行v操作
在“后操作”之前执行p操作
前驱:画出前驱图,把每一对前驱关系都看出同步问题
为每一对前驱关系设置同步信号量,初值为0
在每个“前操作”之后执行v操作
在每个“后操作”之前执行p操作
生产者-消费者问题
semaphore mutex = 1; //互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n; //同步信号量,表示空闲缓冲区数量
semaphore full = 0; //同步信号量,表示产品的数量
Producer(){
while( 1 ){
生产一个产品;
p( empty );
p( mutex );
把产品放入缓冲区;
v( mutex );
v( full );
}
}
Consumer(){
while( 1 ){
p( full );
p( mutex );
从缓冲区取出一个产品;
v( mutex );
v( mutex );
使用产品;
}
}
实现互斥的p操作一定要在实现同步的p操作之后
多生产者-多消费者问题
semaphore mutxe = 1; //互斥访问盘子
semaphore apple = 0;
semaphore orange = 0;
semaphore plate = 1; //盘子中还可以放多少水果
dad(){
while( 1 ){
准备一个苹果;
p( plate );
p( mutex );
把苹果放入盘子里;
v( mutex );
v( apple );
}
}
mom(){
while( 1 ){
准备一个橘子;
p( plate );
p( mutex );
把橘子放入盘子里;
v( mutex );
v( orange );
}
}
daughter(){
while( 1 ){
p( plate );
p( mutex );
从盘子里取出苹果;
v( mutex );
v( plate );
吃掉苹果;
}
}
son(){
while( 1 ){
p( orange );
p( mutex );
从盘子里取出橘子;
v( mutex );
v( plate );
吃掉橘子;
}
}
如果缓冲区大小大于1,就必须专门设置1个互斥信号量mutex,来保证互斥访问缓冲区。
吸烟者问题
semaphore offer1 = 0;
semaphore offer2 = 0;
semaphore offer3 = 0;
semaphore finsh = 0;
int i = 0; //用于实现3个吸烟者轮流吸烟
provider(){
while( 1 ){
if( i == 0 ){
将组合1放桌子上;
v( offer1 );
} else if( i == 1 ){
将组合2放入桌子上;
v( offer2 );
}else if( i == 2 ){
将组合3放入桌子上;
v( offer3 );
}
}
i = ( i + 1) % 3;
p( finsh );
}
smoker1(){
while( 1 ){
p( offer1 );
从桌子上拿走组合1,卷烟,抽掉;
v( finsh );
}
}
smoker2(){
while( 1 ){
p( offer2 );
从桌子上拿走组合2,卷烟,抽掉;
v( finsh );
}
}
smoker3(){
while( 1 ){
p( offer3 );
从桌子上拿走组合3,卷烟,抽掉;
v( finsh );
}
}
读者-写者问题
semaphore rw = 1; //用于实现对文件的互斥访问
int count = 0; //记录当前有几个读进程在访问文件
semaphore mutex = 1; //用于保证对count变量的互斥访问
semaphore w = 1; //用于实现“读写公平”
writer(){
while( 1 ){
p( w );
p( rw );
写文件;
v( rw );
v( w );
}
}
reader(){
while( 1 ){
p( w );
p( mutex );
if( count == 0 )
p( rw );
count++;
v( mutex );
v( w );
读文件;
p( mutex );
count--;
if( count == 0 )
v( rw );
v( mutex );
}
}
9 管程
管程是一种特殊设计的软件模块,由这样部分组成:
1.局部于管程的共享数据结构说明
2.对该数据结构进行的一组操作过程
3.对局部于管程的共享数据设置初始值的语句
4.管程有一个名字
管程的基本特征:
1.局部于管程的数据只能被局部于管程的过程所访问
2.一个进程只有通过调用管程内的过程才能进入过程访问共享数据
3.每次仅允许一个进程在管程内执行某个内部过程(由编译器负责实现)
管程解决生产者-消费者问题
monitor ProducerConsumer
condition full,empty; //条件变量用来实现同步(排队)
int count = 0; //缓冲区中的产品数
void insert( Item item ){ //把产品item放入缓冲区
if( count == N )
wait( full );
count++;
insert_item( item );
if( count == 1 )
signal( empty );
}
Item remove(){
if( count == 0 )
wait( empty );
count--;
if( count == N - 1 )
signal( full );
return remove_itrm();
}
end monitor;
//生产者进程
producer(){
while( 1 ){
item = 生产一个从产品;
ProducerConsumer.insert( item );
}
}
//消费者进程
consumer(){
while( 1 ){
item = ProducerConsumer.remove();
消费产品item;
}
}
10 死锁
死锁:各进程相互等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象。
饥饿:由于长期得不到想要的资源,某进程无法向前推进的现象。
预防死锁:
银行家算法
安全序列:所谓安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能有很多个。
假设系统中有n个进程,m个资源
每个进程在运行前先声明对各种资源的最大需求数,则可用一个nm的矩阵(可用二维数组)表示所有进程对各种资源的最大需求数。不妨称为最大需求矩阵MAX,MAX[i,j]=K表示进程pi最多需求K个资源Rj。同理,系统可用一个nm的分配矩阵Allocation表示对所有进程的资源分配情况。MAX-Allocation=Need矩阵,表示各进程最多还需要多少各类资源。另外,还要用一个长度为m的一维数组Available表示当前系统中还有多少可用资源。
某进程pi向系统申请资源,可用一个长度为m的一维数组Requesti表示本次申请的各种资源量。
可用银行家算法预判本次分配是否会导致系统进入不安全状态:
1.如果Requesti[j]<=Need[i,j] (0<=j<=m)便转向2,否则认为出错;
2.如果Requesti[j]<=Available[j] (0<=j<=m)便转向3;否则表示尚无足够资源,pi必须等待;
3.系统试探着把资源分配给进程pi,并修改相应的数据(并非真的分配,修改数值只是为了做预判):
Available = Available - Requesti;
Allocation[i,j] = Allocation[i,j] + Requesti[j];
Need[i,j] = Need[i,j] - Requesti[j]
4.操作系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。若安全,才正式分配;否则,恢复相应数据,让进程阻塞。