实现死锁检测组件

news2024/10/6 19:19:23

文章目录

  • 一、死锁
    • 1.1 死锁的定义
    • 1.2 死锁产生的条件
  • 二、死锁检测
    • 2.1 资源分配图
    • 2.2 有向图
    • 2.3 死锁检测组件 -- hook
  • 三、死锁设计
    • 3.1 有向图
      • 3.1.1 数据结构
      • 3.1.2 图的基本操作接口
      • 3.1.3 判断有向图是否成环
    • 3.2 hook
      • 3.2.1 重载系统的锁接口
      • 3.2.2 加锁前——lock_before
      • 3.2.3 加锁前——lock_before
      • 3.2.4 加锁后——unlock_after
    • 3.3 死锁检测线程
    • 3.4 画图解释
  • 四、代码实现及测试

一、死锁

1.1 死锁的定义

死锁,是指多个线程或者进程在运行过程中因争夺资源而造成的一种僵局,当进程或者线程处于这种僵持状态,若无外力作用,它们将无法再向前推进。

如下图所示,以申请锁资源为例。线程 A 想申请线程 B 的锁,线程 B 想申请线程 C 的锁,线程 C 想申请线程 D 的锁,线程 D 想申请线程 A 的锁,从而构建了一个资源申请环。
在这里插入图片描述
多个线程之间依次想要访问其他线程的资源,这样相互僵持形成的一个资源申请闭环。
死锁的存在是因为有资源申请环的存在,所以只要能检测出资源申请环,就等同于检测出
死锁的存在。

1.2 死锁产生的条件

1.条件互斥:进程/线程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程/线程所占用。
2.请求与保持:当进程/线程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺:进程/线程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
4.环路等待:在发生死锁时,必然存在一个进程/线程——资源的环形链。

二、死锁检测

为了能检测系统是否发生死锁,需要有两点

  1. 用某种数据结构来保存资源的请求和分配信息;
  2. 提供一种算法,利用上述信息检测系统是否进入死锁状态。

2.1 资源分配图

在资源分配图中,有两种结点,两种边
1)两种结点

  • 进程结点:对应一个进程
  • 资源结点:对应一类资源,资源可能有多个

2)两种边

  • 进程结点 ——> 资源结点:表示进程想申请几个资源(每条边代表一个)
  • 资源结点 ——> 进程结点:表示已经为进程分配了几个资源(每条边代表一个)

在这里插入图片描述
如图,顶点表示进程;黄色边代表资源申请边;红色边代表资源获取边。
每个进程获取了自己的资源(锁),同时又申请了其他线程的资源(锁)。

上图展示了一个死锁,检测死锁的关键是检测资源申请边有没有构成一回路。

我们可以采用有向图来存储资源申请环,则问题转化为检测有向图是否成环。

2.2 有向图

有向图的构建有矩阵、邻接表两种方式,本文选择邻接表。
如,有向图
在这里插入图片描述
其邻接表
在这里插入图片描述

2.3 死锁检测组件 – hook

死锁检测的流程可大致归纳为:
1)hook拦截系统的锁接口(pthread_mutex_lockpthread_mutex_unlock);
2)在hook后的锁接口中,利用有向图存储资源分配图的信息
线程A申请线程B的资源锁:若锁未被占有,则占有这个锁;若锁被占有,构建线程A指向线程B的申请边。
3)创建一个线程,定时检测图进程是否有环的存在。

hook类似于C++中的重载。通过hook,系统的pthread_mutex_lockpthread_mutex_unlock,将指向我们自己定义的函数。

hook重载的函数中,把线程与锁的申请关系构建成有向图。通过线程加锁前、加锁后、释放锁,这3个阶段维护有向图
1)加锁前:检测当前申请的锁是否被占用

  • 若被占用:构建当前线程指向占有锁的线程之间的申请边
  • 若未被占用:继续执行后续(占有这个锁)

2)加锁后:占有这个锁之后,检测

  • 该锁在之前没有被占有过:建立线程id与锁id的对应关系
  • 该锁在之前被占有过:释放之前创建的申请边,建立线程id与锁id的对应关系
  • 该锁在之前没被占有过:建立线程id与锁id的对应关系

3)释放锁:清除锁与线程的对应关系

三、死锁设计

3.1 有向图

3.1.1 数据结构

线程既可以拥有资源,也可以申请资源,资源绑定线程。为了实现复用,增加了 type 字段,当 type = RESOURCE,该结构体作为资源使用,放入资源链表; type = PROCESS,该结构体作为线程使用。

1)顶点资源信息,即线程所拥有的资源信息

// 顶点资源信息
struct source_type {

	uint64 id;			// 拥有该资源的线程 id
	enum Type type;		// 顶点类型:线程 or 资源
	uint64 lock_id;		// 资源(锁) id
	int degress;	    // 资源的出度,该资源被多少顶点(线程)申请
};

2)顶点,表示线程;图的资源申请边,表示线程间的资源申请关系。

// 顶点信息
struct vertex {

	struct source_type s;	// 顶点资源信息
	struct vertex *next;	// 指向下一个顶点(邻接表)

};

3)图的结构,资源 (type = RESOURCE) 存储在资源链表,线程(type = PROCESS) 作为图上的顶点。

// 图结构
struct task_graph {

	struct vertex list[MAX];			// 存储顶点于数组中
	int num;							// 顶点的数量

	struct source_type locklist[MAX];	// 存储所有顶点资源(锁)
	int lockidx; 						// 资源(锁)的数量

	pthread_mutex_t mutex;
};

3.1.2 图的基本操作接口

1)创建顶点:create_vertex(struct source_type type)
2)增加顶点:add_vertex(struct source_type type)
3)寻找与资源对应的顶点所在的下标:search_vertex(struct source_type type)
4)增加一条资源申请边:add_edge(struct source_type from, struct source_type to)
5)检查资源i和j之间是否存在边:verify_edge(struct source_type i, struct source_type j)
6)删除资源from到to之间的边:remove_edge(struct source_type from, struct source_type to)


// 创建结点
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);

}

// 检查资源i和j之间是否存在边
int verify_edge(struct source_type i, struct source_type j) {

	if (tg->num == 0) return 0;

	// 寻找资源i对应的结点下标
	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;

}

// 删除资源from到to之间的边
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;
		}

	}

}

3.1.3 判断有向图是否成环

// 打印环  cycle : 1 --> 2 --> 3 --> 4 --> 2
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;
	}

	// 否则将标记置为1,代表访问了,继续DFS
	visited[idx] = 1;
	path[k++] = idx;

	while (ver->next != NULL) {

		DFS(search_vertex(ver->next->s));
		k --;
		
		ver = ver->next;

	}

	return 1;

}

//从list[idx]的顶点开始,检测是否存在环
int search_for_cycle(int idx) {

	struct vertex *ver = &tg->list[idx];
	visited[idx] = 1;	// 标记idx处顶点被访问过
	k = 0;		
	path[k++] = idx;	// 记录起点

	while (ver->next != NULL) {

		// 把visited数组的开始到idx-1处的值都置为0
		int i = 0;
		for (i = 0;i < tg->num;i ++) {
			if (i == idx) continue;
			
			visited[i] = 0;
		}

		// 把path数组的第i到MAX处的值都置为-1
		for (i = 1;i <= MAX;i ++) {
			path[i] = -1;
		}
		k = 1;

		DFS(search_vertex(ver->next->s));
		ver = ver->next;
	}

}

3.2 hook

3.2.1 重载系统的锁接口

// ·······································hook··············································
// 1.定义原类型
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;

// 2.实现
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);
	
}

// 3. 初始化
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");
	
}

3.2.2 加锁前——lock_before

获取资源(锁)前,检测该资源是否被其他线程占用。

  1. 如果被占用,则创建一条资源申请边,表示当前进程正在向拥有资源的线程申请该资源。
  2. 如果没有被占用,则跳过。
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 ++) {

		// 1. 如果被占用
		if (tg->locklist[idx].lock_id == lockaddr) { 
			
			// 创建申请该资源的顶点(PROCESS类型)
			struct source_type from;
			from.id = tid;
			from.type = PROCESS;
			add_vertex(from);

			// 创建拥有该资源的顶点(PROCESS类型)
			struct source_type to;
			to.id = tg->locklist[idx].id;
			to.type = PROCESS;
			add_vertex(to);

			// 申请该资源的结点数量+1
			tg->locklist[idx].degress ++;

			// 如果两个顶点间不存在资源申请边,增加一条边
			if (!verify_edge(from, to))
				add_edge(from, to);

		}

	}
	
	
}

3.2.3 加锁前——lock_before

线程获取资源(锁)后,检查该资源是否存在(资源链表中是否存在)

  1. 若该资源之前不存在,添加资源到资源链表中
  2. 若该资源已经存在,则移除之前创建的自己对该资源的申请边,表示请求已经得到满足
void lock_after(uint64_t tid, uint64_t lockaddr) {

	/*
		if (!lockaddr) {

			tid --> lockaddr;

		} else {

			lockaddr.tid = tid;
			tid -> lockaddr;

		}
		
	 */
	int idx = 0;
	// 1. 若该资源(锁)不存在,添加资源到资源链表中
	if (-1 == (idx = search_lock(lockaddr))) {// 

		// 寻找资源链表中空闲的位置并添加该资源
		int eidx = search_empty_lock(lockaddr);

		tg->locklist[eidx].id = tid;
		tg->locklist[eidx].lock_id = lockaddr;

		// 资源数量+1
		tg->lockidx ++;
		
	} 
	else {	// 2. 该资源(锁)存在,需要移除自己的请求边

		// 申请该资源的顶点(PROCESS类型)
		struct source_type from;
		from.id = tid;
		from.type = PROCESS;
		add_vertex(from);

		// 拥有该资源的顶点(PROCESS类型)
		struct source_type to;
		to.id = tg->locklist[idx].id;
		to.type = PROCESS;
		add_vertex(to);

		// 申请该资源的顶点数-1
		tg->locklist[idx].degress --;

		// 如果存在该资源申请边,则删除
		if (verify_edge(from, to))
			remove_edge(from, to);

		// 线程占用该资源(锁)
		tg->locklist[idx].id = tid;
		
	}
	 
	
}

3.2.4 加锁后——unlock_after

线程释放该资源后,检查该资源是否还被其他线程申请,没有则将其从资源链表中移除。

void unlock_after(uint64_t tid, uint64_t lockaddr) {

	// lockaddr.tid = 0;
	// 此锁已被unlock
	int idx = search_lock(lockaddr);

	// 若该资源没有线程申请,则将其从资源链表中移除
	if (tg->locklist[idx].degress == 0) {
		tg->locklist[idx].id = 0;
		tg->locklist[idx].lock_id = 0;
	}
	
}

3.3 死锁检测线程

创建死锁检测线程,在程序的运行期间时刻监控线程与锁之间的关系。

// 检测死锁
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);	//每隔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);

}

3.4 画图解释

1、
在这里插入图片描述
2、
在这里插入图片描述
3、
在这里插入图片描述

四、代码实现及测试

创建5个线程,1申请2、2申请3、3申请4、4申请5、5申请1
出现死锁
在这里插入图片描述

修改1申请2、2申请3、3申请4、4申请5
检测完成,未出现死锁
在这里插入图片描述


// build: gcc -o deadlock deadlock.c -lpthread -ldl


#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;			// 拥有该资源的线程 id
	enum Type type;		// 顶点类型:线程 or 资源
	uint64 lock_id;		// 资源(锁) 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);

}

// 检查资源i和j之间是否存在边
int verify_edge(struct source_type i, struct source_type j) {

	if (tg->num == 0) return 0;

	// 寻找资源i对应的结点下标
	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;

}

// 删除资源from到to之间的边
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;
		}

	}

}

// 打印环  cycle : 1 --> 2 --> 3 --> 4 --> 2
void print_deadlock(void) {

	int i = 0;

	printf("deadlock : ");
	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;
	}

	// 否则将标记置为1,代表访问了,继续DFS
	visited[idx] = 1;
	path[k++] = idx;

	while (ver->next != NULL) {

		DFS(search_vertex(ver->next->s));
		k --;
		
		ver = ver->next;

	}

	return 1;

}

//从list[idx]的顶点开始,检测是否存在环
int search_for_cycle(int idx) {

	struct vertex *ver = &tg->list[idx];
	visited[idx] = 1;	// 标记idx处顶点被访问过
	k = 0;		
	path[k++] = idx;	// 记录起点

	while (ver->next != NULL) {

		// 把visited数组的开始到idx-1处的值都置为0
		int i = 0;
		for (i = 0;i < tg->num;i ++) {
			if (i == idx) continue;
			
			visited[i] = 0;
		}

		// 把path数组的第i到MAX处的值都置为-1
		for (i = 1;i <= MAX;i ++) {
			path[i] = -1;
		}
		k = 1;

		DFS(search_vertex(ver->next->s));
		ver = ver->next;
	}

}


#endif

// 



#if 1

// 搜索锁lock是否在资源列表中,若有则返回下标位置
int search_lock(uint64 lock) {

	int i = 0;
	
	for (i = 0;i < tg->lockidx;i ++) {
		//如果lock存在就返回它在锁列表中的下标位置
		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;

}


/*
获取资源(锁)前,检测该资源是否被其他线程占用。
1. 如果被占用,则创建一条资源申请边,表示当前进程正在向拥有资源的线程申请该资源。
2. 如果没有被占用,则跳过。
*/
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 ++) {

		// 1. 如果被占用
		if (tg->locklist[idx].lock_id == lockaddr) { 
			
			// 创建申请该资源的顶点(PROCESS类型)
			struct source_type from;
			from.id = tid;
			from.type = PROCESS;
			add_vertex(from);

			// 创建拥有该资源的顶点(PROCESS类型)
			struct source_type to;
			to.id = tg->locklist[idx].id;
			to.type = PROCESS;
			add_vertex(to);

			// 申请该资源的结点数量+1
			tg->locklist[idx].degress ++;

			// 如果两个顶点间不存在资源申请边,增加一条边
			if (!verify_edge(from, to))
				add_edge(from, to);

		}

	}
	
	
}


/*
线程获取资源(锁)后,检查该资源是否存在(资源链表中是否存在)
1. 若该资源之前不存在,添加资源到资源链表中
2. 若该资源已经存在,则移除自己对该资源的申请边,表示请求已经得到满足
*/
void lock_after(uint64_t tid, uint64_t lockaddr) {

	/*
		if (!lockaddr) {

			tid --> lockaddr;

		} else {

			lockaddr.tid = tid;
			tid -> lockaddr;

		}
		
	 */
	int idx = 0;
	// 1. 若该资源(锁)不存在,添加资源到资源链表中
	if (-1 == (idx = search_lock(lockaddr))) {// 

		// 寻找资源链表中空闲的位置并添加该资源
		int eidx = search_empty_lock(lockaddr);

		tg->locklist[eidx].id = tid;
		tg->locklist[eidx].lock_id = lockaddr;

		// 资源数量+1
		tg->lockidx ++;
		
	} 
	else {	// 2. 该资源(锁)存在,需要移除自己的请求边

		// 申请该资源的顶点(PROCESS类型)
		struct source_type from;
		from.id = tid;
		from.type = PROCESS;
		add_vertex(from);

		// 拥有该资源的顶点(PROCESS类型)
		struct source_type to;
		to.id = tg->locklist[idx].id;
		to.type = PROCESS;
		add_vertex(to);

		// 申请该资源的顶点数-1
		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;
	// 此锁已被unlock
	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);	//每隔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··············································
// 1.定义原类型
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;

// 2.实现
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);
	
}

// 3. 初始化
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) {
	pthread_t selfid = pthread_self(); 
	printf("thread_routine 1 : %ld \n", selfid);

	pthread_mutex_lock(&r1);
	sleep(1);	// 休眠,允许期间其他线程能运行
	pthread_mutex_lock(&r2);
	
	pthread_mutex_unlock(&r2);
	pthread_mutex_unlock(&r1);

}

void *t2_cb(void *arg) {
	pthread_t selfid = pthread_self(); 
	printf("thread_routine 2 : %ld \n", selfid);

	pthread_mutex_lock(&r2);
	sleep(1);
	pthread_mutex_lock(&r3);

	pthread_mutex_unlock(&r3);
	pthread_mutex_unlock(&r2);

}

void *t3_cb(void *arg) {
	pthread_t selfid = pthread_self(); 
	printf("thread_routine 3 : %ld \n", selfid);
	
	pthread_mutex_lock(&r3);
	sleep(1);
	pthread_mutex_lock(&r4);

	pthread_mutex_unlock(&r4);
	pthread_mutex_unlock(&r3);

}

void *t4_cb(void *arg) {
	pthread_t selfid = pthread_self(); 
	printf("thread_routine 4 : %ld \n", selfid);
	
	pthread_mutex_lock(&r4);
	sleep(1);
	pthread_mutex_lock(&r5);

	pthread_mutex_unlock(&r5);
	pthread_mutex_unlock(&r4);

}

void *t5_cb(void *arg) {
	pthread_t selfid = pthread_self(); 
	printf("thread_routine 5 : %ld \n", selfid);
	
	pthread_mutex_lock(&r5);
	sleep(1);
	pthread_mutex_lock(&r1);

	pthread_mutex_unlock(&r1);
	pthread_mutex_unlock(&r5);

}




// deadlock
// 

int main() {

	init_hook();

	// 启动检测死锁的线程
	start_check();
	printf("start_check\n");

	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/742819.html

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

相关文章

DevExpress开发练习-皮肤选择

安装devexpress支持&#xff1a; DevExpress 21.2.6 默认状态设置为最大化。 设置ribbon属性&#xff1a; /// <summary> /// 设置Ribbon属性 /// </summary> /// <param name"ribbon"></param> private void SetRibbon(RibbonControl ri…

react-flow基础使用及dagre库的使用

前言 最近项目中需要用到拓扑图的展示&#xff0c;最开始选用的是antv的拓扑图组件。antv组件虽然此很方便&#xff0c;但是在布局的时候总是会有莫名其妙的bug&#xff0c;然后自己也想法去解决&#xff08;看前辈经验、官方issue&#xff09;&#xff0c;最后还是不能解决。…

【安卓12源码】WMS的作用及其启动流程

一、WMS 的作用 WMS 在 Android 系统的地位&#xff0c;它作为中间层&#xff0c;连接了上层的 View 框架和下层的 SurfaceFingler。 WMS 主要职责 窗口管理&#xff1a;负责启动、添加、删除窗口&#xff0c;管理窗口大小、层级&#xff0c;核心成员有&#xff1a;WindowCont…

短视频矩阵系统源码开发规则

抖音seo源码部署&#xff08;前端vue jquery layui 后端php&#xff09;组成 抖音SEO源码开发规则是为了提高抖音内容在搜索引擎的排名&#xff0c;增加曝光和流量而制定的一系列准则。 这些开发规则包括但不限于以下几点&#xff1a; 首先&#xff0c;优化关键词&#xff…

Mac OS 源码编译安装Nginx

下载软件 访问地址&#xff1a; https://nginx.org 根据自己的需求下载合适的安装包 首先建个临时目录 nginx-src 并下载所需软件的源码进行配置 mkdir nginx-src cd nginx-src wget https://nginx.org/download/nginx-1.18.0.tar.gz wget https://ftp.pcre.org/pub/p…

前端框架Layui的使用讲解(Layui搭建登录注册页面)

目录 一、前言 1.什么是Layui 2.Layui的背景 3.为什么要使用Layui 4.Layui的模块化 二、Layui使用讲解 1.初识Layui 2.搭建登录页面 静态效果图​ 封装引入文件页面&#xff08;公用页面&#xff09; jsp页面搭建 userDao编写 Servlet页面编写 xml文件配置 3.搭…

DevOps(一)

DevOps 1. DevOps起源1.1 瀑布开发模型1.2 敏捷开发模型 2. DevOps到底是什么&#xff1f;3. DevOps与虚拟化、容器、微服务4. CI/CD是什么 &#xff1f;4.1 CI 持续集成&#xff08;Continuous Integration&#xff09;4.2 CD 持续交付&#xff08;Continuous Delivery&#x…

JAVA基础方法-substring+indexof

substring用法&#xff1a; 1.public String substring(int beginIndex, int endIndex) 第一个参数int为开始的索引&#xff0c;对应String数字中的开始位置&#xff0c; 第二个参数是截止的索引位置&#xff0c;对应String中的结束位置。 public static void main(String[]…

react中styled-components 全局样式设置

前言 使用 styled-components 库时&#xff0c;你可以使用它的 createGlobalStyle 函数来设置全局样式。下面是一个示例&#xff1a; 安装 styled-components npm install styled-components导入 createGlobalStyle 在你的代码文件中导入 createGlobalStyle&#xff1a; i…

信息安全-应用安全-软件成分安全分析(SCA)能力的建设与演进

1. 前言 SCA 概念出现其实很久了。简单来说&#xff0c;就是针对现有的软件系统生成粒度非常细的 SBOM&#xff08;Software Bill of Materials 软件物料单&#xff09;清单&#xff0c;然后通过⻛险数据去匹配有没有存在⻛险组件被引用。目前&#xff0c;市面上比较出色的商业…

laravel6.x文档阅读手册

laravel中文文档6.x 目录 一、入门指南 安装 服务器要求 安装 Laravel Laravel 使用 Composer 来管理项目依赖。因此&#xff0c;在使用 Laravel 之前&#xff0c;请确保你的机器已经安装了 Composer。 通过 Laravel 安装器 首先&#xff0c;通过使用 Composer 安装 Lara…

Django_加载settings配置

当使用下面命令启动django服务时&#xff0c;setting会自动加载 python manage.py runserver 通过查看manage.py可以找到加载代码为 os.environ.setdefault(DJANGO_SETTINGS_MODULE, settings的路径id) 知道settings加载原理后&#xff0c;在调试时可以不用启动http服务&…

如何用 Jenkins+Docker 实现一键自动化部署

本文章实现最简单全面的Jenkinsdockerspringboot 一键自动部署项目&#xff0c;步骤齐全&#xff0c;少走坑路。 环境&#xff1a;centos7git(gitee) 简述实现步骤&#xff1a;在docker安装jenkins&#xff0c;配置jenkins基本信息&#xff0c;利用Dockerfile和shell脚本实现…

滴...这里有一道数据库操作型面试题,已到达,请查收~

系列文章传送门&#xff1a; 【七天入门数据库】第一天 MySQL的安装部署 【七天入门数据库】第二天 数据库理论基础 【七天入门数据库】第三天 MySQL的库表操作 题目&#xff1a;单表查询&#xff0c;根据提供的素材&#xff0c;按下列要求查询相关数据。 题目素材&#x…

前端vue入门(纯代码)27_路由的query参数

安静地努力&#xff01;&#xff01;&#xff01; 【25.Vue Router--路由的query参数】 多级路由在src/router/index.js中【三级路由】的配置如下&#xff1a; // 该文件专门用于创建整个应用的路由器 import VueRouter from "vue-router"; //引入组件 import Abo…

接口自动化测试要做什么?

先了解下接口测试流程&#xff1a; 1、需求分析2、Api文档分析与评审 3、测试计划编写 4、用例设计与评审5、环境搭建&#xff08;工具&#xff09; 6、执行用例 7、缺陷管理 8、测试报告 接口流程详细内容&#xff0c;请狠狠点击下面这篇文章&#xff0c; 《做接口测试的流程…

关于Vue 、vue2、vue3

vue优点&#xff1f;vue2、vue3响应式比较&#xff1f; &#xff08;1&#xff09; 响应式编程 Vue 会自动对页面中某些数据的变化做出响应。通过 MVVM 思想实现数据的双向绑定&#xff0c;让开发者不用再操作 DOM 对象&#xff0c;有更多的时间去思考业务逻辑。 组件化开发…

程序员,你喜欢写文档吗?

博主&#xff1a;爱码叔 个人博客站点&#xff1a; icodebook 公众号&#xff1a;漫话软件设计 微博&#xff1a;程序员涛哥 专注于软件设计与架构、技术管理。擅长用通俗易懂的语言讲解技术。对技术管理工作有自己的一定见解。文章会第一时间首发在个站上&#xff0c;欢迎大家…

【论文解读】A Fast Sub-pixel Motion Estimation Algorithm for H.264/AVC Video Coding

简介 题目&#xff1a;A Fast Sub-pixel Motion Estimation Algorithm for H.264/AVC Video Coding 原文&#xff1a;https://ieeexplore.ieee.org/document/5688303 级别&#xff1a;SCI 年份&#xff1a;2011 年 机构&#xff1a;上海交通大学 结论&#xff1a;亚像素搜索计…

【C++】unordered_map、unordered_set 模拟实现

文章目录 概念框架实现正反迭代器Find()、Insert() 、Erase()unordered_map 的 operator[ ] 源代码HashTable.hunordered_map.hunordered_set.h 概念 unordered_set 是含有 Key 类型唯一对象集合的关联容器。搜索、插入和移除拥有平均常数时间复杂度。在内部&#xff0c;元素并…