【Linux】 Linux 死锁

文章目录

      • [Linux 死锁](#Linux 死锁)
        • [1. 什么是死锁?](#1. 什么是死锁?)
        • [2. 死锁的四个必要条件](#2. 死锁的四个必要条件)
        • [3. 死锁的检测](#3. 死锁的检测)
        • [4. 死锁的预防与避免](#4. 死锁的预防与避免)
        • [5. 死锁的处理策略](#5. 死锁的处理策略)
        • [6. 实例:死锁的代码示例](#6. 实例:死锁的代码示例)
        • [7. 死锁的解决方法](#7. 死锁的解决方法)

Linux 死锁

在并发编程中,死锁是一个常见且棘手的问题。它发生在两个或多个线程互相等待彼此释放资源时,从而导致程序无法继续执行。本文将深入探讨死锁的概念、发生条件、如何检测以及预防策略。

1. 什么是死锁?

死锁指的是多个进程或线程因为互相等待对方持有的资源,导致这些进程或线程都无法继续执行的情况。死锁的典型场景是线程 A 锁住了资源 X,并等待资源 Y,而线程 B 锁住了资源 Y,并等待资源 X。由于双方都在等待对方释放资源,最终进入僵局。

2. 死锁的四个必要条件

死锁的发生需要满足以下四个必要条件:

  1. 互斥条件(Mutual Exclusion):至少有一个资源必须处于非共享的模式下,即某个资源一次只能被一个线程使用。
  2. 占有且等待条件(Hold and Wait):一个进程已经获得了某个资源,但又在等待其他资源,同时不释放它已占有的资源。
  3. 不可剥夺条件(No Preemption):进程已经获得的资源在未使用完毕之前,不能被强制剥夺。
  4. 循环等待条件(Circular Wait):存在一个进程链,使得每个进程都在等待链中的下一个进程所占有的资源。

如果以上四个条件同时满足,死锁就可能发生。

3. 死锁的检测

死锁的检测通常涉及检查系统中是否存在循环等待。操作系统可以通过资源分配图(Resource Allocation Graph,RAG)来检测死锁。在这个图中,节点代表进程和资源,边表示进程对资源的占用和请求。若图中存在一个环,则说明系统可能处于死锁状态。

在实际的 Linux 系统中,可以通过 pstop 等命令监视系统的状态,或者编写特定的算法检测死锁。但是,手动检测死锁并不总是简单,因此预防策略往往是更好的选择。

4. 死锁的预防与避免

避免死锁的主要策略是破坏前面提到的四个必要条件之一。常见的死锁预防方法包括:

  1. 破坏互斥条件:使资源尽可能变为共享资源。某些资源(如读写锁)可以允许多个线程同时访问。

  2. 破坏占有且等待条件:要求进程在开始时一次性申请所有需要的资源。这样可以避免在获得部分资源后继续等待其他资源的情况。

  3. 破坏不可剥夺条件:允许操作系统强制剥夺某些资源。在某些情况下,如果一个进程需要其他资源而无法获取,可以通过释放当前资源,等待一段时间后重新尝试获取所有资源。

  4. 破坏循环等待条件:为所有资源排序,并要求进程按照预定义的顺序请求资源。这样可以避免循环等待的发生。

5. 死锁的处理策略

针对死锁的处理策略主要分为三种:

  1. 预防策略:通过设计系统时避免死锁的发生,具体方法如上文提到的打破死锁的四个条件之一。这种方法需要在系统设计之初进行规划。

  2. 避免策略 :在运行时避免进入可能引发死锁的状态。比如,操作系统可以使用银行家算法(Banker's Algorithm)来动态评估资源分配是否安全。如果系统处于不安全状态,则阻止资源的分配,从而避免死锁。

  3. 检测和恢复策略:允许死锁的发生,然后通过检测机制来识别死锁,并采取措施恢复。例如,终止一个或多个进程,或者回滚某些进程的操作。

6. 实例:死锁的代码示例

下面是一个简单的死锁代码示例,在该示例中,两个线程分别获取不同的锁,然后尝试获取对方已经占有的锁,最终导致死锁:

c 复制代码
#include <pthread.h>
#include <stdio.h>

pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

void* thread1_func(void* arg) {
    pthread_mutex_lock(&lock1);
    printf("Thread 1 acquired lock 1\n");
    sleep(1); // 模拟一些工作
    pthread_mutex_lock(&lock2); // 等待获取 lock2,发生死锁
    printf("Thread 1 acquired lock 2\n");
    pthread_mutex_unlock(&lock2);
    pthread_mutex_unlock(&lock1);
    return NULL;
}

void* thread2_func(void* arg) {
    pthread_mutex_lock(&lock2);
    printf("Thread 2 acquired lock 2\n");
    sleep(1); // 模拟一些工作
    pthread_mutex_lock(&lock1); // 等待获取 lock1,发生死锁
    printf("Thread 2 acquired lock 1\n");
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    pthread_create(&thread1, NULL, thread1_func, NULL);
    pthread_create(&thread2, NULL, thread2_func, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}

在这个例子中,线程 1 获取了 lock1,线程 2 获取了 lock2,然后它们互相等待对方释放资源,导致死锁。

7. 死锁的解决方法

为了避免上面的死锁,可以引入锁的顺序策略,即线程按照相同的顺序获取锁。例如,修改代码,使两个线程都先获取 lock1,再获取 lock2,从而打破循环等待条件。

c 复制代码
void* thread1_func(void* arg) {
    pthread_mutex_lock(&lock1);
    pthread_mutex_lock(&lock2);
    // 执行一些操作
    pthread_mutex_unlock(&lock2);
    pthread_mutex_unlock(&lock1);
    return NULL;
}

通过保证锁的获取顺序一致,可以有效地避免死锁的发生。

相关推荐
CYX_cheng2 分钟前
算法基础-二分查找
算法
码哝小鱼7 分钟前
iptables限制网速
linux·服务器·网络
Persistence is gold9 分钟前
cassandra指定配置文件的docker启动方法
运维·docker·容器
leaoleao沄15 分钟前
linux-IO-进程-线程(相关函数)
linux·运维·服务器
mikey棒棒棒19 分钟前
算法练习题25——合并多项式
java·算法·hashmap·哈希·多项式
i嗑盐の小F20 分钟前
【IEEE&ACM Fellow、CCF组委】第三届人工智能与智能信息处理国际学术会议(AIIIP 2024)
人工智能·深度学习·算法·机器学习·自然语言处理·信号处理
学步_技术20 分钟前
Python编码系列—Python工厂方法模式:构建灵活对象的秘诀
开发语言·python·工厂方法模式
DANGAOGAO20 分钟前
蓝桥杯4. Fizz Buzz 经典问题
算法·蓝桥杯
frank006007134 分钟前
linux 使用mdadm 创建raid0 nvme 磁盘
linux·运维
绿白尼36 分钟前
进程与线程
linux