死锁 手撕死锁检测工具

目录

引言

一.理论联立

1.死锁的概念和原因

2.死锁检测的基本思路

 

3.有向图在死锁检测中的应用

二.代码实现案例(我们会介绍部分重要接口解释)

1.我们定义一个线性表来存线程ID和锁ID

2.表中数据的查询接口

3.表中数据的删除接口

4.表中数据的添加接口

5.before_lock接口

6.afterlock接口

7.after_unlock接口

8.加锁和解锁的接口

9.检测死锁的接口

三.结果展示


 

引言

死锁是指在计算机系统中,多个进程(或线程)因竞争资源而造成的一种僵局,若无外力作用,这些进程(或线程)都将无法向前推进

我们将基于多个线程和多个互斥锁来介绍死锁的长生

 

一.理论联立

1.死锁的概念和原因

①.死锁是操作系统和学术概念,指线程占用资源导致互相等待对方释放资源的情况。

②.死锁常见于多线程环境中,导致CPU占用率100%,出现死循环。

图中有线程A,线程B,和线程C 三个线程 它们各自拥有自己各自的资源的情况下 其中

线程A想占用线程B的资源

线程B 想占用线程C的资源

线程C想占用线程A的资源 

最后形成了一个环 最后导致了死锁

2.死锁检测的基本思路

①.死锁检测依赖于资源占用情况的检测,通过判断是否构成环来实现。

②.环的构成表示线程间形成了死锁。

 

3.有向图在死锁检测中的应用

①.有向图是否成环的问题是死锁检测的底层算法。

②.通过有向图来判断是否构成环,从而检测死锁。

③.有向图的构建通过节点和边来表示线程和资源的关系。

④.环的检测通过深度优先搜索(DFS)来实现。

 

 

 

二.代码实现案例(我们会介绍部分重要接口解释)

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#define _GNU_SOURCE
#include <dlfcn.h>
#include<stdlib.h>#define MAX		100typedef unsigned long int uint64;struct rela_node{pthread_mutex_t *mtx;pthread_t thid;
};struct rela_node rela_table[MAX]={0};//search
pthread_t search_rela_table(pthread_mutex_t*mtx){int i = 0;for(i;i<MAX;i++){if(mtx==rela_table[i].mtx){return rela_table[i].thid;}}return 0;
}//dele
int dele_rela_table(pthread_t tid,pthread_mutex_t *mtx){int i =0;for(i;i<MAX;i++){if((rela_table[i].thid==tid)&&(rela_table[i].mtx==mtx)){rela_table[i].thid=0;rela_table[i].mtx=NULL;return 0;}}return -1;
}//add
int add_rela_table(pthread_t tid,pthread_mutex_t *mtx){int i =0;for(i;i<MAX;i++){if((rela_table[i].thid==0)&&(rela_table[i].mtx==NULL)){rela_table[i].thid=tid;rela_table[i].mtx=mtx;return 0;}}return -1;
}#if 1  
//有向图
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 task_graph *tg = NULL;
int path[MAX+1];
int visited[MAX];
int k = 0;
int deadlock = 0;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;}}}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;}}int init_graph(void){tg=(struct task_graph*)malloc(sizeof(struct task_graph));tg->num=0;
}#endifvoid before_lock(pthread_t tid,pthread_mutex_t*mtx){pthread_t otherid=search_rela_table(mtx);if(otherid!=0){//mtx有线程在占用struct source_type from;from.id=tid;from.type=PROCESS;struct source_type to;to.id=otherid;to.type=PROCESS;add_edge(from,to);}}//如果走到after_lock 则表明mtx没有被线程占用 把之前的边删除 然后我们占用该mtx
void after_lock(pthread_t tid,pthread_mutex_t*mtx){pthread_t otherid=search_rela_table(mtx);if(otherid!=0){//删除旧边struct source_type from;from.id=tid;from.type=PROCESS;struct source_type to;to.id=otherid;to.type=PROCESS;if(verify_edge(from,to)){remove_edge(from,to);}}//mtx无线程在占用 则占我们占用add_rela_table(tid,mtx);
}
void after_unlock(pthread_t tid,pthread_mutex_t*mtx){dele_rela_table(tid,mtx);//有小问题 这个死锁工具可能只能检测一次 如果存在解锁情况 可能会导致after_lock mtx找不到旧线程id
}//检测死锁
void check_dead_lock(void){int i =0;for(i;i<tg->num;i++){search_for_cycle(i);}
}static void *thread_routine(void*arg){while(1){sleep(5);check_dead_lock();}
}
void start_check(void) {pthread_t tid;pthread_create(&tid, NULL, thread_routine, NULL);}#if 1  //hook
typedef int (*pthread_mutex_lock_t)(pthread_mutex_t*mtx);
pthread_mutex_lock_t pthread_mutex_lock_f=NULL;typedef int (*pthread_mutex_unlock_t)(pthread_mutex_t*mtx);
pthread_mutex_unlock_t pthread_mutex_unlock_f=NULL;typedef int (*pthread_create_t)(pthread_t *restrict thread, const pthread_attr_t *restrict attr,void *(*start_routine)(void *), void *restrict arg);
pthread_create_t pthread_create_f = NULL;int pthread_mutex_lock(pthread_mutex_t*mtx){// printf("before pthread_mutex_lock%ld,%p \n",pthread_self(),mtx);pthread_t selfid = pthread_self();before_lock(selfid, mtx);pthread_mutex_lock_f(mtx);// printf("after pthread_mutex_lock\n");after_lock(selfid,mtx);
}int pthread_mutex_unlock(pthread_mutex_t*mtx){pthread_t selfid = pthread_self();pthread_mutex_unlock_f(mtx);after_unlock(selfid,mtx);// printf("after pthread_mutex_unlock%ld,%p \n",pthread_self(),mtx);}int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr,void *(*start_routine)(void *), void *restrict arg) {pthread_create_f(thread,attr,start_routine,arg);struct source_type v1;v1.id=*thread;v1.type=PROCESS;add_vertex(v1);
}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");}if (!pthread_create_f) {pthread_create_f = dlsym(RTLD_NEXT, "pthread_create");}}
#endif#if 1//debug
pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx3 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx4 = PTHREAD_MUTEX_INITIALIZER;void * t1_cb(void*arg){printf("pid1=%ld\n",pthread_self());pthread_mutex_lock(&mtx1);sleep(1);pthread_mutex_lock(&mtx2);pthread_mutex_unlock(&mtx2);pthread_mutex_unlock(&mtx1);}
void * t2_cb(void*arg){printf("pid2=%ld\n",pthread_self());pthread_mutex_lock(&mtx2);sleep(1);pthread_mutex_lock(&mtx3);pthread_mutex_unlock(&mtx3);pthread_mutex_unlock(&mtx2);}void * t3_cb(void*arg){printf("pid3=%ld\n",pthread_self());pthread_mutex_lock(&mtx3);sleep(1);pthread_mutex_lock(&mtx4);pthread_mutex_unlock(&mtx4);pthread_mutex_unlock(&mtx3);
}void * t4_cb(void*arg){printf("pid4=%ld\n",pthread_self());pthread_mutex_lock(&mtx4);sleep(1);pthread_mutex_lock(&mtx1);pthread_mutex_unlock(&mtx1);pthread_mutex_unlock(&mtx4);
}
int main(){init_graph();//有向图的初始化init_hook();//hook函数的初始化pthread_t t1,t2,t3,t4;start_check();//开始检测死锁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_join(t1,NULL);pthread_join(t2,NULL);pthread_join(t3,NULL);pthread_join(t4,NULL);printf("complete\n");
}
#endif

具体代码接口的实现

1.我们定义一个线性表来存线程ID和锁ID

typedef unsigned long int uint64;struct rela_node{pthread_mutex_t *mtx;pthread_t thid;
};struct rela_node rela_table[MAX]={0};

2.表中数据的查询接口

//search
pthread_t search_rela_table(pthread_mutex_t*mtx){int i = 0;for(i;i<MAX;i++){if(mtx==rela_table[i].mtx){return rela_table[i].thid;}}return 0;
}

 

3.表中数据的删除接口

//dele
int dele_rela_table(pthread_t tid,pthread_mutex_t *mtx){int i =0;for(i;i<MAX;i++){if((rela_table[i].thid==tid)&&(rela_table[i].mtx==mtx)){rela_table[i].thid=0;rela_table[i].mtx=NULL;return 0;}}return -1;
}

4.表中数据的添加接口

//add
int add_rela_table(pthread_t tid,pthread_mutex_t *mtx){int i =0;for(i;i<MAX;i++){if((rela_table[i].thid==0)&&(rela_table[i].mtx==NULL)){rela_table[i].thid=tid;rela_table[i].mtx=mtx;return 0;}}return -1;
}

 

5.before_lock接口

void before_lock(pthread_t tid,pthread_mutex_t*mtx){pthread_t otherid=search_rela_table(mtx);if(otherid!=0){//mtx有线程在占用struct source_type from;from.id=tid;from.type=PROCESS;struct source_type to;to.id=otherid;to.type=PROCESS;add_edge(from,to);}}

我们传入当前线程id和锁

我们先对锁进行判断是否有线程在占用 如果有线程在占用我们则和该线程建立一条边

6.afterlock接口

//如果走到after_lock 则表明mtx没有被线程占用 把之前的边删除 然后我们占用该mtx
void after_lock(pthread_t tid,pthread_mutex_t*mtx){pthread_t otherid=search_rela_table(mtx);if(otherid!=0){//删除旧边struct source_type from;from.id=tid;from.type=PROCESS;struct source_type to;to.id=otherid;to.type=PROCESS;if(verify_edge(from,to)){remove_edge(from,to);}}//mtx无线程在占用 则占我们占用add_rela_table(tid,mtx);
}

我们要先判断该锁之前是否和其他线程建立边 如果有我们就删除旧边 然后占用该把锁

7.after_unlock接口

void after_unlock(pthread_t tid,pthread_mutex_t*mtx){dele_rela_table(tid,mtx);//有小问题 这个死锁工具可能只能检测一次 如果存在解锁情况 可能会导致after_lock mtx找不到旧线程id
}

8.加锁和解锁的接口

int pthread_mutex_lock(pthread_mutex_t*mtx){// printf("before pthread_mutex_lock%ld,%p \n",pthread_self(),mtx);pthread_t selfid = pthread_self();before_lock(selfid, mtx);pthread_mutex_lock_f(mtx);// printf("after pthread_mutex_lock\n");after_lock(selfid,mtx);
}int pthread_mutex_unlock(pthread_mutex_t*mtx){pthread_t selfid = pthread_self();pthread_mutex_unlock_f(mtx);after_unlock(selfid,mtx);// printf("after pthread_mutex_unlock%ld,%p \n",pthread_self(),mtx);}

9.检测死锁的接口

//检测死锁
void check_dead_lock(void){int i =0;for(i;i<tg->num;i++){search_for_cycle(i);}
}static void *thread_routine(void*arg){while(1){sleep(5);check_dead_lock();}
}
void start_check(void) {pthread_t tid;pthread_create(&tid, NULL, thread_routine, NULL);}

 

三.结果展示

 

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

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

相关文章

Java 中 SQL 注入问题剖析​

一、引言​ 在当今数字化时代&#xff0c;数据是企业和组织的核心资产之一。许多应用程序都依赖于数据库来存储和管理数据&#xff0c;而 Java 作为一种广泛使用的编程语言&#xff0c;常被用于开发与数据库交互的应用程序。然而&#xff0c;SQL 注入这一安全漏洞却如同隐藏在…

安全理念和安全产品发展史

从安全理念的发展历史来看,技术与产品的演进始终围绕 “威胁对抗” 与 “业务适配” 两大核心展开。以下从七个关键阶段解析安全技术与产品的发展脉络,并结合最新实践与未来趋势提供深度洞察: 一、密码学奠基阶段(1970s 前) 安全理念:以 “信息保密” 为核心,防御手段…

【Ansible自动化运维】二、Playbook 深入探究:构建复杂自动化流程

​ 在 Ansible 自动化运维体系中&#xff0c;Playbook 是极为关键的部分。它允许我们以一种结构化、可重复的方式定义和执行一系列复杂的任务&#xff0c;从而构建高效的自动化流程。本篇文章将深入探究 Ansible Playbook 的各个方面&#xff0c;助您掌握构建复杂自动化…

springboot项目中常用的工具类和api

在Spring Boot项目中&#xff0c;开发者通常会依赖一些工具类和API来简化开发、提高效率。以下是一些常用的工具类及其典型应用场景&#xff0c;涵盖 Spring 原生工具、第三方库&#xff08;如Hutool、Guava&#xff09; 和 Java 自带工具。 1. Spring Framework 自带工具类 (…

23种设计模式-行为型模式-模板方法

文章目录 简介场景解决代码关键优化点 总结 简介 模板方法是一种行为设计模式&#xff0c;它在超类中定义了一个算法的框架&#xff0c;允许子类在不修改结构的情况下重写算法的特定步骤。 场景 假如你正在开发一款分析文档的数据挖掘程序。用户需要向程序输入各种格式&…

解决Long类型前端精度丢失和正常传回后端问题

在 Java 后端开发中&#xff0c;可能会遇到前后端交互过程中 Long 类型精度丢失的问题。尤其是在 JavaScript 中&#xff0c;由于其 Number 类型是双精度浮点数&#xff0c;超过 16 位的 Long 类型值就会发生精度丢失。 问题背景 假设有如下实体类&#xff1a; public class…

PowerPhotos:拯救你的Mac照片库,告别苹果原生应用的局限

如果你用Mac管理照片&#xff0c;大概率被苹果原生「照片」应用折磨过——无法真正并行操作多个图库。每次切换图库都要关闭重启&#xff0c;想合并照片得手动导出导入&#xff0c;重复文件更是无处可逃…… 直到我发现了 PowerPhotos&#xff0c;这款专为Mac设计的照片库管理…

android 14.0 工厂模式 测试音频的一些问题(高通)

1之前用tinycap&#xff0c;现在得用agmcap 执行----agmcap /data/test.wav -D 100 -d 101 -i CODEC_DMA-LPAIF_RXTX-TX-3 -T 3 报错1 agmcap data/test.wav -D 100 -d 101 -i CODEC_DMA-LPAIF_RXTX-TX-3 -T 3 Failed to open xml file name /vendor/etc/backend_co…

以库存系统为核心的ERP底层架构设计

在企业资源计划&#xff08;ERP&#xff09;系统中&#xff0c;库存系统常被视为基础模块。但在现代企业的数字化进程中&#xff0c;库存系统不仅仅是一个模块&#xff0c;它已经逐步演化为驱动整个ERP生态的核心引擎。本文从架构设计的角度&#xff0c;探讨为何库存系统应被置…

辛格迪客户案例 | 北京舒曼德医药实施电子合约系统(eSign)

01 北京舒曼德医药科技开发有限公司&#xff1a;医药科技的数字化先锋 北京舒曼德医药科技开发有限公司&#xff08;以下简称“舒曼德医药”&#xff09;作为国内医药科技领域的领军企业&#xff0c;致力于创新药物的研发、临床试验和市场推广。公司以“科技兴药、质量为先、服…

【UE5】RTS游戏的框选功能+行军线效果实现

目录 效果 步骤 一、项目准备 二、框选NPC并移动到指定地点 三、框选效果 效果 步骤 一、项目准备 1. 新建一个俯视角游戏工程 2. 新建一个pawn、玩家控制器和游戏模式,这里分别命名为“MyPawn”、“MyController”和“MyGameMode” 3. 打开“MyGameMode”,设置玩家…

vim定位有问题的脚本/插件的一般方法

在使用vim的过程中可能会遇到一些报错或其他不符合预期的情况&#xff0c;本文介绍一些我自己常用的定位有问题脚本/插件的方法&#xff08;以下方法同样适用于neovim&#xff09; 执行了某些命令的情况 这种情况最简单&#xff0c;使用:h 命令&#xff0c;如果插件有文档的话…

智能驱动教育变革:人工智能在高中教育中的实践路径与创新策略

一、引言 随着信息技术的飞速发展&#xff0c;人工智能&#xff08;Artificial Intelligence, AI&#xff09;已成为推动社会进步的重要力量。在教育领域&#xff0c;人工智能的应用正逐渐改变着传统的教学模式和方法&#xff0c;为教育现代化注入了新的活力。高中教育作为教育…

VLAN(虚拟局域网)

一、vlan概述 VLAN(virtual local area network)是一种通过逻辑方式划分网络的技术&#xff0c;允许将一个物理网络划分为多个独立的虚拟网络。每一个vlan是一个广播域&#xff0c;不同vlan之间的通信需要通过路由器或三层交换机 [!注意] vlan是交换机独有的技术&#xff0c;P…

spring-cloud-starter-alibaba-seata使用说明

Spring Cloud Alibaba Seata 使用说明 spring-cloud-starter-alibaba-seata 是 Spring Cloud Alibaba 生态中用于集成分布式事务框架 Seata 的核心组件&#xff0c;支持 AT&#xff08;自动补偿&#xff09;、TCC&#xff08;手动补偿&#xff09; 等模式。 一、依赖配置 添加…

每日一题(小白)暴力娱乐篇23

由题意得知给我们一串数字&#xff0c;我们每次交换两位&#xff0c;最少交换多少次成功得到有顺序的数组。我们以平常的思维去思考&#xff0c;加入给你一串数字获得最少的交换次数&#xff0c;意味着你的交换后续基本不会变&#xff0c;比如说2 1 3 5 4 中1与2交换后不变&…

Python基础——Pandas库

对象的创建 导入 Pandas 时&#xff0c;通常给其一个别名“pd”&#xff0c;即 import pandas as pd。作为标签库&#xff0c;Pandas 对象在 NumPy 数组基础上给予其行列标签。可以说&#xff0c;列表之于字典&#xff0c;就如 NumPy 之于 Pandas。Pandas 中&#xff0c;所有数…

Spring入门概念 以及入门案例

Spring入门案例 Springspring是什么spring的狭义与广义spring的两个核心模块IoCAOP Spring framework特点spring入门案例不用new方法&#xff0c;如何使用返回创建的对象 容器&#xff1a;IoC控制反转依赖注入 Spring spring是什么 spring是一款主流的Java EE轻量级开源框架 …

The packaging for this project did not assign a file to the build artifact

问题&#xff1a; maven install报错&#xff1a;The packaging for this project did not assign a file to the build artifact 解决方案&#xff1a; 方案1&#xff1a; 使用mvn clean install 就可以解决问题&#xff0c; 方案2&#xff1a; 找到lifecycle点clean再点…

C++入门一:C++ 编程概述

一、C 语言与 C 的关系&#xff1a;从 “带类的 C” 到独立王国 1.1 血缘关系&#xff1a;C 是 C 的 “超级进化版” 起源&#xff1a;C 由 Bjarne Stroustrup 在 1980 年代开发&#xff0c;最初名为 “C with Classes”&#xff08;带类的 C&#xff09;&#xff0c;旨在为 …