同步

来源:互联网 发布:ubuntu kylin 16.04 编辑:程序博客网 时间:2024/04/28 18:25

从进程的特征出发:

并发:

进程的执行时间断性

进程的相对执行速度不可预测

共享:

进程/线程之间的制约性

不确定性:

进程的执行的结果与其执行的相对速度有关,是不确定的


竞争条件:

 


竞争条件:

两个或多个进程读写某个共享数据,而最后的结果取决于进程运行的正确时序


进程互斥:

有图各进程要求使用共享资源,而这些资源需要排他性使用,各进程之间竞争使用这些资源---进程互斥


临界资源:critical

系统中某些资源一次只能允许一个进程使用,称这样的资源为临界资源或互斥资源或共享变量


临界区(互斥去):

各个进程中对某个临界资源实施操作的程序片段



互斥去使用原则:



1没有进程在临界区时,想进入临界区的进程可进入

2,不允许两个进程同时处于临界区

3,临界区外运行的进程不得阻塞其他进程进入临界区

4,不得使进程无限期等待进入临界区


软件方案:

Dekker解法,Peterson解法

硬件方案:

屏蔽中断,TSL(XCHG)指令






进程软件解决方法:


free:临界区空闲标志

true:有进程在临界区

false:无进程在临界区

初值:free---false


P:

........

while(free);

free = true; 前两行:lock()

临界区

free = false; unlock()

......


Q:

........

while(free);

free = true;

临界区

free = false;

......



软件解法2:

P:
......
while(not turn)
临界区

turn = false;

.....


Q:
......
while(not turn)
临界区

turn = false;

.....



turn :谁进临界区的标志

true----P

false----Q

初值任意


软件解法3:after you 问题



软件解法4:DEKKER算法--忙等待




解决了临界区保护问题


软件解法5----PETERSON算法


解决了互斥访问的问题,而且克服了强制轮流法的缺点



调用线程用进程号(i)


#define FALSE 0

#define TRUE 1

#define N 2 //进程的个数

int turn; //轮到谁

int interested[N]; //兴趣数组,初始值均为false


void enter_region(int process) //process = 0或1

{

int other;

other = 1 - process; //另外一个进程的进程号

interested[process] = TRUE;

turn = process;//表明本进程感兴趣

while(turn == process && interested[other] == TRUE);//设置标志位

}


void leave_region(int process)

{

interested[process] = FALSE;

}


硬件解决1--中断屏蔽方法


开关中断指令




 代价高,限制cpu并发能力(临界区太小)

不适用多处理器

适用于操作系统本身,不适用用户进程


硬件解法2---测试并加锁指令


TSL指令:TEST AND SET LOCK


 

 

 硬件解决3---交换指令

XCHG指令:EXCHANGE




小结:

软件方法:

编程技巧

硬件方法:

忙等待:

进程在得到临界区访问权之前,持续测试而不做其他事情

自旋锁Spin lock (多处理器 )

优先级反转(倒置)


 进程同步:协作关系

指系统中多个进程中发生的事件存在某种时序关系,需要相互合作,共同完成一项任务


具体地说,一个进程运行到某一点时,要求另一伙伴进程为它提供消息,在未获得消息之前,该进程进入阻塞态,获得消息后被唤醒

唤醒进入就绪态


生产者/消费者问题--又称有界缓存区问题



要解决的问题:

1,当缓存区已满,生产者就不会继续向其中添加数据

2,当缓冲区为空时,消费者不会从中移走数据

3,生产者或消费者不能同时对buffer操作


避免忙等待:

睡眠与唤醒操作(原语)




其他同步例子1:

 SPOOLING系统

 例子2:




经典机制

信号量及P,V操作


一个特殊变量

用于进程间传递信息的一个整数值

定义:

struct semaphore

{

int count;

queueType queue;

};

信号量说明:semaphore s;

对信号量可以实施的操作:初始化,P和V操作


P,V操作定义:





有关说明

原语操作:不能中断

信号量三个操作:

初始化,p操作,v操作


用pv操作解决进程间互斥问题







生产者和消费者问题:

用信号量解决生产者,消费者问题:




---->




讨论:顺序与位置

p(&full);

p(&mutex); //2个p操作能否颠倒顺序?--->思索


//若颠倒生产者的P操作:

先执行p(&mutex);再执行p(&full);

若buffer为空,先执行p(&mutex);将mutex初始化为0,然后执行p(&full);,因为full为空,执行p操作 full -1 为负数,所以将阻塞。

生产者,执行p(&empty)发现有空buffer,再执行p(&mutex),此时mutex为负数,将阻塞


v(&mutex);

v(&empty); //可以颠倒


用信号量解决读者/写者的问题:




要求满足要求:

1,允许多个读者同时执行读操作

2,不允许多个写者同时操作

3,不允许读者,写者同时操作


读者优先:



第一类读者写者问题:

第一个读者做P操作,最后一个读者V操作

rc也处在临界区,所以加一个互斥条件,用以保护rc





Linux提供读写锁:

eg:

linux 的IPX路由器,用ipx_routes_lock读写锁保护IPX路由表并发访问


/*******************/

进程控制
进程控制是进程管理中最基本的功能:创建新进程,终止已完成进程,将因发生异常情况而无法继续运行的进程置于阻塞状态,负责进程运行中的状态转换等。


OS内核的两大功能:
支撑功能:中断处理、时钟管理、原语操作
资源管理功能:进程管理,存储器管理,设备管理


进程的创建:
1,申请空白PCB,我新进程申请获得唯一的数字标识符,并从PCB集合中索取一个空白PCB
2,为新进程分配运行所需的资源
3,初始化进程控制块
4,如果进程就绪队列能够接纳新进程,变将新进程插入就绪队列


进程同步:
两种形式的制约关系:
1,间接相互制约关系---源于互斥
2,直接相互制约关系---源于相互合作


同步机制应遵循的规则:
1,空闲让进
2,忙则等待
3,有限等待
4,让权等待


硬件同步机制:
1,关中断:在进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断
2,利用Test-and-Set指令实现互斥:
3,利用Swap指令实现进程互斥


信号量机制:
1,整型信号量:除初始化外,仅能通过两个标准原子操作wait和signal来访问(P,V操作)
wait(s){  ---忙等
while(s <= 0);
s--;
}
signal(s){
s++;
}
2,记录型信号量:---让权等待
3,AND型信号量
4,信号量集


利用信号量实现进程互斥:
为该资源设置一互斥信号量mutex,并设其初始值为1,然后将各进程访问该资源的临界区CS设置wait(mutex)和signal(mutex)操作之间即可。


设mutex为互斥信号量,其初始值为1,取值范围(-1,0,1)。
mutex = 1:两个进程皆未进入需要互斥的临界区;
mutex = 0:有一个进程进入临界区运行,另外一个必须等待,挂入阻塞队列
mutex = -1:有一个进程进入临界区运行,另外一个进程因等待而阻塞在信号量队列中,需要被当前已在临界区运行的进程退出时唤醒


利用信号量实现前趋关系:
设有两个并发执行的进程P1和P2,p1中有语句S1;p2中有语句S2,希望S1执行后再执行S2。
需要:使进程P1和P2共享一个公用信号量S,并赋予初始值0,将signal(S)操作放在语句S1后面,而在S2语句前面插入wait(S)操作。


管程:代表共享资源的数据结构以及由该共享数据结构实施操作的一组过程所组成的资源管理程序共同构成了一个操作系统资源管理模块。


管程被请求和释放资源的进程所调用。


管程的组成:
1,管程的名称
2,局部于管程的共享数据结构说明
3,对该数据结构进行操作的一组过程
4,局限于管程的共享数据设置初始值的语句


封装于管程内部的数据结构仅能被封装于管程内部的过程所访问,任何管程外的过程都不能访问它;反之,封装于管程内部的过程也仅能访问管程内的数据结构。


所有要访问临界资源时,都只能通过管程间接访问,而管程每次只准许一个进程进入管程,执行管程内的过程,从而实现进程互斥


管程的特性:
模块化,抽象数据类型,信息屏蔽,


作者:钓雪
链接:https://www.zhihu.com/question/30641734/answer/105402533
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


#define MAX 100


/* 定义管程 PC */
monitor PC
{
    int count = 0; 
    /* 我们使用条件变量full 表示被填满的buffer, empty 表示空的buffer */
    conditon full, empty;


    void insert(int item)
    {
        /* 当buffer满的时候,我们在full上将插入操作阻塞 */
        if ( count == MAX ) wait(&full);
        insert_item(item);
        count += 1;
        /* 当buffer不空的时候,我们在empty上唤醒取出操作 */
        if ( count == MAX -1 ) signal(&empty);    
    }


    int remove()
    {
        /* 当buffer空的时候,我们在empty上将取出操作阻塞 */
        if( count == 0 ) wait(&empty);
        remove_item(item);
        count -= 1;
        /* 当buffer不满的时候,我们在full上唤醒插入操作 */
        return item;
        if( count == MAX - 1) signal(&full);
    }
}


    void producer()
    {
        int item;
        item = produce_item();
        /*调用管程中的函数 */
        PC.insert(item);
    }
    
    void consumer()
    {
        int item;
        /*调用管程中的函数 */
        item = PC.remove();
        consumer_item();
    }