Linux下的死锁检测组件(分模块讲解)

目录

一:什么是死锁?

1、资源的分配(锁)

2、死锁的产生

二:怎么寻找死锁?

1、什么类型可以导致死锁

2、有向图的使用

3、对于锁的存储

三:死锁检测的构成

1、有向图的重要函数

        1.1、结点的搜索

        1.2、结点的添加

        1.3、边的添加

        1.4、圆圈的搜索

  2、关于锁的函数

        2.1、锁的查找

        2.2、锁的添加和删除

        2.3、加锁之前的操作

        2.4、加锁之后的操作

        2.5、解锁之后的操作

        2.6、加锁、解锁的函数重载


一:什么是死锁?

1、资源的分配(锁)

        我们现在所碰到关于锁的操作,基本上都是在多线程中怕碰到的,比如一个线程使用一把锁,在每次使用资源之前都要先加锁的操作。

2、死锁的产生

        现在有两个线程(A,B),两把锁(a,b),现在A占用a,B占用b,当都占用之后,现在A还想占用b的资源,而B也想占用a的资源,但是A要占用b之前需要等待B释放b,但是此时的B却是在等待A释放a,A和B都在互相等待,互不释放锁,因此产生了死锁。配一张图可以更好的理解。当然也可以通过代码实现死锁。

//其中的sleep一定要先拿到一个锁之后再sleep,这样保证两个线程都会拿到第一个锁
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(&r1);

	pthread_mutex_unlock(&r1);
	pthread_mutex_unlock(&r2);
//两方都在互相等待,于是产生了死锁
}

二:怎么寻找死锁?

1、什么类型可以导致死锁

        我们上面是两个线程,当出现多个线程的时候是什么样子呢?如果上面的函数再增加两个,然后互相等待,A等待B,B等待C,C等待D,D等待A,这样形成了一个圆圈,大家都在等,类似于一个圆这种类型的,就可以导致死锁的产生。

2、有向图的使用

        对于上面的死锁会形成一个圆,那么我们学到的一个结构也就是有向图,使用这个结构就可以检测是否成为一个圆。

3、对于锁的存储

        3.1、加锁前:为了避免成为死锁,当我们每次加锁之前,要在一个表中进行检查,查看这个锁是不是已经被使用了。如果没有被使用,那么我们就可以进行加锁。如果已经使用了,那么我们就向有向图中添加一条边指向已经占用这个锁的线程id,表示我们想拥有你所占用的锁。

        3.2、加锁后:加锁后为了方便其他线程进行检查,要保存当前的线程id和锁的id,表示我加了一个锁。并且将有向图的一条边进行删除操作,因为我已经得到我想要的锁了,因此可以删掉。

        3.3、解锁后:当不需要这个锁了,解锁后,要将存放在表中的线程id和锁的id进行删除。

其实我们通过上面所描述的,已经是这个死锁检测中最重要的函数了,其他重要的就是有向图的创建和检查是否成为了圆。

三:死锁检测的构成

1、有向图的重要函数

        1.1、结点的搜索

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;
}

        1.2、结点的添加

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 ++;
	}
}

        1.3、边的添加

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);
}

        1.4、圆圈的搜索

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;
	}
}

  2、关于锁的函数

        对于锁的一些操作,有单独的结构体(线程id、锁的id)来存储,和有向图是没关系的。

        2.1、锁的查找

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

        2.2、锁的添加和删除

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

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

        2.3、加锁之前的操作

void before_lock(pthread_t tid, pthread_mutex_t *mtx) {
	pthread_t otherid = search_rela_table(mtx);//先通过上面的搜索函数,进行搜索,当发现这把锁已经被别人使用,那么就向有向图中添加一条边指向这个锁的线程id,表示我想拥有这把锁
	if (otherid != 0) {
		struct source_type from;
		from.id = tid;
		from.type = PROCESS;

		struct source_type to;
		to.id = otherid;
		to.type = PROCESS;	
		add_edge(from, to);    //添加边,从我到你
	}
}

        2.4、加锁之后的操作

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);
		}
	}
	add_rela_table(mtx, tid);    //已经加锁了,那就从加锁的表中添加
}

        2.5、解锁之后的操作

void after_unlock(pthread_t tid, pthread_mutex_t *mtx) {
	del_rela_table(mtx, tid);    //解锁后从表中删除数据
}

        2.6、加锁、解锁的函数重载

int pthread_mutex_lock(pthread_mutex_t *mtx) {
	printf("before pthread_mutex_lock %ld, %p\n", pthread_self(), mtx);
	before_lock(pthread_self(),mtx);

	pthread_mutex_lock_f(mtx);		//通过函数指针,执行系统的加锁操作。

	printf("after pthread_mutex_lock\n");
	after_lock(pthread_self(),mtx);
}

int pthread_mutex_unlock(pthread_mutex_t *mtx) {
	pthread_mutex_unlock_f(mtx);//通过函数指针,执行系统的解锁操作。

	printf("after pthread_mutex_unlock %ld\n, %p", pthread_self(), mtx);
	after_unlock(pthread_self(),mtx);
}

        然后使用最开始举的例子,就可以检测是否产生死锁了,当然可以单独起一个线程,每隔几秒就检查是否死锁。感谢大家的观看!https://xxetb.xetslk.com/s/2D96kH

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值