进程互斥的实现方法

发布时间 2023-09-20 22:42:07作者: Euler0525

软件实现

单标志

int turn = 0;  // 进程ID
// P0进程
while (turn != 0);  // 进入区(死循环)
critical section;   // 临界区
turn = 1;           // 退出区
remainder section;  // 剩余区
// P1进程
while (turn != 1);
critical section;
turn = 0;
remainder section;

两个进程交替执行。如果P0turn修改为0后不再访问临界区,P1进程修改turn0后,turn的值永远为0P1无法再访问临界区,违背了空闲让进的原则。

双标志先检查

bool flag[2];  // flag[0]表示P0进入临界区, flag[1]表示P1进入临界区
// P0进程
while (flag[1]);
flag[0] = true;
critical section;
flag[0] = false;
remainder section;
// P1进程
while (flag[0]);
flag[1] = true;
critical section;
flag[1] = false;
remainder section;

但是按照如下执行顺序(两个while判断时标志均未修改,巧了~)

  1. P0将要进入临界区,判断flag[1]false,此时flag[0]还是false
  2. 此时P1判断flag[0]flase,可以进入临界区;
  3. 两个进程同时访问了临界区

违背了忙则等待的原则

双标志后检查

bool flag[2];  // flag[0]表示P0进入临界区, flag[1]表示P1进入临界区
// P0进程
flag[0] = true;
while (flag[1]);
critical section;
flag[0] = false;
remainder section;
// P1进程
flag[1] = true;
while (flag[0]);
critical section;
flag[1] = false;
remainder section;

违背了空闲让进、有限等待的原则(都将标志改为true后再判断,都进不去了~)

Peterson算法

bool flag[2];
int turn = 0;
// P0进程
flag[0] = true
turn = 1;
while (flag[1] && turn == 1);
critical section;
flag[0] = false;
remainder action;
// P1进程
flag[1] = true
turn = 0;
while (flag[0] && turn == 0);
critical section;
flag[1] = false;
remainder action;

违背了让权等待的原则while判断始终执行,占用CPU。

硬件实现

中断屏蔽

禁止一切中断,CPU执行完临界区前不会切换。

缺点:

  • 可能会被滥用
  • 关中断时间长影响效率
  • 不适用于多处理机
  • 只适用于内核态进程

Test-And-Set(TS指令)

bool TestAndSet(bool *lock)
{
    bool old;
    old = *lock;
    *lock = true;  // 加锁
    return old;    // 返回原来的lock
}

while (TestAndSet(&lock));
// 临界区
*lock = false;  // 解锁
// 剩余区

原子操作,不会被打断。但是违背了让权等待

Swap指令

// lock表示临界区是否被加锁
bool old = true;
while (old)
    swap(&lock, &old);
// 临界区
lock = false;  // 解锁
// 剩余区

但是违背了让权等待

信号量

PV操作:等待唤醒机制

信号量:停车场内的空余车位,共享资源剩余数量

  • 整数型信号量(死循环→忙等待)
int S = 1;  // 初始化,表示可用资源数

// wait原语,相当于进入区
void wait(int S)
{
    while (S <= 0);  // 资源不够,循环等待 违背“让权等待”,发生忙等待
    S -= 1;          // 如果资源数够,占用一个(或多个)
}
// signal原语,相当于退出区
void signal(int S)
{
    S += 1;  // 使用资源后释放
}
/*******************************/
// 进程Pn
...
wait(S);     // 进入区,申请资源
访问共享资源;  // 临界区
signal(S);   // 退出区,释放资源
...
  • 记录型
typedef struct
{
    int value;          // 剩余资源数量
    struct process *L;  // 等待队列
}semaphore;

void wait(semaphore S)
{
    // 申请资源
    --S.value;
    if (S.value < 0)
    {
        block(S.L);  // block原语阻塞(原子操作,中间不会被打断),进入等待队列
    }
}

void signal(semaphore S)
{
    // 释放资源
    ++S.value;
    if (S.value <= 0)
    {
        wakeup(S.L);  // wakeup原语唤醒队头进程(阻塞→就绪)
    }
}