【操作系统】死锁

一、做题!资源分配问题(计算类)

1. 最小死锁条件计算

题目描述 关键公式 答案 原理说明
8台设备,k个进程各需3台(临界2台) k*(3-1) ≥ 8 可能会死的k最小值:4 满足∑(最大需求-1) ≥ 总资源
n台设备,3进程需[3,4,5]台(临界[2,3,4]) (3-1)+(4-1)+(5-1) ≥ n 可能会死的n最小值:9 满足∑(最大需求-1) ≥ 总资源

2. 死锁进程数判定

案例:3设备4进程
结论:最少需要3个进程形成循环等待链才会死锁

二、必背!死锁处理策略对比

1. 三大解决策略

策略 实现方法 优点 缺点
预防 破坏四大必要条件 绝对避免死锁,能确保不死 资源利用率低
避免 银行家算法 动态分配更灵活 需预知最大需求,不能判断系统是否死了
检测恢复 资源分配图检测+剥夺资源 不限制进程行为,但提供解除手段 恢复过程复杂

2. 死锁预防具体措施-破坏四大必要条件

破坏条件 实施方法 示例
互斥条件 使用共享资源 只读文件共享访问
占有并等待 一次性分配所有资源 进程启动前申请全部资源
非抢占条件 允许强制回收资源 数据库事务回滚机制
循环等待条件 顺序资源分配法 统一按R1→R2→R3顺序申请

3. 死锁避免-避免系统处于不安全状态

银行家算法:当申请资源时,系统会预分配并检查预分配是否是安全状态。

我也不背代码,遇到题目现推:5个苹果,分了3个,还剩2个,如果有人释放1个,那么现在还有3个能分,以此类推。

/**
 * 银行家算法实现类
 */
class BankersAlgorithm {
            
  /**
   * @param {number} processCount - 进程数量
   * @param {number} resourceCount - 资源类型数量
   * @param {number[][]} maxDemand - 最大需求矩阵 [进程][资源]
   * @param {number[][]} allocation - 已分配矩阵 [进程][资源]
   * @param {number[]} available - 可用资源向量
   */
  constructor(processCount, resourceCount, maxDemand, allocation, available) {
            
    this.processCount = processCount;
    this.resourceCount = resourceCount;
    this.max = maxDemand;
    this.allocation = allocation;
    this.available = [...available];
    // 计算需求矩阵 = 最大需求 - 已分配
    this.need = maxDemand.map((proc, i) => 
      proc.map((res, j) => res - allocation[i][j])
    );
    this.safeSequence = [];
  }

  /**
   * 检查当前系统状态是否安全
   * @returns {boolean|number[]} 安全序列数组或false
   */
  isSafe() {
            
    const work = [...this.available];
    const finish = new Array(this.processCount).fill(false);
    this.safeSequence = [];

    // 尝试找到可以完成的进程
    let found;
    do {
            
      found = false;
      for (let i = 0; i < this.processCount; i++) {
            
        // 检查进程是否未完成且需求可满足
        if (!finish[i] && this.canFulfill(i, work)) {
            
          // 模拟释放资源
          for (let j = 0; j < this.resourceCount; j++) {
            
            work[j] += this.allocation[i][j];
          }
          finish[i] = true;
          this.safeSequence.push(i);
          found = true;
        }
      }
    } while (found);

    // 检查所有进程是否完成
    return finish.every(f => f) ? this.safeSequence : false;
  }

  /**
   * 检查资源请求是否安全
   * @param {number} processId - 请求进程ID
   * @param {number[]} request - 请求资源向量
   * @returns {boolean} 是否允许分配
   */
  requestResources(processId, request) {
            
    // 1. 检查请求是否超过需求
    for (let j = 0; j < this.resourceCount; j++) {
            
      if (request[j] > this.need[processId][j]) {
            
        console.error(`错误:进程${
              processId}请求超过最大需求`);
        return false;
      }
    }

    // 2. 检查请求是否超过可用资源
    for (let j = 0; j < this.resourceCount; j++) {
            
      if (request[j] > this.available[j]) {
            
        console.error(`错误:进程${
              processId}请求超过可用资源`);
        return false;
      }
    }

    // 3. 尝试分配资源
    for (let j = 0; j < this.resourceCount; j++) {
            
      this.available[j] -= request[j];
      this.allocation[processId][j] += request[j];
      this.need[processId][j] -= request[j];
    }

    // 4. 检查分配后是否安全
    const isSafe = this.isSafe();
    if (!isSafe) {
            
      // 回滚分配
      for (let j = 0; j < this.resourceCount; j++) {
            
        this.available[j] += request[j];
        this.allocation[processId][j] -= request[j];
        this.need[processId][j] += request[j];
      }
      console.warn(`警告:分配给进程${
              processId}会导致系统不安全`);
    }
    return !!isSafe;
  }

  /**
   * 检查当前工作资源能否满足进程需求
   * @private
   */
  canFulfill(processId, work) {
            
    return this.need[processId].every((res, j) => res <= work[j]);
  }
}

// ================= 测试用例 =================
// 示例系统状态(3个进程,4类资源)
const processCount = 3;
const resourceCount = 4;

// 最大需求矩阵
const maxDemand = [
  [3, 1, 4, 2],
  [2, 5, 1, 3],
  [4, 2, 3, 1]
];

// 已分配矩阵
const allocation = [
  [1, 0, 2, 1],
  [0, 2, 1, 0],
  [1, 0, 1, 1]
];

// 可用资源
const available = [1, 2, 0, 1];

// 初始化银行家算法
const banker = new BankersAlgorithm(
  processCount, 
  resourceCount, 
  maxDemand, 
  allocation, 
  available
);

// 测试1:检查初始状态安全性
const safeSeq = banker.isSafe();
console.log('安全序列:', safeSeq || '不安全');

// 测试2:进程1请求资源 [1, 0, 0, 1]
const request1 = [1, 0, 0, 1];
console.log('
进程1请求[1,0,0,1]:', 
  banker.requestResources(1, request1) ? '允许' : '拒绝');

// 测试3:进程0请求资源 [2, 0, 1, 0](超过可用)
const request2 = [2, 0, 1, 0];
console.log('进程0请求[2,0,1,0]:', 
  banker.requestResources(0, request2) ? '允许' : '拒绝');

三、临界区互斥原则

1. 四大基本原则

原则 实现要求 典型违规案例
互斥访问 临界区同一时间仅允许一个进程进入 多核CPU未加锁的共享变量
空闲让进 无进程在临界区时应立即允许进入 忙等待不释放CPU
有限等待 等待时间必须有上限 优先级反转导致饥饿
让权等待 无法进入时应立即阻塞 自旋锁(spinlock)占用CPU

下列同步机制中能实现让权等待的是:信号量方法【主动放弃CPU

让权等待:进程不能进入临界时,应马上主动放开CPU,防止忙等待

2. 实现方法对比

类型 具体方法 适用场景
软件方法 Peterson算法 单处理器简单系统
硬件方法 原子指令(TSL/XCHG) 多核高性能场景
系统支持 信号量(semaphore) 复杂同步需求
现代方案 RCU(Read-Copy-Update) Linux内核同步

四、结论

死锁特征

系统死锁时必然有两个或两个以上进程会处于阻塞态
存在循环等待链
系统吞吐量骤降

银行家算法核心

安全状态 ⇔ ∃安全序列使得: 
∀i, 已分配_i + 剩余 ≥ 最大需求_i

实际系统应用

Linux默认采用死锁检测(OOM Killer)
数据库系统多使用超时回滚机制

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容