Linux高级--3.2.5 “外挂式”死锁监测设计

news2025/1/7 6:08:55

一、生活中“死锁”的场景

三个人,甲乙丙, 甲借了丙的钱,丙借了乙的钱,乙借了甲的钱。

甲找乙还钱,乙说:“别人还我 我就还你 ”,甲说:“好,那我等你”

乙找丙还钱,丙说:“别人还我 我就还你 ”,乙说:“好,那我等你”

丙找甲还钱,甲说:“别人还我 我就还你 ”,丙说:“好,那我等你”

三个人就这么等着,天长地久。

夫妻俩,吵架后,丈夫:你先给我道歉,我就给你道歉。 妻子:你先给我道歉,我就给你道歉。

两个人互相不让就僵持不下了。

二、代码中的死锁场景

代码中的锁就是上面的“钱”,三个人就是三个线程。

代码实例

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


pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;

#define OPEN_LOG

#ifdef OPEN_LOG
	#define log(cnt, ...) fprintf(stdout, "[LOCK][%s][%d]: " cnt "\n", __func__, __LINE__, ##__VA_ARGS__)
#else
	#define log(cnt, ...) NULL
#endif

void *pthread_funA(void *arg) {
    pthread_mutex_lock(&mutex1); // 借钱
    log("get mtx1 success");
    sleep(1);
    log("will get mtx2");
    pthread_mutex_lock(&mutex2); // 要钱
    log("get mtx2 success");
    pthread_mutex_unlock(&mutex2); // 存钱
    pthread_mutex_unlock(&mutex1); // 还钱
    return NULL;
}

void *pthread_funB(void *arg) {
    pthread_mutex_lock(&mutex2);
    log("get mtx2 success");
    sleep(1);
    log("will get mtx3");
    pthread_mutex_lock(&mutex3);
    log("get mtx2 success");
    pthread_mutex_unlock(&mutex3);
    pthread_mutex_unlock(&mutex2);
    return NULL;
}

void *pthread_funC(void *arg) {
    pthread_mutex_lock(&mutex3);
    log("get mtx3 success");
    sleep(1);
    log("will get mtx1");
    pthread_mutex_lock(&mutex1);
    log("get mtx2 success");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex3);
    return NULL;
}

int main(void) {
    pthread_t tid1, tid2, tid3;

    pthread_create(&tid1, NULL, pthread_funA, NULL);
    pthread_create(&tid2, NULL, pthread_funB, NULL);
    pthread_create(&tid3, NULL, pthread_funC, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    return 0;
}

执行后程序不会退出 

$ ./a.out
[LOCK][pthread_funA][21]: get mtx1 success
[LOCK][pthread_funB][33]: get mtx2 success
[LOCK][pthread_funC][45]: get mtx3 success
[LOCK][pthread_funC][47]: will get mtx1
[LOCK][pthread_funB][35]: will get mtx3
[LOCK][pthread_funA][23]: will get mtx2
^C

图示 

 

总结并分析解决方案

通过图示,可以看出三个线程构成一个有向的环路,如果能把两个线程间的的指向(依赖)关系记录下来,并实时检测判断这些关系是否构成一个回路,如果发现有回路则说明有死锁现象。

这就需要“图式”存储结构,我这里选择“邻接表”。

根据代码逻辑,在即将加锁前,找下是否有线程持锁,有的话就构成了一个指向关系,即一条有向边。把这条边关系加入到邻接矩阵中即可。

这里又有个问题,持锁前,找哪个线程已经持过这个锁,则需要在线程持锁后把这个关系保存下来才能找到,所以又需要一个数组或链表保存。

1. 实现带有环路检测的“邻接表” 

2. 数组/链表 保存已经持锁的关系

3. 持锁前把指向关系(有向边)加入到“图”中,并检测是否构成回路。

4. 由于我们的检测代码不能影响原有的项目代码,则需要把线程创建和持锁的操作都用dlsym函数hook主,在里面做我们需要的逻辑即可,这就是“外挂式”操作。

三、“邻接矩阵”的环路检测

这里只提供写好的代码,并分析环路检测的实现。不展开讨论图的实现。(可自己查阅)

/*
    代码为了死锁检测专门写的 邻接链表创建的接口 及 环路检测接口
    并未实现深度、广度搜索函数
*/

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

#define OPEN_LOG

#ifdef OPEN_LOG
	#define log(cnt, ...) fprintf(stdout, "[map][%s][%d]: " cnt "\n", __func__, __LINE__, ##__VA_ARGS__)
#else
	#define log(cnt, ...) NULL
#endif

#define MAX_VNODS   100

typedef uint64_t vnode_id_t;

struct vnode {
    vnode_id_t id;
    uint8_t seekFlag; // 0:unvisted 1:visting 2:visted
    struct vnode *next;
};

struct vnode varry[MAX_VNODS];
char tmplog[1000] = {0};


void map_init(void) {
    int i;
    memset(varry, 0, sizeof(struct vnode) * MAX_VNODS);
}

int get_pos_from_id(vnode_id_t id) {
    int i;
    for (i = 0; i < MAX_VNODS; i++) {
        if (varry[i].id == id) {
            return i;
        }
    }
    log("seek pos err");
    return -1;
}

/// @brief 
/// @param id , must be > 0
/// @return 
int map_add_vnode(vnode_id_t id) {
    int i = get_pos_from_id(0);
    if (i != -1) {
        // log("will add pos:%d", i);
        varry[i].next = NULL;
        varry[i].id = id;
        return 0;       
    }
    return -1;
}

void next_seek(struct vnode *next) {
    if (next == NULL) {
        return;
    } else {
        next_seek(next->next);
        // log("will free: %p", next);
        free(next);
        next = NULL;
    }
    return;
}

int map_rm_vnode(vnode_id_t id) {
    int i = get_pos_from_id(id);
    if (i != -1) {
        varry[i].id = 0;
        if (varry[i].next != NULL) {
            next_seek(varry[i].next);
        }   
    }
    return 0;
}

/// @brief add 'from' point to 'to'
/// @param from 
/// @param to 
/// @return 
int map_add_edge(vnode_id_t from, vnode_id_t to) {
    if (get_pos_from_id(to) == -1) {
        return -1;
    }
    int pos = get_pos_from_id(from);
    if (pos == -1) return -1;
    struct vnode *newNext = malloc(sizeof(struct vnode));
    if (newNext == NULL) {
        log("malloc err");
        exit(EXIT_FAILURE);
    }
    newNext->id = to;
    newNext->next = NULL;

    struct vnode *tmp = &varry[pos];
    for (;tmp->next != NULL; tmp = tmp->next);
    tmp->next = newNext;
    // log("add %p attach to %ld", newNext, from);
    return 0;
}

int map_rm_edge(vnode_id_t from, vnode_id_t to) {
    if (get_pos_from_id(to) == -1) {
        return -1;
    }
    int pos = get_pos_from_id(from);
    if (pos == -1) return -1;
    struct vnode *tmp = &varry[pos];
    for (; tmp != NULL && tmp->next != NULL; tmp = tmp->next) {
        if (tmp->next->id == to) {
            struct vnode *newNext = tmp->next->next;
            free(tmp->next);
            tmp->next = newNext;
            return 0;
        }
    }
    return -1;
}


int traverse_vnode_arry_from_pos(int pos) {
    int i;
    struct vnode *tmp = &varry[pos];
    // log("id: %ld", tmp->id); // 遍历打印每个节点打印时可以打开
    if (varry[pos].seekFlag == 1) return -1; // 已经动态访问中 说明遇到环形关系了
    if (varry[pos].seekFlag == 2) return 0; // 访问过了这个节点下的所有关系 则返回
    varry[pos].seekFlag = 1; // 标记为 开始访问
    sprintf(tmplog + strlen(tmplog), "%ld-->", tmp->id);
    for (tmp = tmp->next; tmp != NULL; tmp = tmp->next) {
        int seekp = get_pos_from_id(tmp->id);
        if (varry[seekp].seekFlag == 0) {
            int ret = traverse_vnode_arry_from_pos(seekp);
            if (ret == -1) return ret;
        } else if (varry[seekp].seekFlag == 1) {
            sprintf(tmplog + strlen(tmplog), "%ld", varry[seekp].id);
            log("has the circle: %ld", varry[seekp].id);
            return -1;
        }
    }
    // pos节点下的所有关系以及递归访问结束没遇到环形关系 标记为 已访问
    varry[pos].seekFlag = 2;
    memset(tmplog, 0, sizeof(tmplog));
    return 0;
}

/// @brief 遍历所有节点
/// @return 如果遇到环形关系 返回-1 否则0
int traverse_all_vnodes() {
    int i;
    for (i = 0; i < MAX_VNODS; i++) {
        if (varry[i].id > 0) varry[i].seekFlag = 0;
    }

    memset(tmplog, 0, sizeof(tmplog));
    for (i = 0; i < MAX_VNODS; i++) {
        if (varry[i].seekFlag || varry[i].id == 0) continue;
        if (traverse_vnode_arry_from_pos(i) == -1) {
            return -1;
        }
    }
    return 0;  
}


int main(int argc, char *argv[]) {
    map_init();
    map_add_vnode(1);
    map_add_vnode(2);
    map_add_vnode(3);
    map_add_vnode(4);
    map_add_vnode(5);
    map_add_edge(1, 2);
    map_add_edge(1, 5);

    map_add_edge(2, 3);
    map_add_edge(3, 4);

    map_add_edge(4, 1);
    map_add_edge(4, 2);

    map_add_edge(5, 2);
    // map_rm_edge(1, 4);

    if (traverse_all_vnodes() < 0) {
        log("has circle: %s", tmplog);
    }

    map_rm_vnode(1);
    map_rm_vnode(2);
    map_rm_vnode(3);
    map_rm_vnode(4);
    map_rm_vnode(5);
    return 0;
}
$ ./a.out
[map][traverse_vnode_arry_from_pos][134]: id: 1
[map][traverse_vnode_arry_from_pos][134]: id: 5
[map][traverse_vnode_arry_from_pos][134]: id: 2
[map][traverse_vnode_arry_from_pos][134]: id: 3
[map][traverse_vnode_arry_from_pos][134]: id: 4
[map][traverse_vnode_arry_from_pos][146]: has the circle: 1
[map][main][192]: has circle: 1-->5-->2-->3-->4-->1

环路检测的核心逻辑在于使用深度优先搜索(DFS)遍历图,并通过标记节点的访问状态来判断是否存在环路。以下是环路检测逻辑的详细解释:


核心思想

  1. 节点访问状态的三种标记

    • 未访问 (unvisited, visitState = 0): 初始状态,节点未被访问。
    • 访问中 (visiting, visitState = 1): 节点正在被访问,表示当前递归路径中已经访问到该节点。
    • 已访问 (visited, visitState = 2): 节点及其所有邻居都已访问完成,递归路径回溯到该节点后,其状态被标记为已访问。
  2. 环路的判定

    • 如果在访问某节点时,其邻居节点的状态为visiting(访问中),说明邻居节点已在当前递归路径中,再次访问到它就形成了一个环。
    • 如果状态为visited,说明该节点已被完全处理,不需要再次递归。
  3. 路径记录

    • 在遍历路径时,记录经过的节点 ID,方便输出完整路径(包括环路路径)。

环路检测逻辑的具体实现

1. traverse_all_vnodes 函数

功能

  • 遍历所有节点,初始化节点的访问状态。
  • 对未访问的节点调用 traverse_check_cycle 递归检查是否存在环路。

逻辑

  • 遍历节点数组 varry
  • 如果节点状态为 unvisited 且有有效 ID,则调用递归函数检查。
  • 如果检测到环路,立即返回并打印路径。
2. traverse_check_cycle 函数

功能

  • 使用递归的方式进行深度优先搜索,检查从当前节点开始的路径中是否存在环路。

逻辑

  1. 检测当前节点状态
    • 如果节点状态为 visiting,说明已经形成环路,记录环路路径并返回 true
    • 如果状态为 visited,说明无需处理,返回 false
  2. 标记当前节点为 visiting
    • 记录当前节点为正在访问状态。
    • 将节点 ID 追加到路径记录中。
  3. 递归访问所有邻居节点
    • 遍历当前节点的邻居(即链表中的 next 节点)。
    • 对每个邻居节点,根据其 ID 找到对应的位置,并递归调用 traverse_check_cycle
    • 如果某个邻居返回 true(即检测到环路),立即返回。
  4. 回溯时标记节点为 visited
    • 如果所有邻居访问完成且没有环路,标记当前节点为已访问状态。

四、dlsym的hook方法

dlsym 是用于动态链接库符号解析的一个常用函数,通常与 LD_PRELOAD 配合使用,可以用来对动态库中的函数进行 Hook(拦截并重定义)。以下是一个详细的说明和示例代码,帮助理解 dlsym 的 Hook 使用。


核心思路

  1. 目的:通过 LD_PRELOAD 替换原生动态库的函数行为。例如,我们希望在调用 malloc 时记录分配信息。
  2. 实现
    • 使用 dlsym(RTLD_NEXT, "function_name") 获取被 Hook 函数的真实地址。
    • 编写同名函数,重写其逻辑,同时调用真实函数以实现功能增强或修改。
  3. 关键点
    • dlsym(RTLD_NEXT, "function_name") 是解析同名函数在后续库中的实际实现。
    • 防止递归调用:在 Hook 函数内部调用真实函数时,必须避免重新进入 Hook 函数。

示例:Hook malloc

目标:拦截 malloc 调用,在分配内存时打印日志,并最终返回原始函数结果。

Code
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

// 定义函数指针以保存原始 malloc 函数地址
static void* (*real_malloc)(size_t size) = NULL;

// 自定义 malloc 函数(重写原生 malloc)
void* malloc(size_t size) {
    if (!real_malloc) {
        // 通过 dlsym 获取原始 malloc 的地址
        real_malloc = dlsym(RTLD_NEXT, "malloc");
        if (!real_malloc) {
            fprintf(stderr, "Error locating original malloc: %s\n", dlerror());
            exit(EXIT_FAILURE);
        }
    }

    // 打印日志信息
    fprintf(stderr, "Hooked malloc: Allocating %zu bytes\n", size);

    // 调用真实的 malloc 函数
    void* ptr = real_malloc(size);

    // 打印分配结果
    fprintf(stderr, "Hooked malloc: Allocated at %p\n", ptr);

    return ptr;
}

// 同理,可以 Hook 其他函数
void free(void* ptr) {
    static void (*real_free)(void*) = NULL;
    if (!real_free) {
        real_free = dlsym(RTLD_NEXT, "free");
        if (!real_free) {
            fprintf(stderr, "Error locating original free: %s\n", dlerror());
            exit(EXIT_FAILURE);
        }
    }

    // 打印日志信息
    fprintf(stderr, "Hooked free: Freeing memory at %p\n", ptr);

    // 调用真实的 free 函数
    real_free(ptr);
}
编译和运行
  1. 编译为共享库:

    gcc -shared -fPIC -o hookmalloc.so hookmalloc.c -ldl
    
  2. 设置环境变量 LD_PRELOAD 并运行程序:

    LD_PRELOAD=./hookmalloc.so ./your_program
    

    例如:

    LD_PRELOAD=./hookmalloc.so ls
    

    在运行 ls 的过程中,所有内存分配调用都会被 Hook 并记录日志。

示例输出

运行 ls 时可能会输出如下内容:

Hooked malloc: Allocating 64 bytes
Hooked malloc: Allocated at 0x55f92d5a7000
Hooked malloc: Allocating 128 bytes
Hooked malloc: Allocated at 0x55f92d5a7080
Hooked free: Freeing memory at 0x55f92d5a7000
Hooked free: Freeing memory at 0x55f92d5a7080

关键问题和解决方法

  1. 递归调用问题

    • 如果在 Hook 函数内部直接调用原始函数,而未正确通过 dlsym 获取原函数地址,可能导致无限递归。
    • 解决:使用 real_malloc 保存原始函数地址,并确保仅调用真实的 malloc
  2. 性能影响

    • Hook 函数会增加额外的日志或逻辑处理,可能对性能有一定影响。
    • 优化:在 Hook 中避免过多的操作,仅记录关键信息。
  3. 多线程安全

    • 使用 dlsym 是线程安全的,但若 Hook 函数中有共享状态,需要加锁保护。

扩展:Hook 多个函数

可以将多个函数的 Hook 实现集中在一个动态库中,例如同时 Hook malloc, free, realloc


应用场景

  1. 调试和监控:追踪内存分配和释放,检测内存泄漏。
  2. 行为修改:对动态库的行为进行增强或调整,例如限制内存分配的最大值。
  3. 模拟环境:在测试中替换特定函数的实现。

六、死锁检测的代码实现

map.h
#ifndef __MAP_H
#define __MAP_H
#include <stdint.h>
#include <pthread.h>

typedef pthread_t vnode_id_t;
#define MAX_VNODS   100 // 支持检测的最多元素数

void map_init(void);

/// @brief 
/// @param id , must be > 0
/// @return -1 失败 0成功
int map_add_vnode(vnode_id_t id);

int map_rm_vnode(vnode_id_t id);

/// @brief add 'from' point to 'to'
/// @param from 
/// @param to 
/// @return  -1 失败 0成功
int map_add_edge(vnode_id_t from, vnode_id_t to);

/// @return  -1 失败 0成功
int map_rm_edge(vnode_id_t from, vnode_id_t to);

/// @brief 遍历所有节点
/// @return 如果遇到环形关系 返回-1 否则0
int traverse_all_vnodes();

#endif
map.c
/*
    代码为了死锁检测专门写的 邻接链表创建的接口 及 环路检测接口
    并未实现深度、广度搜索函数
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "map.h"

#define OPEN_LOG

#ifdef OPEN_LOG
	#define log(cnt, ...) fprintf(stdout, "[map][%s][%d]: " cnt "\n", __func__, __LINE__, ##__VA_ARGS__)
#else
	#define log(cnt, ...) NULL
#endif

struct vnode {
    vnode_id_t id;
    uint8_t seekFlag; // 0:unvisted 1:visting 2:visted
    struct vnode *next;
};

struct vnode varry[MAX_VNODS];
char tmplog[1000] = {0};


void map_init(void) {
    memset(varry, 0, sizeof(struct vnode) * MAX_VNODS);
}

static int get_pos_from_id(vnode_id_t id) {
    int i;
    for (i = 0; i < MAX_VNODS; i++) {
        if (varry[i].id == id) {
            return i;
        }
    }
    // log("seek pos err");
    return -1;
}

/// @brief 
/// @param id , must be > 0
/// @return 
int map_add_vnode(vnode_id_t id) {
    int i = get_pos_from_id(0);
    if (i != -1) {
        // log("will add pos:%d", i);
        varry[i].next = NULL;
        varry[i].id = id;
        return 0;       
    }
    return -1;
}

static void next_seek(struct vnode *next) {
    if (next == NULL) {
        return;
    } else {
        next_seek(next->next);
        // log("will free: %p", next);
        free(next);
        next = NULL;
    }
    return;
}

int map_rm_vnode(vnode_id_t id) {
    int i = get_pos_from_id(id);
    if (i != -1) {
        varry[i].id = 0;
        if (varry[i].next != NULL) {
            next_seek(varry[i].next);
        }   
    }
    return 0;
}

/// @brief add 'from' point to 'to'
/// @param from 
/// @param to 
/// @return 
int map_add_edge(vnode_id_t from, vnode_id_t to) {
    if (get_pos_from_id(to) == -1) {
        return -1;
    }
    int pos = get_pos_from_id(from);
    if (pos == -1) return -1;
    struct vnode *newNext = malloc(sizeof(struct vnode));
    if (newNext == NULL) {
        log("malloc err");
        exit(EXIT_FAILURE);
    }
    newNext->id = to;
    newNext->next = NULL;

    struct vnode *tmp = &varry[pos];
    for (;tmp->next != NULL; tmp = tmp->next);
    tmp->next = newNext;
    // log("add %p attach to %ld", newNext, from);
    return 0;
}

int map_rm_edge(vnode_id_t from, vnode_id_t to) {
    if (get_pos_from_id(to) == -1) {
        return -1;
    }
    int pos = get_pos_from_id(from);
    if (pos == -1) return -1;
    struct vnode *tmp = &varry[pos];
    for (; tmp != NULL && tmp->next != NULL; tmp = tmp->next) {
        if (tmp->next->id == to) {
            struct vnode *newNext = tmp->next->next;
            free(tmp->next);
            tmp->next = newNext;
            return 0;
        }
    }
    return -1;
}


static int traverse_vnode_arry_from_pos(int pos) {
    struct vnode *tmp = &varry[pos];
    // log("id: %ld", tmp->id); // 遍历打印每个节点打印时可以打开
    if (varry[pos].seekFlag == 1) return -1; // 已经动态访问中 说明遇到环形关系了
    if (varry[pos].seekFlag == 2) return 0; // 访问过了这个节点下的所有关系 则返回
    varry[pos].seekFlag = 1; // 标记为 开始访问
    sprintf(tmplog + strlen(tmplog), "%ld-->", tmp->id);
    for (tmp = tmp->next; tmp != NULL; tmp = tmp->next) {
        int seekp = get_pos_from_id(tmp->id);
        if (varry[seekp].seekFlag == 0) {
            int ret = traverse_vnode_arry_from_pos(seekp);
            if (ret == -1) return ret;
        } else if (varry[seekp].seekFlag == 1) {
            sprintf(tmplog + strlen(tmplog), "%ld", varry[seekp].id);
            log("has the circle: %ld", varry[seekp].id);
            return -1;
        }
    }
    // pos节点下的所有关系以及递归访问结束没遇到环形关系 标记为 已访问
    varry[pos].seekFlag = 2;
    memset(tmplog, 0, sizeof(tmplog));
    return 0;
}

/// @brief 遍历所有节点
/// @return 如果遇到环形关系 返回-1 否则0
int traverse_all_vnodes() {
    int i;
    for (i = 0; i < MAX_VNODS; i++) {
        if (varry[i].id > 0) varry[i].seekFlag = 0;
    }

    memset(tmplog, 0, sizeof(tmplog));
    for (i = 0; i < MAX_VNODS; i++) {
        if (varry[i].seekFlag || varry[i].id == 0) continue;
        if (traverse_vnode_arry_from_pos(i) == -1) {
            return -1;
        }
    }
    return 0;  
}

#if 0
int main(int argc, char *argv[]) {
    map_init();
    map_add_vnode(1);
    map_add_vnode(2);
    map_add_vnode(3);
    map_add_vnode(4);
    map_add_vnode(5);
    map_add_edge(1, 2);
    map_add_edge(1, 5);

    map_add_edge(2, 3);
    map_add_edge(3, 4);

    map_add_edge(4, 1);
    map_add_edge(4, 2);

    map_add_edge(5, 2);
    // map_rm_edge(1, 4);

    if (traverse_all_vnodes() < 0) {
        log("has circle: %s", tmplog);
    }

    map_rm_vnode(1);
    map_rm_vnode(2);
    map_rm_vnode(3);
    return 0;
}
#endif
deadlock.h
#ifndef __DEADLOCK_H_
#define __DEADLOCK_H_

void deadlock_init(void);

#endif
deadlock.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "map.h"

#define MAX_THREADS  MAX_VNODS

#define OPEN_LOG

#ifdef OPEN_LOG
	#define log(cnt, ...) fprintf(stdout, "[LOCK][%s][%d]: " cnt "\n", __func__, __LINE__, ##__VA_ARGS__)
#else
	#define log(cnt, ...) NULL
#endif


static int (*pthread_create_f)(pthread_t *, const pthread_attr_t *, void *(*) (void *), void *) = NULL;
static int (*pthread_mutex_lock_f)(pthread_mutex_t *) = NULL;
static int (*pthread_mutex_unlock_f)(pthread_mutex_t *) = NULL;

struct thread_mxt {
    pthread_t tid;
    pthread_mutex_t *mxt;
};

static struct thread_mxt g_info[MAX_THREADS];

void thread_mxt_info_init(void) {
    memset(g_info, 0, sizeof(struct thread_mxt) * MAX_THREADS);
    return;
}

int thread_mxt_info_add(pthread_t tid, pthread_mutex_t *pmxt) {
    int i;
    for (i = 0; i < MAX_THREADS; i++) {
        if (g_info[i].mxt == NULL && g_info[i].tid == 0) {
            g_info[i].mxt = pmxt;
            g_info[i].tid = tid;
            return 0;
        }
    }
    return -1;
}

int thread_mxt_info_rm(pthread_t tid, pthread_mutex_t *pmxt) {
    int i;
    for (i = 0; i < MAX_THREADS; i++) {
        if (g_info[i].mxt == pmxt && g_info[i].tid == tid) {
            g_info[i].mxt = NULL;
            g_info[i].tid = 0;
            return 0;
        }
    }
    return -1;
}

pthread_t thread_mxt_info_seek(pthread_mutex_t *pmxt) {
    int i;
    for (i = 0; i < MAX_THREADS; i++) {
        if (g_info[i].mxt == pmxt) {
            return g_info[i].tid;
        }
    }
    return 0;
}

// 监控线程
void* thread_monitor(void *arg) {
    (void)arg;
    while (1) {
        if (traverse_all_vnodes() == -1) {
            log("find circle");
        }
        sleep(5);
    }
}


void deadlock_init(void) {

    pthread_t tid;

    if (pthread_create_f == NULL) {
        pthread_create_f = dlsym(RTLD_NEXT, "pthread_create");
        if (!pthread_create_f) {
            log("Error locating original malloc: %s", dlerror());
            exit(EXIT_FAILURE);
        }
    }

    if (pthread_mutex_lock_f == NULL) {
        pthread_mutex_lock_f = dlsym(RTLD_NEXT, "pthread_mutex_lock");
        if (!pthread_mutex_lock_f) {
            log("Error locating original malloc: %s", dlerror());
            exit(EXIT_FAILURE);
        }
    }

    if (pthread_mutex_unlock_f == NULL) {
        pthread_mutex_unlock_f = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
        if (!pthread_mutex_unlock_f) {
            log("Error locating original malloc: %s", dlerror());
            exit(EXIT_FAILURE);
        }
    }
    map_init();
    thread_mxt_info_init();

    int ret = pthread_create(&tid, NULL, thread_monitor, NULL);
    if (ret < 0) {
        log("pthread create err");
        return;
    }

}

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg) {
    
    if (pthread_create_f == NULL) return -1;
    int ret = pthread_create_f(thread, attr, start_routine, arg);
    if (ret >= 0) {
        map_add_vnode(*thread);
    } else {
        log("thread create err: %d", ret);
    }
    return ret;
}

int pthread_mutex_lock(pthread_mutex_t *mutex) {
    if (pthread_mutex_lock_f == NULL) return 0;
    pthread_t tid = thread_mxt_info_seek(mutex);
    if (tid > 0) {
        map_add_edge(pthread_self(), tid);
    }
    int ret = pthread_mutex_lock_f(mutex);
    map_rm_edge(pthread_self(), tid);
    thread_mxt_info_add(pthread_self(), mutex);
    return ret;
}
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
    thread_mxt_info_rm(pthread_self(), mutex);
    return pthread_mutex_unlock_f(mutex);
}
test.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include "deadlock.h"

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;

#define OPEN_LOG

#ifdef OPEN_LOG
	#define log(cnt, ...) fprintf(stdout, "[LOCK][%s][%d]: " cnt "\n", __func__, __LINE__, ##__VA_ARGS__)
#else
	#define log(cnt, ...) NULL
#endif

void *pthread_funA(void *arg) {
    (void)arg;
    pthread_mutex_lock(&mutex1); // 借钱
    log("get mtx1 success");
    sleep(1);
    log("will get mtx2");
    pthread_mutex_lock(&mutex2); // 要钱
    log("get mtx2 success");
    pthread_mutex_unlock(&mutex2); // 存钱
    pthread_mutex_unlock(&mutex1); // 还钱
    return NULL;
}

void *pthread_funB(void *arg) {
    (void)arg;
    pthread_mutex_lock(&mutex2);
    log("get mtx2 success");
    sleep(1);
    log("will get mtx3");
    pthread_mutex_lock(&mutex3);
    log("get mtx2 success");
    pthread_mutex_unlock(&mutex3);
    pthread_mutex_unlock(&mutex2);
    return NULL;
}

void *pthread_funC(void *arg) {
    (void)arg;
    pthread_mutex_lock(&mutex3);
    log("get mtx3 success");
    sleep(1);
    log("will get mtx1");
    pthread_mutex_lock(&mutex1);
    log("get mtx2 success");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex3);
    return NULL;
}

int main(void) {
    pthread_t tid1, tid2, tid3;

    deadlock_init();

    pthread_create(&tid1, NULL, pthread_funA, NULL);
    pthread_create(&tid2, NULL, pthread_funB, NULL);
    pthread_create(&tid3, NULL, pthread_funC, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    return 0;
}
Makefile
CC = gcc
AR = ar
CFLAGS = -Wall -Wextra -O0 -pthread  -ldl -g
LIB = libdeadlock.a
TARGET = test
OBJS = deadlock.o map.o

all: $(TARGET)

$(TARGET): $(LIB)
	$(CC) test.c -o $@ -L./ -ldeadlock $(CFLAGS)

$(LIB): $(OBJS)
	$(AR) rcs $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) $(TARGET)
执行结果
$ make
gcc -Wall -Wextra -O0 -pthread  -ldl -g -c deadlock.c -o deadlock.o
ar rcs libdeadlock.a deadlock.o map.o
gcc test.c -o test -L./ -ldeadlock -Wall -Wextra -O0 -pthread  -ldl -g

$ ./test
[LOCK][pthread_funA][22]: get mtx1 success
[LOCK][pthread_funC][48]: get mtx3 success
[LOCK][pthread_funB][35]: get mtx2 success
[LOCK][pthread_funC][50]: will get mtx1
[LOCK][pthread_funB][37]: will get mtx3
[LOCK][pthread_funA][24]: will get mtx2
[map][traverse_vnode_arry_from_pos][140]: has the circle: 140313289017088
[LOCK][thread_monitor][75]: find circle
^C
总结 

 可以看到test.c代码中仅调用了deadlock_init函数,线程创建和加锁解锁的代码均未变动。

map.c中的打印还有问题,你们可自己再优化改下。不过功能没问题。


https://github.com/0voice 
 
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271220.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像去雾 | 基于Matlab的图像去雾系统(四种方法)

图像去雾 | 基于Matlab的图像去雾系统&#xff08;四种方法&#xff09; 目录 图像去雾 | 基于Matlab的图像去雾系统&#xff08;四种方法&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于Matlab的图像去雾系统&#xff08;四种方法&#xff09; 关于图像…

解决Vue中设置el-select的高度不生效问题

el-select是Element UI框架中的一个选择器组件&#xff0c;它允许用户从多个选项中选择一个或多个项目。但这里确存在一个小坑&#xff0c;我们可以看到直接修改el-select的高度是无法生效的 <template><div id"login"><el-select v-model"role…

嵌入式驱动开发详解8(阻塞/非阻塞/异步通信)

文章目录 前言阻塞非阻塞异步通知后续 前言 首先来回顾一下“中断”&#xff0c;中断是处理器提供的一种异步机制&#xff0c;我们配置好中断以后就 可以让处理器去处理其他的事情了&#xff0c;当中断发生以后会触发我们事先设置好的中断服务函数&#xff0c; 在中断服务函数…

人工智能之数学基础:向量内积以及应用

本文重点 向量的点积(Dot Product),又称数量积(Scalar Product)或内积,是线性代数中的一个重要概念。它接受两个向量作为输入,并返回一个实数作为输出。点积不仅在数学和物理学中有广泛应用,而且在人工智能领域也扮演着重要角色。 内积 在数学上,向量x和向量y的内积…

集线器,交换机,路由器,mac地址和ip地址知识记录总结

一篇很不错的视频简介 基本功能 从使用方面来说&#xff0c;都是为了网络传输的标识&#xff0c;和机器确定访问对象 集线器、交换机和路由器 常听到路由器和集线器&#xff0c;下面是区别&#xff1a; 集线器 集线器&#xff1a;一个简单的物理扩展接口数量的物理硬件。…

linux 逻辑卷挂盘

1.输入 sudo-i 转到root账号下。 2. 输入root密码后登录&#xff0c;输入parted -l &#xff0c;发现与以前的挂盘不一样&#xff0c;未挂的盘与系统盘在一起&#xff0c;都是/dev/vda里。 2.输入 lsblkr,查看逻辑分区的设备名称 3. 新增目录 mkdir /data 4. 尝试直接将逻辑分…

python23-常用的第三方库01:request模块-爬虫

requests 模块是 Python 中的一个第三方库&#xff0c;用于发送 HTTP 请求。 它提供了一个简单且直观的 API&#xff0c;使得发送网络请求和解析响应变得非常容易。requests 模块支持各种 HTTP 方法&#xff0c;如 GET、POST、PUT、DELETE 等&#xff0c;并且具有处理 cookies…

解释一下:运放的输入失调电流

输入失调电流 首先看基础部分:这就是同相比例放大器 按照理论计算,输入VIN=0时,输出VOUT应为0,对吧 仿真与理论差距较大,有200多毫伏的偏差,这就是输入偏置电流IBIAS引起的,接着看它的定义 同向和反向输入电流的平均值,也就是Ib1、Ib2求平均,即(Ib1+Ib2)/2 按照下面…

常规继承类对象大小

这个相对简单&#xff0c;只需要考虑类对象大小的累加和内存对齐 #include<iostream> using namespace std;class Base { public:int ma;int mb;char mc; };class Derive :public Base { public:int md; };class Derive2 :public Derive { public:int me; };int main() …

Scrum中敏捷项目经理(Scrum Master)扮演什么角色?

敏捷开发模式已经逐渐被主流的软件研发团队所接受&#xff0c;其中Scrum是最具代表性的敏捷方法之一。Scrum框架中有三个核心角色&#xff1a;Product Owner&#xff08;PO&#xff09;、Scrum Master&#xff08;SM&#xff09;和Development Team&#xff08;DT&#xff09;。…

[网络安全]数据安全领域关键技术总结

去标识计算技术 概念 去标识计算 (Differential Privacy) 是一种数据隐私保护技术&#xff0c;它可以在不暴露个人敏感信息的前提下进行统计分析&#xff0c;并保证对隐私的泄露控制在可承受的范围之内。 底层逻辑 基于差分隐私的去标识计算是指在计算处理中采用一些差分隐私…

手机租赁平台开发实用指南与市场趋势分析

内容概要 在当今快速变化的科技时代&#xff0c;手机租赁平台的发展如火如荼。随着越来越多的人希望使用最新款的智能手机&#xff0c;但又不愿意承担昂贵的购机成本&#xff0c;手机租赁平台应运而生。这种模式不仅为用户提供了灵活的选择&#xff0c;还为企业创造了新的商机…

【从零开始入门unity游戏开发之——C#篇42】C#补充知识——随机数(Random)、多种方法实现string字符串拼接、语句的简写

文章目录 一、随机数1、Random.Next()生成随机整数示例&#xff1a;生成一个随机整数生成指定范围内的随机整数 2、Random.NextSingle生成随机浮点数示例&#xff1a;生成随机浮点数 3、 生成随机字母或字符示例&#xff1a;生成随机字母示例&#xff1a;生成随机小写字母 二、…

overleaf写学术论文常用语法+注意事项+审阅修订

常用语法 导入常用的宏包 \usepackage{cite} \usepackage{amsmath,amssymb,amsfonts} \usepackage{algorithmic} \usepackage{algorithm} \usepackage{graphicx} \usepackage{subfigure} \usepackage{textcomp} \usepackage{xcolor} \usepackage{lettrine} \usepackage{booktab…

Rabbitmq追问1

如果消费端代码异常&#xff0c;未手动确认&#xff0c;那么这个消息去哪里 2024-12-31 21:19:12 如果消费端代码发生异常&#xff0c;未手动确认&#xff08;ACK&#xff09;的情况下&#xff0c;消息的处理行为取决于消息队列的实现和配置&#xff0c;以下是基于 RabbitMQ …

DeepSpeed训练得到checkpoint如何像Huggingface模型一样评测evaluation?zero_to_fp32.py有什么用?怎么用?

DeepSpeed训练得到checkpoint如何像Huggingface模型一样评测evaluation&#xff1f; 具体步骤 首先看一个样例&#xff1a; 这是我用open-instruct框架&#xff0c;使用DeepSpeed训练1728个steps得到的一个checkpoint。请注意&#xff0c;下文我演示用的例子是基于step_1152&…

游戏如何检测Root权限

Root权限&#xff0c;即超级用户权限&#xff0c;在Android系统中&#xff0c;获取Root权限意味着用户可以修改系统文件、移除预装应用、安装特殊应用等。 在Root环境下&#xff0c;游戏面临着相当大的安全隐患&#xff0c;用户获取了最高权限&#xff0c;意味着可以通过各类工…

【UE5 C++课程系列笔记】21——弱指针的简单使用

目录 概念 声明和初始化 转换为共享指针 打破循环引用 弱指针使用警告 概念 在UE C 中&#xff0c;弱指针&#xff08;TWeakPtr &#xff09;也是一种智能指针类型&#xff0c;主要用于解决循环引用问题以及在不需要强引用保证对象始终有效的场景下&#xff0c;提供一种可…

微信小程序中的 storage(本地存储)和内存是两个完全不同的存储区域

这是一个非常关键且容易混淆的概念 既然 this.globalData.appId appId 是将 appId 存储在内存中&#xff0c;为什么微信小程序中的 wx.getStorage 和 wx.setStorage&#xff08;本地存储&#xff09;中没有 appId&#xff0c;并且您提出了一个非常重要的疑问&#xff1a;stor…

DevSecOps自动化在安全关键型软件开发中的实践、Helix QAC Klocwork等SAST工具应用

DevSecOps自动化对于安全关键型软件开发至关重要。 那么&#xff0c;什么是DevSecOps自动化&#xff1f;具有哪些优势&#xff1f;为何助力安全关键型软件开发&#xff1f;让我们一起来深入了解~ 什么是DevSecOps自动化&#xff1f; DevSecOps自动化是指在软件开发生命周期的各…