计算机操作系统笔记(5)--进程管理之经典进程的同步问题

一 生产者–消费者问题

生产者进程和消费者进程都以异步方式运行,但它们之间必须保持同步。
同步模式:生产者和消费者之间的关系
互斥模式:不同生产者之间的关系、不同消费者之间的关系

①利用记录型信号量解决生产者–消费者问题
可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;
利用信号量empty和full分别表示缓冲池中空缓冲池和满缓冲池的数量。

//mutex: 生产者间,消费者间互斥使用缓冲区
//empty: 缓冲区的空闲容量
//full: 缓冲区的已占容量
Var mutex, empty, full: semaphore := 1, n, 0;
    buffer: array[0, ..., n-1] of item;
    in, out: integer := 0, 0;
begin
    parbegin
        producer://生产者
            begin
                repeat
                    ...
                    生产一个产品放入nextp;
                    ...
                    //进入区
                    wait(empty);
                    wait(mutex);
                    //临界区
                    buffer(in) := nextp;
                    in := (in+1) mod n;
                    //退出区
                    signal(mutex);
                    signal(full);
                until false;
            end

        consumer://消费者
            begin
                repeat
                    //进入区
                    wait(full);
                    wait(mutex);
                    //临界区
                    mextc := buffer(out);
                    out := (out+1) mod n;
                    //退出区
                    signal(mutex);
                    signal(empty);
                    //剩余区
                    消费nextc中的产品;
                until false;
            end
    parend
end

注意:
1)每个程序中用于实现互斥的wait(mutex)signal(mutex)必须成对出现
2)对资源信号量emptyfullwaitsignal操作,同样需要成对出现,但处于不同的程序中
3)在每个程序中的多个wait操作顺序不颠倒。应先先申请资源信号量,后申请互斥信号量,否则可能引起进程死锁。siganl可以没有顺序。

②利用AND信号量解决生产者–消费者问题

//mutex: 生产者间,消费者间互斥使用缓冲区
//empty: 缓冲区的空闲容量
//full: 缓冲区的已占容量
Var mutex, empty, full: semaphore := 1, n, 0;
    buffer: array[0, ..., n-1] of item;
    in, out: integer := 0, 0;
begin
    parbegin
        producer://生产者
            begin
                repeat
                    ...
                    生产一个产品放入nextp;
                    ...
                    //进入区
                    Swait(empty, mutex);//不必考虑信号量先后问题
                    //临界区
                    buffer(in) := nextp;
                    in := (in+1) mod n;
                    //退出区
                    Ssignal(mutex, full);
                until false;
            end

        consumer://消费者
            begin
                repeat
                    //进入区
                    Swait(full, mutex);//不必考虑信号量先后问题
                    //临界区
                    mextc := buffer(out);
                    out := (out+1) mod n;
                    //退出区
                    Ssignal(mutex, empty);
                    //剩余区
                    消费nextc中的产品;
                until false;
            end
    parend
end

二 哲学家进餐问题

五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在桌子上有五只碗和五支筷子,他们的生方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。
可见:
相邻两位不能同时进餐;
最多只能有两人同时进餐。

①利用记录型信号量解决哲学家时餐问题
放在桌子的筷子是临界资源,在一段时间内只允许一个哲学家使用。为实现对筷子的互斥使用,用一个信号量表示一只筷子,五个信号量构成信号量数组。
Var chopstick: array[0, ..., 4] of semaphore := 1
所有信号量均被初始化为1。

Var chopstick: array[0, ..., 4] of semaphore := 1
//第i位哲学家的活动可描述为:
    repeat
        //进入区
        wait(chopstick[i]);
        wait(chopstick[(i+1) mod 5]);
        //临界区
        ...
        eat;
        ...
        //退出区
        signal(chopstick[i]);
        signal(chopstick[(i+1) mod 5]);
        //剩余区
        ...
        think;
    until false;

以上算法虽能保证相邻两位不会同时进餐,但可能引起死锁
例如五拉哲学家同时饥饿而各自拿起左边的筷子时,就会使五个信号量chopstick均为0,都将因无筷子可拿而无限等待。

解决办法
1)同时最多允许4位哲学家拿左边筷子(能够保证至少一位哲学家能够进餐)。

Var chopstick: array[0, ..., 4] of semaphore := 1;
var count: semaphore := 4;//资源信号量
repeat
    //进入区
    wait(count);
    wait(chopstick[i]);
    wait(chopstick[(i+1) mod 5]);
    //临界区
    ...
    eat;
    ...
    //退出区
    signal(chopstick[i]);
    signal(chopstick[(i+1) mod 5]);
    signal(count);
    //剩余区
    ...
    think;
until false;

2)哲学家必须拿到两支筷子(使用AND信号量),否则释放已拿到的筷子。

详见下方②

3)奇数号哲学家先拿左边筷子,再拿右边筷子;偶数号哲学家先拿右边筷子,再拿左边筷子。

Var chopstick: array[0, ..., 4] of semaphore := 1;
Var i: integer;
repeat
    //进入区
    if i mod 2=1 then
        begin
            wait(chopstick[i]);//先拿左边筷子
            wait(chopstick[(i+1) mod 5]);//再拿右边筷子
        end
    else
        begin
            wait(chopstick[(i+1) mod 5]);//先拿右边筷子
            wait(chopstick[i]);//再拿左边筷子
        end
    end
    //临界区
    eat;
    //退出区
    signal(chopstick[i]);
    signal(chopstick[(i+1) mod 5]);
    //剩余区
    ...
    think;
until false;

②利用AND信号量机制解决哲学家进餐问题
在哲学家时餐问题中,要求每个哲学家先获得两个临界资源(筷子)后方能进餐。本质上是AND同步问题。

Var chopstick: array[0, ..., 4] of semaphore := (1, 1, 1, 1, 1);//表示chopstick[i]可用
Philosopher i;
repeat
    think;
    //进入区
    Swait(chopstick[(i+1) mod 5], chopstick[i]);
    //临界区
    eat;
    //退出区
    Ssignal(chopstick[(i+1) mod 5], chopstick[i]);
until false;

三 读者–写者问题

一个数据文件或记录可被多个进程共享。
1)只要求读文件的进程为“Reader进程”,其它进程则称为“Writer进程”。
2)允许多个进程同进读一个共享对象,但不允许一个Writer进程和其他Reader进程或Writer进程同时访问共享对象。

“读者–写者问题”是保证一个Writer进程必须与其他进程互斥地访问共享对象的同步问题。
读–读共享;写–写互斥;写–读互斥。

①利用记录型信号量解决读者–写者问题
互斥信号量wmutex:实现Reader与Writer进程间在读或写时的互斥。
互斥信号量rmutex:实现Reader进程间互斥访问Readcount的信号量。
整型变量Readcount:表示正在读的进程数目。

由于只有一个Reader进程在读,便不允许Writer进程写。所以仅当Readcount=0(即无Reader进程在读)时,Reader才需要执行Wait(wmutex)操作。若Wait(wmutex)操作成功,Reader进程便可去读,相应地,做Readcount+1操作。

同理,仅当Reader进程在执行了Readcount-1=0时,才需要执行signal(wmutex)操作,以便让Writer进程写。

//Wmutex:读-写互斥;写-写互斥
//Rmutex:读间访问Readcount互斥
//Readcount:记录记者进程数
Var wmutex, rmutex: semaphore := 1, 1;
    Readcount: integer := 0;
begin
    parbegin
        Reader:begin//读进程
            repeat
                //进入区
                wait(rmutex);//子进入区
                if Readcount=0 then wait(wmutex);
                Readcount := Readcount + 1;//子临界区
                signal(rmutex);//子退出区
                //临界区
                ...
                读;
                ...
                //退出区
                wait(rmutex);
                Readcount := Readcount - 1;
                if Readcount=0 then signal(wmutex);
                signal(rmutex);
            until false;
        end

        Writer:begin//写进程
            repeat
                wait(wnutex);
                写;
                signal(wnutex);
            until false;
        end
    parend
end

评价:能实现读者–写者问题,但读优先,对写者不公平。

②利用信号量集机制实现读者–写者问题
引入信号量L,并赋予其初值RN,通过执行Swait(L, 1, 1)操作,来控制读者的数目(即最多允许RN个读者同时读)。

每当有一个读者进入时,就要先执行Swait(L, 1, 1)操作,使L的值减1。当有RN个读者进入读后,L便减为0,第RN+1个读者要进入读时,必然会因Swait(L, 1, 1)操作失败而阻塞。

//L:控制读者进程的数目<=RN
//Mx:实现读-写互斥;写-写互斥
Var RN: integer;
    L, mx: semaphore : RN, 1;
begin
    parbegin
        reader:begin
            repeat
                Swait(L, 1, 1);
                Swait(mx, 1, 0);//a
                ...
                读;
                ...
                Ssignal(L, 1);
            until false;
        end

        writer:begin
            repeat
                Swait(mx, 1, 1; L, RN, 0);//b
                写;
                Ssignal(mx, 1);
            until false;
        end
    parend
end

注释a
Swait(mx, 1, 0)语句起着开关的作用。只要无writer进程进入写,即mx=1,reader进程就可以进入读。但只要一旦有writer进程进入写时,即mx=0,则任何reader进程就都无法进入读。

注释b
Swait(mx, 1, 1; L, RN, 0)语句表示仅当既无writer进程在写(即mx=1),又无reader进程在读(即L=RN),writer进程才能进入临界区写。

你可能感兴趣的:(学习笔记)