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

news2024/11/17 19:57:50

死锁介绍

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

死锁案例

线程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 1


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 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 1

int 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 //sample

pthread_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.coloradmin.cn/o/1352182.html

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

相关文章

winform简易用户权限管理系统

一、功能简介 1. 数据库可使用sqlite、mssql、mysql等 2. 管理员账号不管控&#xff0c;其余账号进行权限管控&#xff08;左侧菜单&#xff0c;表单按钮&#xff09;&#xff0c;且只能看到自己创建的角色、用户 二、操作界面 1. 管理员账号登陆后&#xff0c;左侧菜单栏自动…

【Spark精讲】SparkSQL的RBO与CBO

Spark SQL核心:Catalyst Spark SQL的核心是Catalyst查询编译器&#xff0c;它将用户程序中的SQL/Dataset/DataFrame经过一系列操作&#xff0c;最终转化为Spark系统中执行的RDD。 Catalyst组成部分 Parser &#xff1a;用Antlr将SQL/Dataset/DataFrame转化成一棵未经解析的树…

怎么快速修复mfc140.dll文件?解决mfc140.dll缺失的方法

面对计算机报告的 ​mfc140.dll​ 文件遗失错误&#xff0c;这通常表明系统中缺少一个关键的动态链接库文件&#xff0c;该文件对于运行以 Microsoft Foundation Class (MFC) 库编写的程序十分重要&#xff0c;尤其是那些需要图形界面的应用程序和一些游戏。若没有这个文件&…

清风数学建模-数学规划模型

内容&#xff1a;数学规划模型&#xff08;cab aeqbeq lbub&#xff09; 一.题型类型 1.线性规划linprog 2.非线性规划 fmincon 3.整数规划 intlinprog 4.&#xff08;0-1规划&#xff09;&#xff08;特殊的线性整数规划&#xff09;intlinprog 5.多目标规划 linprog 标…

JumpServer3.0版本(用户管理、邮件、MFA认证配置)

创建用户组 控制台页面可以看见左侧的用户管理下,有用户列表和用户组 点击用户组、点击创建按钮、设置名称,用户不用选择还没建用户,提交即可 创建用户 点击用户列表创建按钮,设置名称、用户名、邮箱等必填项 这个时候用户组选项,可以选好我们创建的用户组了,先创用…

Spring高手之路-Spring Bean、Java Bean和对象的区别与联系

目录 什么是Spring Bean 什么是Java Bean 什么是对象 Spring Bean与Java Bean与对象的联系与区别 联系 区别 什么是Spring Bean 在Spring官方文档中对Bean的解释如下&#xff1a; In Spring, the objects that form the backbone of your application and that are manage…

MySQL数据库高级SQL语句及存储过程

目录 一、高级SQL语句 &#xff08;一&#xff09;case语句 1.语法定义 2.示例 &#xff08;二&#xff09;空值(NULL) 和 无值( ) 1.区别 2.示例 &#xff08;1&#xff09;字符长度 &#xff08;2&#xff09;判断方法 ① 空值(NULL) ② 无值( ) &#xff08;3…

代码随想录算法训练DAY18|二叉树5

算法训练DAY18|二叉树5 513.找树左下角的值 力扣题目链接 给定一个二叉树&#xff0c;在树的最后一行找到最左边的值。 示例 1: 示例 2: 思路 本题要找出树的最后一行的最左边的值。此时大家应该想起用层序遍历是非常简单的了&#xff0c;反而用递归的话会比较难一点。 我…

【通关喜报】腾讯云TDSQL TCP/TCE、云运维tcp 12月认证考试,全员过关,年终冲刺!

2023年12月23日云贝教育有6位学员参加了腾讯云TDSQL-TCP以及TCE认证考试。都取得非常好的成绩~下面我们来看一下各位同学的理论考试和上机考试成绩吧~

BWP频域位置的确定

这里根据协议整理下BWP频域相关参数以及如何确定BWP的频域位置。 BWP的配置包含几个参数 &#xff1a; 1 SCS, CyclePrefix 和locationAndBandwidth。 BWP频域起始位置N_start_BWPOcarrierRBstart&#xff0c;其中Ocarrier 由RRC层参数offsetToCarrier决定。 locationAndB…

Java学习——设计模式——结构型模式2

结构型模式 结构型模式主要涉及如何组合各种对象以便获得更好、更灵活的结构。虽然面向对象的继承机制提供了最基本的子类扩展父类的功能&#xff0c;但结构型模式不仅仅简单地使用继承&#xff0c;而更多地通过组合与运行期的动态组合来实现更灵活的功能。 包括&#xff1a; 1…

Redis命令---String篇 (超全)

目录 1.Redis Setnx 命令 - 只有在 key 不存在时设置 key 的值。简介语法可用版本: > 1.0.0返回值: 设置成功&#xff0c;返回 1 。 设置失败&#xff0c;返回 0 。 示例 2.Redis Getrange 命令 - 返回 key 中字符串值的子字符简介语法可用版本: > 2.4.0返回值: 截取得到…

vue3 接入 Element Plus

vue3 接入 Element Plus vue3 发布已经很久了&#xff0c;官方也已经发布公告&#xff0c;自2023年12月31日起停止对 vue2 版本的维护更新&#xff0c;因此&#xff0c;vue3 正式登上了历史的舞台。组件库一直是前端开发的利器&#xff0c;减少了开发者开发复杂度&#xff0c;提…

计算机毕业设计------基于SpringCloud的实验室管理系统

项目介绍 实验室管理系统的用户可以分为两种&#xff1a;系统管理员和普通用户。系统管理员主要功能&#xff1a; 登录登出、分析数据、管理用户、管理日志、管理实验室、管理预约、维护个人资料、实验室保修管理 用户主要功能&#xff1a; 注册登录、查询实验室、实验室预约…

Edge浏览器的卸载(一分钟版)

一分钟看完不耽误 开整工具下载后 结尾 开整 工具 Remove-MS-Edge 看名字&#xff0c;简单直接 CSDN下载 资源设置是免费的&#xff0c;大家尽管下载 不放心软件安全的话&#xff0c;自己上github地址下载也行 下载后 解压之后 我们打开有gui的&#xff0c;也就是有界面的&…

胡润研究院发布《2023胡润中国最具历史文化底蕴品牌榜》

胡润研究院发布《2023胡润中国最具历史文化底蕴品牌榜》&#xff0c;前十名分别是片仔癀、同仁堂、贵州茅台、五粮液、中国银行、中华、黄山、农业银行、建设银行、汾酒。 榜单调研范围涵盖中国内地具有60年以上历史的为消费者提供产品或服务的品牌&#xff0c;综合考察品牌历史…

polar CTF web upload tutu

一、题目 二、解题 1、上传两个一样的木马提示不是 setu&#xff08;色图&#xff09; 2、上传两个图&#xff0c;提示md5值不一样 综上他需要两张md5值相同的图 找工具 fastcoll 可生成两个md5值相同的文件 http://www.win.tue.nl/hashclash/fastcoll_v1.0.0.5.exe.zip 照…

编织Spring魔法:解读核心容器中的Beans机制【beans 一】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 编织Spring魔法&#xff1a;解读核心容器中的Beans机制【beans 一】 前言什么是Spring核心容器Beans的生命周期管理&#xff1a;初始化和销毁方法&#xff1a;各种作用域&#xff1a; beans的配置方式…

【Bidomain建模范式:Pansharpening】

Bidomain Modeling Paradigm for Pansharpening &#xff08;泛锐化的Bidomain建模范式&#xff09; 泛锐化是一个具有挑战性的低层次视觉任务&#xff0c;其目的是学习光谱信息和空间细节之间的互补表示。尽管取得了显着的进步&#xff0c;现有的基于深度神经网络&#xff0…

JVM内存模型理解

1、首先理解下什么是 jvm 内存模型&#xff1f; jvm内存模型定义了Java虚拟机运行时如何组织和管理内存&#xff0c;规定了各个内存区域的作用、结构和交互方式&#xff0c;以及线程间的内存可见性、内存操作的原子性等行为&#xff0c;以支持Java程序的执行&#xff0c;即一种…