69天探索操作系统-第14天:理解死锁检测算法和银行家算法

394 阅读11分钟

pro5.avif

1.介绍

死锁检测是操作系统设计和进程管理中的关键方面。本综合指南探讨了死锁检测算法的复杂性,特别强调银行算法。我们将探讨理论概念和实际应用,提供实例代码和详细解释。

2.理解死锁

定义和特征

死锁是指一组进程因为每个进程都持有资源并等待其他进程获取的资源而阻塞的情况。

死锁的条件

  • 互斥: 资源不能同时共享。示例:打印机一次只能由一个进程使用。实现涉及信号量或互斥锁。
  • 持有并等待: 进程在等待其他资源时持有资源。创建资源依赖链。可以通过资源预分配来预防。
  • 无预取: 资源不能被强制从进程中取出。只有持有进程才能释放资源。这是维护系统完整性的关键。
  • 循环等待: 等待资源的进程链。每个进程持有下一个进程所需的资源。每个进程保存的资源在进程之间形成了资源的闭环依赖关系。

真实案例

  • 数据库事务
-- Transaction 1:
UPDATE Account_A SET balance = balance - 100;
UPDATE Account_B SET balance = balance + 100;

-- Transaction 2:
UPDATE Account_B SET balance = balance - 50;
UPDATE Account_A SET balance = balance + 50;
  • 文件系统操作
// Process 1:
lock(file1);
lock(file2);

// Process 2:
lock(file2);
lock(file1);

3.死锁检测机制

资源分配图 (RAG)

表示进程与资源之间关系的有向图。

#include <stdio.h>
#include <stdlib.h>

#define MAX_PROCESSES 10
#define MAX_RESOURCES 10

typedef struct {
    int allocation[MAX_PROCESSES][MAX_RESOURCES];
    int request[MAX_PROCESSES][MAX_RESOURCES];
    int available[MAX_RESOURCES];
    int processes;
    int resources;
} ResourceGraph;

// Initialize Resource Graph
void initializeRAG(ResourceGraph *graph, int p, int r) {
    graph->processes = p;
    graph->resources = r;
    
    // Initialize matrices
    for(int i = 0; i < p; i++) {
        for(int j = 0; j < r; j++) {
            graph->allocation[i][j] = 0;
            graph->request[i][j] = 0;
        }
    }
    
    // Initialize available resources
    for(int i = 0; i < r; i++) {
        graph->available[i] = 0;
    }
}

// Check for cycle in the graph (deadlock detection)
int detectDeadlock(ResourceGraph *graph) {
    int work[MAX_RESOURCES];
    int finish[MAX_PROCESSES] = {0};
    int deadlock = 0;
    
    // Initialize work array
    for(int i = 0; i < graph->resources; i++) {
        work[i] = graph->available[i];
    }
    
    // Find an unfinished process that can be completed
    int found;
    do {
        found = 0;
        for(int i = 0; i < graph->processes; i++) {
            if(!finish[i]) {
                int canComplete = 1;
                
                // Check if process can complete with available resources
                for(int j = 0; j < graph->resources; j++) {
                    if(graph->request[i][j] > work[j]) {
                        canComplete = 0;
                        break;
                    }
                }
                
                if(canComplete) {
                    // Process can complete, release its resources
                    for(int j = 0; j < graph->resources; j++) {
                        work[j] += graph->allocation[i][j];
                    }
                    finish[i] = 1;
                    found = 1;
                }
            }
        }
    } while(found);
    
    // Check if all processes finished
    for(int i = 0; i < graph->processes; i++) {
        if(!finish[i]) {
            deadlock = 1;
            break;
        }
    }
    
    return deadlock;
}

int main() {
    ResourceGraph graph;
    int p = 5, r = 3;
    
    initializeRAG(&graph, p, r);
    
    // Example resource allocation
    graph.allocation[0][0] = 1; graph.allocation[0][1] = 0; graph.allocation[0][2] = 0;
    graph.allocation[1][0] = 2; graph.allocation[1][1] = 0; graph.allocation[1][2] = 2;
    graph.allocation[2][0] = 3; graph.allocation[2][1] = 0; graph.allocation[2][2] = 2;
    graph.allocation[3][0] = 0; graph.allocation[3][1] = 1; graph.allocation[3][2] = 0;
    graph.allocation[4][0] = 0; graph.allocation[4][1] = 0; graph.allocation[4][2] = 2;
    
    // Example resource requests
    graph.request[0][0] = 0; graph.request[0][1] = 0; graph.request[0][2] = 0;
    graph.request[1][0] = 2; graph.request[1][1] = 0; graph.request[1][2] = 2;
    graph.request[2][0] = 0; graph.request[2][1] = 0; graph.request[2][2] = 0;
    graph.request[3][0] = 1; graph.request[3][1] = 0; graph.request[3][2] = 0;
    graph.request[4][0] = 0; graph.request[4][1] = 0; graph.request[4][2] = 2;
    
    // Available resources
    graph.available[0] = 3;
    graph.available[1] = 3;
    graph.available[2] = 2;
    
    if(detectDeadlock(&graph)) {
        printf("Deadlock detected!\n");
    } else {
        printf("No deadlock detected.\n");
    }
    
    return 0;
}

等待图

RAG的一个简化版本,仅显示过程依赖关系。

#include <stdio.h>
#include <stdlib.h>

#define MAX_PROCESSES 10

typedef struct {
    int matrix[MAX_PROCESSES][MAX_PROCESSES];
    int processes;
} WaitForGraph;

// Initialize Wait-for Graph
void initializeWFG(WaitForGraph *graph, int p) {
    graph->processes = p;
    for(int i = 0; i < p; i++) {
        for(int j = 0; j < p; j++) {
            graph->matrix[i][j] = 0;
        }
    }
}

// Detect cycle using DFS
int detectCycleDFS(WaitForGraph *graph, int vertex, int visited[], int recStack[]) {
    if(!visited[vertex]) {
        visited[vertex] = 1;
        recStack[vertex] = 1;
        
        for(int i = 0; i < graph->processes; i++) {
            if(graph->matrix[vertex][i]) {
                if(!visited[i] && detectCycleDFS(graph, i, visited, recStack)) {
                    return 1;
                } else if(recStack[i]) {
                    return 1;
                }
            }
        }
    }
    recStack[vertex] = 0;
    return 0;
}

// Detect deadlock in Wait-for Graph
int detectDeadlock(WaitForGraph *graph) {
    int visited[MAX_PROCESSES] = {0};
    int recStack[MAX_PROCESSES] = {0};
    
    for(int i = 0; i < graph->processes; i++) {
        if(detectCycleDFS(graph, i, visited, recStack)) {
            return 1;
        }
    }
    return 0;
}

int main() {
    WaitForGraph graph;
    int p = 4;
    
    initializeWFG(&graph, p);
    
    // Example: Process 0 waiting for Process 1
    graph.matrix[0][1] = 1;
    // Process 1 waiting for Process 2
    graph.matrix[1][2] = 1;
    // Process 2 waiting for Process 3
    graph.matrix[2][3] = 1;
    // Process 3 waiting for Process 0 (creates a cycle)
    graph.matrix[3][0] = 1;
    
    if(detectDeadlock(&graph)) {
        printf("Deadlock detected!\n");
    } else {
        printf("No deadlock detected.\n");
    }
    
    return 0;
}

4.银行家的算法深入研究

安全算法

安全算法确定系统是否处于安全状态。

#include <stdio.h>
#include <stdlib.h>

#define MAX_PROCESSES 10
#define MAX_RESOURCES 10

typedef struct {
    int allocation[MAX_PROCESSES][MAX_RESOURCES];
    int max[MAX_PROCESSES][MAX_RESOURCES];
    int available[MAX_RESOURCES];
    int need[MAX_PROCESSES][MAX_RESOURCES];
    int processes;
    int resources;
} BankersState;

// Initialize Banker's Algorithm state
void initializeBankers(BankersState *state, int p, int r) {
    state->processes = p;
    state->resources = r;
    
    for(int i = 0; i < p; i++) {
        for(int j = 0; j < r; j++) {
            state->allocation[i][j] = 0;
            state->max[i][j] = 0;
            state->need[i][j] = 0;
        }
    }
    
    for(int i = 0; i < r; i++) {
        state->available[i] = 0;
    }
}

// Calculate need matrix
void calculateNeed(BankersState *state) {
    for(int i = 0; i < state->processes; i++) {
        for(int j = 0; j < state->resources; j++) {
            state->need[i][j] = state->max[i][j] - state->allocation[i][j];
        }
    }
}

// Check if system is in safe state
int isSafe(BankersState *state) {
    int work[MAX_RESOURCES];
    int finish[MAX_PROCESSES] = {0};
    int safeSequence[MAX_PROCESSES];
    int count = 0;
    
    // Initialize work array
    for(int i = 0; i < state->resources; i++) {
        work[i] = state->available[i];
    }
    
    // Find a process that can be completed
    while(count < state->processes) {
        int found = 0;
        
        for(int i = 0; i < state->processes; i++) {
            if(!finish[i]) {
                int canAllocate = 1;
                
                for(int j = 0; j < state->resources; j++) {
                    if(state->need[i][j] > work[j]) {
                        canAllocate = 0;
                        break;
                    }
                }
                
                if(canAllocate) {
                    for(int j = 0; j < state->resources; j++) {
                        work[j] += state->allocation[i][j];
                    }
                    
                    safeSequence[count] = i;
                    finish[i] = 1;
                    count++;
                    found = 1;
                }
            }
        }
        
        if(!found) {
            printf("System is not in safe state.\n");
            return 0;
        }
    }
    
    printf("System is in safe state.\nSafe sequence: ");
    for(int i = 0; i < state->processes; i++) {
        printf("%d ", safeSequence[i]);
    }
    printf("\n");
    
    return 1;
}

int main() {
    BankersState state;
    int p = 5, r = 3;
    
    initializeBankers(&state, p, r);
    
    // Example allocation matrix
    int allocation[5][3] = {
        {0, 1, 0},
        {2, 0, 0},
        {3, 0, 2},
        {2, 1, 1},
        {0, 0, 2}
    };
    
    // Example max matrix
    int max[5][3] = {
        {7, 5, 3},
        {3, 2, 2},
        {9, 0, 2},
        {2, 2, 2},
        {4, 3, 3}
    };
    
    // Available resources
    state.available[0] = 3;
    state.available[1] = 3;
    state.available[2] = 2;
    
    // Copy matrices
    for(int i = 0; i < p; i++) {
        for(int j = 0; j < r; j++) {
            state.allocation[i][j] = allocation[i][j];
            state.max[i][j] = max[i][j];
        }
    }
    
    calculateNeed(&state);
    isSafe(&state);
    
    return 0;
}

5.实现例子

死锁检测

image.png 银行算法包含安全性和资源请求算法:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_PROCESSES 10
#define MAX_RESOURCES 10

typedef struct {
    int processes;
    int resources;
    
    // Main matrices
    int allocation[MAX_PROCESSES][MAX_RESOURCES];
    int max[MAX_PROCESSES][MAX_RESOURCES];
    int need[MAX_PROCESSES][MAX_RESOURCES];
    int available[MAX_RESOURCES];
    
    // For tracking safe sequence
    int safe_sequence[MAX_PROCESSES];
    bool finished[MAX_PROCESSES];
} BankersSystem;

// Initialize the Banker's Algorithm system
void initializeBankers(BankersSystem *bs, int p, int r) {
    bs->processes = p;
    bs->resources = r;
    
    // Initialize all matrices to 0
    for(int i = 0; i < p; i++) {
        for(int j = 0; j < r; j++) {
            bs->allocation[i][j] = 0;
            bs->max[i][j] = 0;
            bs->need[i][j] = 0;
        }
        bs->finished[i] = false;
    }
    
    for(int i = 0; i < r; i++) {
        bs->available[i] = 0;
    }
}

// Calculate Need Matrix
void calculateNeed(BankersSystem *bs) {
    for(int i = 0; i < bs->processes; i++) {
        for(int j = 0; j < bs->resources; j++) {
            bs->need[i][j] = bs->max[i][j] - bs->allocation[i][j];
        }
    }
}

// Check if resources can be allocated to a process
bool canAllocateResources(BankersSystem *bs, int process_id, int work[]) {
    for(int i = 0; i < bs->resources; i++) {
        if(bs->need[process_id][i] > work[i])
            return false;
    }
    return true;
}

// Safety Algorithm
bool isSafeState(BankersSystem *bs) {
    int work[MAX_RESOURCES];
    int completed = 0;
    
    // Initialize work = available
    for(int i = 0; i < bs->resources; i++) {
        work[i] = bs->available[i];
    }
    
    // Reset finished array
    for(int i = 0; i < bs->processes; i++) {
        bs->finished[i] = false;
    }
    
    // Find a safe sequence
    while(completed < bs->processes) {
        bool found = false;
        
        for(int i = 0; i < bs->processes; i++) {
            if(!bs->finished[i] && canAllocateResources(bs, i, work)) {
                // Add process to safe sequence
                bs->safe_sequence[completed] = i;
                
                // Add allocated resources back to work
                for(int j = 0; j < bs->resources; j++) {
                    work[j] += bs->allocation[i][j];
                }
                
                bs->finished[i] = true;
                completed++;
                found = true;
            }
        }
        
        if(!found) {
            printf("System is not in safe state!\n");
            return false;
        }
    }
    
    printf("System is in safe state.\nSafe sequence: ");
    for(int i = 0; i < bs->processes; i++) {
        printf("P%d ", bs->safe_sequence[i]);
    }
    printf("\n");
    
    return true;
}

// Resource Request Algorithm
bool requestResources(BankersSystem *bs, int process_id, int request[]) {
    // Check if request is valid
    for(int i = 0; i < bs->resources; i++) {
        if(request[i] > bs->need[process_id][i]) {
            printf("Error: Process has exceeded its maximum claim!\n");
            return false;
        }
        if(request[i] > bs->available[i]) {
            printf("Error: Resources not available!\n");
            return false;
        }
    }
    
    // Try to allocate resources
    for(int i = 0; i < bs->resources; i++) {
        bs->available[i] -= request[i];
        bs->allocation[process_id][i] += request[i];
        bs->need[process_id][i] -= request[i];
    }
    
    // Check if resulting state is safe
    if(isSafeState(bs)) {
        printf("Resources allocated successfully to Process %d\n", process_id);
        return true;
    }
    
    // If not safe, rollback changes
    for(int i = 0; i < bs->resources; i++) {
        bs->available[i] += request[i];
        bs->allocation[process_id][i] -= request[i];
        bs->need[process_id][i] += request[i];
    }
    
    printf("Request denied: Would lead to unsafe state!\n");
    return false;
}

// Print current system state
void printSystemState(BankersSystem *bs) {
    printf("\nCurrent System State:\n");
    
    printf("\nAllocation Matrix:\n");
    for(int i = 0; i < bs->processes; i++) {
        for(int j = 0; j < bs->resources; j++) {
            printf("%d ", bs->allocation[i][j]);
        }
        printf("\n");
    }
    
    printf("\nMax Matrix:\n");
    for(int i = 0; i < bs->processes; i++) {
        for(int j = 0; j < bs->resources; j++) {
            printf("%d ", bs->max[i][j]);
        }
        printf("\n");
    }
    
    printf("\nNeed Matrix:\n");
    for(int i = 0; i < bs->processes; i++) {
        for(int j = 0; j < bs->resources; j++) {
            printf("%d ", bs->need[i][j]);
        }
        printf("\n");
    }
    
    printf("\nAvailable Resources:\n");
    for(int i = 0; i < bs->resources; i++) {
        printf("%d ", bs->available[i]);
    }
    printf("\n");
}

int main() {
    BankersSystem bs;
    int processes = 5;
    int resources = 3;
    
    initializeBankers(&bs, processes, resources);
    
    // Initialize Available Resources
    bs.available[0] = 3;
    bs.available[1] = 3;
    bs.available[2] = 2;
    
    // Initialize Allocation Matrix
    int allocation[5][3] = {
        {0, 1, 0},
        {2, 0, 0},
        {3, 0, 2},
        {2, 1, 1},
        {0, 0, 2}
    };
    
    // Initialize Maximum Matrix
    int max[5][3] = {
        {7, 5, 3},
        {3, 2, 2},
        {9, 0, 2},
        {2, 2, 2},
        {4, 3, 3}
    };
    
    // Set up initial state
    for(int i = 0; i < processes; i++) {
        for(int j = 0; j < resources; j++) {
            bs.allocation[i][j] = allocation[i][j];
            bs.max[i][j] = max[i][j];
        }
    }
    
    calculateNeed(&bs);
    printSystemState(&bs);
    
    // Check if system is in safe state
    printf("\nChecking initial state safety...\n");
    isSafeState(&bs);
    
    // Try to request resources
    printf("\nTesting resource request...\n");
    int request[3] = {1, 0, 2};
    requestResources(&bs, 1, request);
    
    return 0;
}

6.性能分析

不同死锁检测算法的性能指标:

AlgorithmTime ComplexitySpace Complexity
资源分配图 Resource Allocation GraphO(P + R)O(P * R)
等待图 Wait-for GraphO(P²)O(P²)
银行家算法 Banker's AlgorithmO(P² * R)O(P * R)
(P = processes, R = resources)

7.常见挑战与解决方案

  • 恢复机制: 解决方案:实施基于优先级的过程终止。实现过程优先级系统。资源重新分配策略。
  • 可扩展性问题: 挑战:随着系统增长,检测算法变得更慢。解决方案:实施分层检测方法。分区资源管理。
#include <stdio.h>
#include <stdlib.h>

#define MAX_PROCESSES 10
#define MAX_RESOURCES 10

typedef struct {
    int pid;
    int priority;
    int age;
    int resources_held[MAX_RESOURCES];
} Process;

typedef struct {
    Process processes[MAX_PROCESSES];
    int num_processes;
    int num_resources;
} DeadlockRecovery;

// Initialize recovery system
void initializeRecovery(DeadlockRecovery *dr, int num_proc, int num_res) {
    dr->num_processes = num_proc;
    dr->num_resources = num_res;
    
    for(int i = 0; i < num_proc; i++) {
        dr->processes[i].pid = i;
        dr->processes[i].priority = rand() % 10; // 0-9 priority
        dr->processes[i].age = 0;
        
        for(int j = 0; j < num_res; j++) {
            dr->processes[i].resources_held[j] = 0;
        }
    }
}

// Select victim process for termination
int selectVictimProcess(DeadlockRecovery *dr) {
    int victim = -1;
    int lowest_priority = 999;
    
    for(int i = 0; i < dr->num_processes; i++) {
        int current_priority = dr->processes[i].priority - (dr->processes[i].age / 10);
        
        if(current_priority < lowest_priority) {
            lowest_priority = current_priority;
            victim = i;
        }
    }
    
    return victim;
}

// Release resources held by terminated process
void releaseResources(DeadlockRecovery *dr, int victim) {
    printf("Releasing resources held by process %d\n", victim);
    
    for(int i = 0; i < dr->num_resources; i++) {
        if(dr->processes[victim].resources_held[i] > 0) {
            printf("Released resource %d\n", i);
            dr->processes[victim].resources_held[i] = 0;
        }
    }
}

// Handle deadlock recovery
void recoverFromDeadlock(DeadlockRecovery *dr) {
    int victim = selectVictimProcess(dr);
    
    if(victim != -1) {
        printf("Selected process %d for termination\n", victim);
        releaseResources(dr, victim);
        
        // Age remaining processes
        for(int i = 0; i < dr->num_processes; i++) {
            if(i != victim) {
                dr->processes[i].age++;
            }
        }
    }
}

int main() {
    DeadlockRecovery dr;
    initializeRecovery(&dr, 5, 3);
    
    // Simulate resource allocation
    dr.processes[0].resources_held[0] = 1;
    dr.processes[1].resources_held[1] = 1;
    dr.processes[2].resources_held[2] = 1;
    
    // Simulate deadlock detection and recovery
    printf("Simulating deadlock recovery...\n");
    recoverFromDeadlock(&dr);
    
    return 0;
}

8.最佳示例

  • 预防策略: 实施资源排序、使用超时机制、定期系统状态验证。
  • 检测频率: 定期检查而非持续监控、基于事件的检测、资源利用率阈值。
  • 恢复策略: 进程优先级管理、资源抢占策略、检查点和回滚机制。
#include <stdio.h>
#include <stdlib.h>

#define MAX_RESOURCES 10

typedef struct {
    int resource_id;
    int priority;
    int available;
} Resource;

typedef struct {
    Resource resources[MAX_RESOURCES];
    int num_resources;
} ResourceOrderingSystem;

// Initialize resource ordering system
void initializeResourceOrdering(ResourceOrderingSystem *ros, int num_res) {
    ros->num_resources = num_res;
    
    for(int i = 0; i < num_res; i++) {
        ros->resources[i].resource_id = i;
        ros->resources[i].priority = i; // Higher number = higher priority
        ros->resources[i].available = 1;
    }
}

// Check if resource request follows ordering
int isValidResourceRequest(ResourceOrderingSystem *ros, int current_resource, int requested_resource) {
    if(current_resource == -1) return 1; // First resource request
    
    return ros->resources[current_resource].priority < ros->resources[requested_resource].priority;
}

// Request resource
int requestResource(ResourceOrderingSystem *ros, int process_id, int current_resource, int requested_resource) {
    if(!isValidResourceRequest(ros, current_resource, requested_resource)) {
        printf("Invalid resource request order! Must request resources in increasing priority.\n");
        return 0;
    }
    
    if(!ros->resources[requested_resource].available) {
        printf("Resource %d not available\n", requested_resource);
        return 0;
    }
    
    ros->resources[requested_resource].available = 0;
    printf("Resource %d allocated to process %d\n", requested_resource, process_id);
    return 1;
}

int main() {
    ResourceOrderingSystem ros;
    initializeResourceOrdering(&ros, 5);
    
    // Simulate resource requests
    int process_id = 1;
    int current_resource = -1;
    
    // Valid resource ordering
    if(requestResource(&ros, process_id, current_resource, 0)) {
        current_resource = 0;
    }
    
    if(requestResource(&ros, process_id, current_resource, 2)) {
        current_resource = 2;
    }
    
    // Invalid resource ordering (will fail)
    requestResource(&ros, process_id, current_resource, 1);
    
    return 0;
}

9.结论

死锁检测和预防仍然是操作系统设计中的关键方面。算法的选择取决于系统的要求、规模和性能限制。虽然银行算法较为保守,但它提供了一种安全的数据分配方法,而其他检测方法则提供了在准确性与性能之间进行权衡的不同选择。

10.参考资料和进一步阅读

  • Silberschatz, A., Galvin, P. B., & Gagne, G. (2018). Operating System Concepts (10th ed.)
  • Tanenbaum, A. S. (2015). Modern Operating Systems (4th ed.)
  • Stallings, W. (2018). Operating Systems: Internals and Design Principles (9th ed.)
  • Dijkstra, E. W. (1965). Solution of a Problem in Concurrent Programming Control
  • Coffman, E. G., Elphick, M., & Shoshani, A. (1971). System Deadlocks

进一步阅读:

  • "The Deadlock Problem: A Comprehensive Review" - ACM Computing Surveys
  • "Resource Allocation Graphs in Operating Systems" - IEEE Transactions
  • "Performance Analysis of Deadlock Detection Algorithms" - Journal of Systems and Software

This concludes our Day 14. Day 15 will cover Memory Management Overview, focusing on memory hierarchy and related concepts.