线程死锁检测组件逻辑与源码

死锁介绍

任务的执行体之间互相持有对方所需的资源而不释放,形成了相互制约而都无法继续执行任务的情况,被称为“死锁”。

死锁案例

线程A持有锁a不释放,需要去获取锁b才能继续执行任务,

线程B持有锁b不释放,需要去获取锁c才能继续执行任务,

线程C持有锁c不释放,需要去获取锁d才能继续执行任务,

线程D持有锁d不释放,需要去获取锁a才能继续执行任务。

线程ABCD陷入了逻辑套死,形成了一个环,因此谁都无法继续执行任务。

如何判断形成死锁

判断形成死锁,只需要解决问题:即能判断线程和线程之后互相持有资源不释放,形成了一个环。

如何判断形成环

需要存储两种关系。1:锁和线程之间的关系。2、线程和线程之间的关系。

针对1,锁和线程之间的关系是多对一的,即,一个线程持有多个锁,但是一个锁最多只能能被一个线程持有。我们可以构建一个结构体数组存储这些关系,一个结构体存储着锁地址和线程id,一个结构体代表着一种关系。

针对2,线程和线程之间的关系,表示一个线程想要获取的资源,是否被另一个线程持有?如果被另一个线程持有,那么是哪个线程?需要用有向图记录,有向图有很多种记录方式,在编程中最常体现为邻接表。如图:

这个就是一个邻接表,线程tA想持有的资源在tB中,线程tB想持有的资源在tC中,以此类推,形成了一个环,也就是一个死锁。邻接表的结构是多个链表的头节点被用数组的方式串起来。

死锁检测组件编写逻辑

主要需要解决一下3块逻辑

线程获取锁

线程解锁

线程如果解锁,需要从锁-持有方数组中删除该关系。若邻接表中记录了其他线程在向该线程索取资源,那么删除也删除该关系。

监控环形成

需要另起一个线程负责监控邻接表,定期检查线程之间是否形成了死锁关系,如果形成了死锁关系,那么将这种关系输出到某处,提示程序员需要修改逻辑bug。

检测组件代码以及应用

代码比较长,了解了逻辑之后可以,可以直接在需要检测的函数的最前面调用init_hook()和start_check()这两个接口,以及提前配置好这两个接口所依赖的各层级函数、变量、定义等。

#define _GNU_SOURCE
#include <dlfcn.h>#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>#include <stdint.h>#if 1typedef unsigned long int uint64;#define MAX		100enum 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 task_graph *tg = NULL; 
int path[MAX+1];
int visited[MAX];
int k = 0;
int deadlock = 0;
type
struct vertex *create_vertex(struct source_type 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;}}}void print_deadlock(void) { //如果形成了死锁,那么打印环int i = 0;printf("cycle : ");for (i = 0;i < k-1;i ++) {printf("%ld --> ", tg->list[path[i]].s.id);}printf("%ld\n", tg->list[path[i]].s.id);}int DFS(int idx) {struct vertex *ver = &tg->list[idx];if (visited[idx] == 1) {path[k++] = idx;print_deadlock();deadlock = 1;return 0;}visited[idx] = 1;path[k++] = idx;while (ver->next != NULL) {DFS(search_vertex(ver->next->s));k --;ver = ver->next;}return 1;}int search_for_cycle(int idx) { //查询是否形成了环struct vertex *ver = &tg->list[idx];visited[idx] = 1;k = 0;path[k++] = idx;while (ver->next != NULL) {int i = 0;for (i = 0;i < tg->num;i ++) {if (i == idx) continue;visited[i] = 0;}for (i = 1;i <= MAX;i ++) {path[i] = -1;}k = 1;DFS(search_vertex(ver->next->s));ver = ver->next;}}#endif// #if 1int search_lock(uint64 lock) { //int i = 0;for (i = 0;i < tg->lockidx;i ++) {if (tg->locklist[i].lock_id == lock) {return i;}}return -1;
}int search_empty_lock(uint64 lock) {int i = 0;for (i = 0;i < tg->lockidx;i ++) {if (tg->locklist[i].lock_id == 0) {return i;}}return tg->lockidx;}void lock_before(uint64_t tid, uint64_t lockaddr) {/*1. 	if (lockaddr) {tid --> lockaddr.tid;}*/int idx = 0;for (idx = 0;idx < tg->lockidx;idx ++) {if (tg->locklist[idx].lock_id == lockaddr) { // struct source_type from;from.id = tid;from.type = PROCESS;add_vertex(from);struct source_type to;to.id = tg->locklist[idx].id;to.type = PROCESS;add_vertex(to);tg->locklist[idx].degress ++;if (!verify_edge(from, to))add_edge(from, to);}}}void lock_after(uint64_t tid, uint64_t lockaddr) {/*if (!lockaddr) {tid --> lockaddr;} else {lockaddr.tid = tid;tid -> lockaddr;}*/int idx = 0;if (-1 == (idx = search_lock(lockaddr))) {// int eidx = search_empty_lock(lockaddr);tg->locklist[eidx].id = tid;tg->locklist[eidx].lock_id = lockaddr;tg->lockidx ++;} else {struct source_type from;from.id = tid;from.type = PROCESS;add_vertex(from);struct source_type to;to.id = tg->locklist[idx].id;to.type = PROCESS;add_vertex(to);tg->locklist[idx].degress --;if (verify_edge(from, to))remove_edge(from, to);tg->locklist[idx].id = tid;}}void unlock_after(uint64_t tid, uint64_t lockaddr) {// lockaddr.tid = 0;int idx = search_lock(lockaddr);if (tg->locklist[idx].degress == 0) {tg->locklist[idx].id = 0;tg->locklist[idx].lock_id = 0;}}void check_dead_lock(void) {int i = 0;deadlock = 0;for (i = 0;i < tg->num;i ++) {if (deadlock == 1) break;search_for_cycle(i);}if (deadlock == 0) {printf("no deadlock\n");}}static void *thread_routine(void *args) {while (1) {sleep(5);check_dead_lock();}}void start_check(void) {tg = (struct task_graph*)malloc(sizeof(struct task_graph));tg->num = 0;tg->lockidx = 0;pthread_t tid;pthread_create(&tid, NULL, thread_routine, NULL);}// hook
// define
typedef int (*pthread_mutex_lock_t)(pthread_mutex_t *mutex);
pthread_mutex_lock_t pthread_mutex_lock_f = NULL;typedef int (*pthread_mutex_unlock_t)(pthread_mutex_t *mutex);
pthread_mutex_unlock_t pthread_mutex_unlock_f = NULL;// implement
int pthread_mutex_lock(pthread_mutex_t *mutex) {pthread_t selfid = pthread_self();lock_before((uint64_t)selfid, (uint64_t)mutex);pthread_mutex_lock_f(mutex);lock_after((uint64_t)selfid, (uint64_t)mutex);}int pthread_mutex_unlock(pthread_mutex_t *mutex) {pthread_mutex_unlock_f(mutex);pthread_t selfid = pthread_self();unlock_after((uint64_t)selfid, (uint64_t)mutex);}// init
void init_hook(void) {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");}#endif// #if 1 //samplepthread_mutex_t r1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r3 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r4 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t r5 = PTHREAD_MUTEX_INITIALIZER;void *t1_cb(void *arg) {printf("t1: %ld\n", pthread_self());pthread_mutex_lock(&r1);sleep(1);pthread_mutex_lock(&r2);pthread_mutex_unlock(&r2);pthread_mutex_unlock(&r1);}void *t2_cb(void *arg) {printf("t2: %ld\n", pthread_self());pthread_mutex_lock(&r2);sleep(1);pthread_mutex_lock(&r3);pthread_mutex_unlock(&r3);pthread_mutex_unlock(&r2);}void *t3_cb(void *arg) {printf("t3: %ld\n", pthread_self());pthread_mutex_lock(&r3);sleep(1);pthread_mutex_lock(&r4);pthread_mutex_unlock(&r4);pthread_mutex_unlock(&r3);}void *t4_cb(void *arg) {printf("t4: %ld\n", pthread_self());pthread_mutex_lock(&r4);sleep(1);pthread_mutex_lock(&r5);pthread_mutex_unlock(&r5);pthread_mutex_unlock(&r4);}void *t5_cb(void *arg) {printf("t5: %ld\n", pthread_self());pthread_mutex_lock(&r1);sleep(1);pthread_mutex_lock(&r5);pthread_mutex_unlock(&r5);pthread_mutex_unlock(&r1);}// deadlock
// int main() {init_hook();  //重载lock与unlock函数,保留原有功能的基础上增加lock_before.lock_after,unlock_after操作start_check(); //另起一个线程定期检查是否形成死锁//形成了死锁pthread_t t1, t2, t3, t4, t5;pthread_create(&t1, NULL, t1_cb, NULL);pthread_create(&t2, NULL, t2_cb, NULL);pthread_create(&t3, NULL, t3_cb, NULL);pthread_create(&t4, NULL, t4_cb, NULL);pthread_create(&t5, NULL, t5_cb, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);pthread_join(t5, NULL);printf("complete\n");}#endif

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

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

相关文章

k8s陈述式资源管理(命令行)

1、资源管理 &#xff08;1&#xff09;陈述式资源管理&#xff08;常用——查、增&#xff09; 使用kubectl工具进行命令行管理 ①特点&#xff1a;对资源的增删查比较方便&#xff0c;对改不友好 ②优点&#xff1a;90%以上的场景都可以满足 ③缺点&#xff1a;命令冗长…

canvas绘制圆点示例

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

如何移除视频中的背景音乐或人物声音

移除视频声音是将视频指定的声音移除&#xff0c;可以选择移除人物声音还是视频的背景音乐&#xff0c;方便实现二次创作。 小编给大家推荐一些方法帮助大家更轻松地移除视频中的背景音乐或人物声音&#xff0c;有兴趣的朋友请自行百度查找&#xff0c;或小程序查找 1、方法&a…

从入门到精通,30天带你学会C++【第十四天:洛谷选题讲解】

彩蛋 这么长的目录应该没人看吧。 Bi------------------------------------------------------------------------------- 目录 Everyday English 前言 函数 sqrt ( ) for循环 题目网址 分析题意 思路点拨 优化程序 AC代码 AC截图 数学 if判断 题目网址 思路…

视频剪辑技巧:轻松制作短视频,一键合并、剪辑、添加背景音乐

随着社交媒体的普及&#xff0c;短视频已是分享生活、娱乐和传递信息的重要方式。如果要制作短视频&#xff0c;但又不熟悉复杂的视频编辑软件&#xff0c;那么本文将讲解一些实用的视频剪辑技巧&#xff0c;轻松制作出高质量的短视频。现在一起来看看云炫AI智剪如何批量合并视…

【HarmonyOS开发】分布式应用的开发实践(元旦快乐)

元旦快乐&#xff0c;再见2023&#xff0c;加油2024&#xff0c;未来可期&#xff0c;愿新的一年带来健康、幸福和成功&#xff01;&#x1f4aa; &#x1f4aa;&#x1f4aa; 多种设备之间能够实现硬件互助、资源共享&#xff0c;依赖的关键技术包括分布式软总线、分布式设备虚…

机器学习基本概念及模型简单代码(自用)

监督学习 监督学习是机器学习的一种方法&#xff0c;其中我们教导模型如何做出预测或决策&#xff0c;通过使用包含输入和对应输出的已标注数据集进行训练。这种方法的关键特点是利用这些标注数据**&#xff08;即带有正确答案的数据&#xff09;**来指导模型的学习过程。 一言…

uni-app 前后端调用实例 基于Springboot 数据列表显示实现

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

【ArcGIS微课1000例】0084:甘肃积石山地震震中100km范围内历史灾害点分布图(2005-2020)

甘肃积石山地震震中100km范围内历史灾害点分布图(2005-2020)。 文章目录 一、成果预览二、实验数据三、符号化四、地图整饰一、成果预览 本实验最终效果图如下所示: 二、实验数据 以下数据可以从本专栏配套的实验数据包中0084.rar中获取。 1. 历史灾害数据。为2005-2020时…

深度学习|10.5 卷积步长 10.6 三维卷积

文章目录 10.5 卷积步长10. 6 三维卷积![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/b5bfa24f57964b0f81f9602f5780c278.png) 10.5 卷积步长 卷积步长是指每计算一次卷积&#xff0c;卷积移动的距离。 设步长为k&#xff0c;原矩阵规模为nxn&#xff0c;核矩阵…

BIOS:计算机中的特洛伊木马

内容概述&#xff1a; 由于主板制造商在计算机启动时用来显示品牌徽标的图像分析组件相关的问题&#xff0c;多个安全漏洞&#xff08;统称为 LogoFAIL&#xff09;允许攻击者干扰计算机设备的启动过程并安装 bootkit。x86 和 ARM 设备都面临风险。主板固件供应链安全公司 Bin…

valgrind跨平台调试及其问题分析

背景 同事在项目中遇到了内存泄漏问题&#xff0c;长时间没有解决&#xff0c;领导临时让我支援一下。心想&#xff0c;应该不难&#xff0c;毕竟我之间做过valgrind的使用总结。并输出内存泄漏问题分析思路&#xff08;案例篇&#xff09;和快速定位内存泄漏的套路两篇文章&a…

【C语言】Ubuntu 22上用GTK写GUI程序

一、GTK介绍 GTK (GIMP Toolkit) 是一个多平台的图形用户界面工具包。它最初是为图像处理程序 GIMP 开发的&#xff0c;后来演变成为许多操作系统上开发图形界面应用程序的通用库。GTK 是用C语言编写的&#xff0c;并且是自由和开源软件&#xff0c;遵循LGPL (GNU Lesser Gene…

三菱MR-JE伺服脉冲轴应用参数设置

三菱MR-JE伺服在脉冲轴控制上的应用&#xff0c;常用参数设置如下&#xff1a; 1、常用参数 未完...

Linux用shell脚本执行乘法口诀表的两种方式

#!/bin/bash # *********************************************************# # # # * Author : 藻头男 # # * QQ邮箱 : 2322944912qq.com # …

nginx 配置代理ip访问https的域名配置

前言 代理服务器是一种中间服务器&#xff0c;用于转发客户端请求到目标服务器。Nginx是一款高性能的Web服务器和反向代理服务器&#xff0c;可以用于配置代理IP访问HTTPS的域名。在本篇文章中&#xff0c;我们将介绍如何使用Nginx配置代理IP访问HTTPS域名&#xff0c;并提供相…

代码训练营Day.21 | 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先

530. 二叉搜索树的最小绝对差 1. LeetCode链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 2. 题目描述 3. 解法 中序遍历&#xff0c;记录前一个指针&#xff0c;并记录前一个指针和当前指针的绝对差值。递归。 class Solution { public:Tre…

Javascript 循环结构while do while for实例讲解

Javascript 循环结构while do while for实例讲解 目录 Javascript 循环结构while do while for实例讲解 一、while语句 二、do…while语句 三、for循环 疑难解答 我们从上一节课知道&#xff0c;JavaScript循环结构总有3种&#xff1a; &#xff08;1&#xff09;while语…

ARM CCA机密计算硬件架构之内存管理

实施了TrustZone安全扩展的Arm A-profile处理器呈现两个物理地址空间(PAS): 非安全物理地址空间安全物理地址空间Realm管理扩展增加了两个PAS: Realm物理地址空间Root物理地址空间下图显示了这些物理地址空间以及如何在工作系统中实施这些空间: 正如表格所示,根状态能够访…

openssl 命令详解

openssl genrsa 命令产生私钥 openssl genrsa 命令是会用来生成 RSA 私有秘钥&#xff0c;不会生成公钥&#xff0c;因为公钥提取自私钥。生成时是可以指定私钥长度和密码保护。 如果需要查看公钥或生成公钥&#xff0c;可以使用 openssl rsa 命令。 命令语法&#xff1a; ope…