手写C/C++死锁检测

简介: 手写C/C++死锁检测

1 死锁的概念

死锁,是指多个线程或者进程在运行过程中因争夺资源而造成的一种僵局,当进程或者线程
处于这种僵持状态,若无外力作用,它们将无法再向前推进。如下图所示,线程 A 想获取线
程 B 的锁,线程 B 想获取线程 C 的锁,线程 C 想获取线程 A 的锁,从而构建了一个资源获取环。

怎么理解一个线程想获取另一个线程的锁呢?
先定义锁,把pthread_mutex_t 理解为锁(实际是一种线程同步机制)
pthread_mutex_t r1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r3 = PTHREAD_MUTEX_INITIALIZER;
获取锁 pthread_mutex_lock(ra) pthread_mutex_lock(rb) pthread_mutex_lock(rc)
只有当 r1 r2 r3 被释放了,也就是调用 pthread_mutex_unlock(ra), ... 之后,相应的锁r1, r2, r3 才能被其它线程所获得,即pthread_mutex_lock,否则pthread_mutex_lock不成功,线程阻塞

用一段代码来演示这种死锁

pthread_mutex_t r1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r3 = PTHREAD_MUTEX_INITIALIZER;

void *t1_cb(void *arg) {
   
   

    pthread_mutex_lock(&r1);
    sleep(1);
    pthread_mutex_lock(&r2);    
    pthread_mutex_unlock(&r2);
    pthread_mutex_unlock(&r1);
}

void *t2_cb(void *arg) {
   
   
    pthread_mutex_lock(&r2);
    sleep(1);
    pthread_mutex_lock(&r1);
    pthread_mutex_unlock(&r1);
    pthread_mutex_unlock(&r2);
}

void *t3_cb(void *arg) {
   
   
    pthread_mutex_lock(&r3);
    sleep(1);
    pthread_mutex_lock(&r1);

    pthread_mutex_unlock(&r1);
    pthread_mutex_unlock(&r3);

}

// deadlock
// 
int main() {
   
   

    pthread_t t1, t2, t3;

    pthread_create(&t1, NULL, t1_cb, NULL);
    pthread_create(&t2, NULL, t2_cb, NULL);
    pthread_create(&t3, NULL, t3_cb, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);
    printf("complete\n");

}

程序将会 打印信息 “complete” ,不会退出,死锁。

为什么?

r1, r2, r3 都是互斥锁,同时只能被一个线程拥有(持有)(lock)一次
t1 持有r1 等 r2 unlock , t1 才会unlock r1
t2 持有r2 等 r3 unlock , t2 才会unlock r2
t3 持有r3 等 r1 unlock , t3 才会unlock r3

就这样,t1,t2,t3都等不到任何unlock,僵持住了,死锁

2 检测死锁

t1 持有r1 等 r2 unlock , t1 才会unlock r1
t2 持有r2 等 r3 unlock , t2 才会unlock r2
t3 持有r3 等 r1 unlock , t3 才会unlock r3
简化一下:
t1 等 r2 ,记作 t1-->r2
t2 等 r3 ,记作 t2-->r3
t3 等 r1 ,记作 t3-->r1
考虑一下t1,t2,t3都持有 各自的锁
t1 等 r2 ,记作 t1,r1-->r2
t2 等 r3 ,记作 t2,r2-->r3
t3 等 r1 ,记作 t3,r3-->r1
这里引申出有向图:
在这里插入图片描述
如果用代码实现检测出代码中的锁的申请存在上面的有向图结构,是不是就可以检测出代码中的死锁了?!

3 用代码实现死锁检测

思路:用dlsym对 pthread_mutex_lock , pthread_mutex_unlock进行hook,
在hook的代码里面实现有向图边的建立与删除(在一个线程T中进行):lock前建立有向图的边,unlock的时候删除这条边。
当程序出现死锁的时候,线程T中通多扫描所有的边,找出其中的有向图,从而得知哪里出现了死锁。

3.1 hook部分

if (!pthread_mutex_lock_f)
pthread_mutex_lock_f = dlsym(RTLD_NEXT, "pthread_mutex_lock");

if (!pthread_mutex_unlock_f)
pthread_mutex_unlock_f = dlsym(RTLD_NEXT, "pthread_mutex_unlock");

3.2 实现有向图的代码

数据结构

typedef unsigned long int uint64;


#define MAX        100

enum Type {
   
   PROCESS, RESOURCE};

struct source_type {
   
   

    uint64 id;
    enum Type type;

    uint64 lock_id;
    int degress;
};

struct vertex {
   
   

    struct source_type s;
    struct vertex *next;

};

struct task_graph {
   
   

    struct vertex list[MAX];
    int num;

    struct source_type locklist[MAX];
    int lockidx; //

    pthread_mutex_t mutex;
};

增删改查

struct vertex *create_vertex(struct source_type type) {
   
   
    struct vertex *tex = (struct vertex *)malloc(sizeof(struct vertex ));
    tex->s = type;
    tex->next = NULL;
    return tex;
}

int search_vertex(struct source_type type) {
   
   
    int i = 0;
    for (i = 0;i < tg->num;i ++) {
   
   
        if (tg->list[i].s.type == type.type && tg->list[i].s.id == type.id) {
   
   
            return i;
        }
    }
    return -1;
}

void add_vertex(struct source_type type) {
   
   
    if (search_vertex(type) == -1) {
   
   
        tg->list[tg->num].s = type;
        tg->list[tg->num].next = NULL;
        tg->num ++;
    }
}

int add_edge(struct source_type from, struct source_type to) {
   
   
    add_vertex(from);
    add_vertex(to);
    struct vertex *v = &(tg->list[search_vertex(from)]);
    while (v->next != NULL) {
   
   
        v = v->next;
    }
    v->next = create_vertex(to);
}


int verify_edge(struct source_type i, struct source_type j) {
   
   
    if (tg->num == 0) return 0;
    int idx = search_vertex(i);
    if (idx == -1) {
   
   
        return 0;
    }
    struct vertex *v = &(tg->list[idx]);
    while (v != NULL) {
   
   
        if (v->s.id == j.id) return 1;
        v = v->next;        
    }
    return 0;
}
int remove_edge(struct source_type from, struct source_type to) {
   
   

    int idxi = search_vertex(from);
    int idxj = search_vertex(to);

    if (idxi != -1 && idxj != -1) {
   
   

        struct vertex *v = &tg->list[idxi];
        struct vertex *remove;

        while (v->next != NULL) {
   
   

            if (v->next->s.id == to.id) {
   
   

                remove = v->next;
                v->next = v->next->next;
                free(remove);
                break;
            }
            v = v->next;
        }
    }
}

文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:链接

相关文章
|
6月前
|
IDE Linux 开发工具
内存泄漏检测工具Valgrind:C++代码问题检测的利器(一)
内存泄漏检测工具Valgrind:C++代码问题检测的利器
1386 0
|
6月前
|
存储 资源调度 算法
Opencv(C++)系列学习---SIFT、SURF、ORB算子特征检测
Opencv(C++)系列学习---SIFT、SURF、ORB算子特征检测
319 0
|
6月前
|
C++
【C++11特性篇】C++11中の【override】【final】关键字——帮助用户检测是否重写
【C++11特性篇】C++11中の【override】【final】关键字——帮助用户检测是否重写
|
4月前
|
NoSQL Linux Redis
c++开发redis module问题之避免在fork后子进程中发生死锁,如何解决
c++开发redis module问题之避免在fork后子进程中发生死锁,如何解决
|
6月前
|
算法 NoSQL Linux
Linux C++环境下避免死锁的全面策略解析与实践指南
Linux C++环境下避免死锁的全面策略解析与实践指南
163 0
|
6月前
|
缓存 Linux iOS开发
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
552 1
|
6月前
|
缓存 测试技术 开发工具
内存泄漏检测工具Valgrind:C++代码问题检测的利器(二)
内存泄漏检测工具Valgrind:C++代码问题检测的利器
165 0
|
6月前
|
存储 算法 C语言
【C/C++ 应用开发 检测文件 】详解 C/C++ 中常用的 5 种文件存在检查方式
【C/C++ 应用开发 检测文件 】详解 C/C++ 中常用的 5 种文件存在检查方式
191 0
|
6月前
|
存储 计算机视觉 C++
Opencv(C++)学习系列---特征点检测和匹配
Opencv(C++)学习系列---特征点检测和匹配
295 0
|
6月前
|
算法 C++
C++哈希表企业级运用----DNA序列的检测
C++哈希表企业级运用----DNA序列的检测