操作系统死锁预防

226 阅读4分钟

死锁预防

预防死锁是通过破坏产生死锁必要条件实现的。由于互斥条件是非共享设备所必须具备的条件,不能改变

1、破坏“请求和保持”条件

为了能够破坏“请求和保持”条件,系统必须保证做到:当一个进程在请求资源时,它不能持有不可抢占资源。该保证可通过以下两个不同的协议实现。

第一种协议

该协议规定,所有进程在开始运行之前,必须一次性地申请其在整个运行过程中所需的全部资源。此时,若系统有足够的可分配资源,则可把其需要的所有资源分配给它。从而破坏了“请求”条件。系统在分配资源时,只要有一种资源不能满足进程的要求,则即使所需的其他资源都空闲也不分配给该进程,而是让其等待。从而破坏了“保持”条件,可以预防发生死锁。

第一种协议的优点是简单、易行、安全。但缺点也极其明显。①资源被严重浪费,严重地降低了资源利用率。有些资源可能仅在运行初期或运行快结束时才会使用②进程经常会发生饥饿现象。因为仅当进程在获得其所需的全部资源后才能开始运行,所以可能由于个别资源长期被其他进程占用

第二种协议

它允许一个进程只获得运行初期所需的资源后,便开始运行。进程运行过程中再逐步释放已分配给自己的、且已用毕的全部资源,然后再请求新的所需资源。

2、破坏“不可抢占”条件

为了能破坏“不可抢占”条件,协议中规定,当一个已经保持了某些不可抢占资源的进程提出新的资源请求而不能得到满足时,它必须释放已经保持的所有资源。该方法实现起来比较复杂,且需要付出很大的代价。因为一个不可抢占的资源(如打印机、刻录机等)在使用一段时间后被抢占,可能会造成进程前一阶段工作的失效。这种策略还可能延长了进程的周转时间增加了系统开销降低了系统吞吐量

3、破坏“循环等待”条件

一个能保证“循环等待”条件不成立的方法是,对系统的所有资源类型进行线性排序:规定每个进程必须一定的顺序请求资源。如果需要多个同类资源单元,则必须一起请求。在采用这种策略后所形成的资源分配图中,不可能再出现环路,因而破坏了“循环等待”条件。如何来规定每种资源的序号是十分重要的。通常应根据大多数进程需要资源的先后顺序来确定资源的序号。一般情况下,进程总会首先输入程序和数据,然后进行运算,最后将运算结果输出。这种预防死锁策略和前两种策略相比,其资源利用率系统吞吐量都有比较明显的改善。但也存在下列问题。第一,为系统中各类资源规定的序号必须相对稳定,这限制了新类型设备的增加。第二,尽管在为资源的类型分配序号时已经考虑到了大多数作业在实际使用这些资源时的顺序,但也经常会发生这种情况:作业使用各类资源的顺序与系统规定的顺序不同,造成对资源的浪费。第三,为了方便用户,系统对用户在编程时所施加的限制条件应尽量少,然而这种按规定次序申请资源的方法必然会限制用户进行简单、自主的编程。

4、 代码示例

std::mutex res1;
std::mutex res2;

void process1() {
    res1.lock();
    std::cout << "进程1 请求资源1" << std::endl;
    res2.lock();
    std::cout << "进程1 请求资源2" << std::endl;

    std::this_thread::sleep_for(std::chrono::seconds(2));
    res2.unlock();
    res1.unlock();
    std::cout << "进程1 释放资源" << std::endl;
}

void process2() {
    res2.lock();
    std::cout << "进程2 请求资源2" << std::endl;
    res1.lock();
    std::cout << "进程2 请求资源1" << std::endl;

    std::this_thread::sleep_for(std::chrono::seconds(2));
    res1.unlock();
    res2.unlock();
    std::cout << "进程2 释放资源" << std::endl;
}

int main() {
    std::thread t1(process1);
    std::thread t2(process2);

    t1.join();
    t2.join();

    return 0;
}

DLcmd

通过破坏 “占有并等待” 条件让进程一次性请求所有需要的资源,避免在持有部分资源的情况下再去请求其他资源。

void process1() {
    std::lock(res1, res2);
    std::lock_guard<std::mutex> lock1(res1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(res2, std::adopt_lock);
    std::cout << "进程1 请求资源1和资源2" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "进程1 释放资源" << std::endl;
}

void process2() {
    std::lock(res1, res2);
    std::lock_guard<std::mutex> lock1(res1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(res2, std::adopt_lock);
    std::cout << "进程2 请求资源1和资源2" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "进程2 释放资源" << std::endl;
}

PDLcmd