实现死锁检测

根据百度百科对于死锁的定义:
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。可能很多人看到这个定义有点懵,我用一个示意图简单的描述一下:
死锁环
从上图不难看出,线程1、线程2、线程3之间形成了一资源申请的环路,造成死锁。总结死锁本质特征就是:存在锁资源申请的环。
接下来讨论如何实现整个死锁方案:
第一步:实现一个简单死锁的例子,便于后续对于死锁的验证,同时感受下死锁的现象。

#include<stdio.h>
#include<pthread.h>
//创建几个基本的线程:
pthread_mutex_t mutex_1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_3 = PTHREAD_MUTEX_INITIALIZER;

void *test_thread1(void *arg)
{
	pthread_mutex_lock(mutex_1);
	sleep(1);
	pthread_mutex_lock(mutex_2);
	pthread_mutex_lock(mutex_2);
	pthread_mutex_lock(mutex_1);
}
void *test_thread2(void *arg)
{
	pthread_mutex_lock(mutex_2);
	sleep(1);
	pthread_mutex_lock(mutex_3);
	pthread_mutex_lock(mutex_3);
	pthread_mutex_lock(mutex_2);
}
void *test_thread3(void *arg)
{
	pthread_mutex_lock(mutex_3);
	sleep(1);
	pthread_mutex_lock(mutex_1);
	pthread_mutex_lock(mutex_1);
	pthread_mutex_lock(mutex_3);
}

int main()
{
    pthread_t tid1, tid2, tid3;
    pthread_create(&tid1, NULL, test_thread1, NULL);
    pthread_create(&tid2, NULL, test_thread2, NULL);
    pthread_create(&tid3, NULL, test_thread3, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    return 0;
}

第二步是设置钩子程序用来对于加锁和解锁调用关系进行记录,一是加锁前的状态,二是加锁后的状态,三是加锁结束后状态。钩子设置一个是项目中直接利用封装后的函数进行操作,这时候就不需要设置钩子程序直接封装后程序进行操作;另一种是利用系统调用实现钩子函数:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdint.h>
typedef int (*pthread_lock_func)(pthread_mutex_t *mutex);
pthread_lock_func lock_func;
typedef int (*pthread_unlock_func)(pthread_mutex_t *mutex);
pthread_unlock_func unlock_func;

//预留构建锁调用关系构建函数
int lock_before(uint64_t tid, uint64_t mutex)
{
	return 0;
}
int lock_after(uint64_t tid, uint64_t mutex)
{
	return 0;
}
int unlock_after(uint64_t tid, uint64_t mutex)
{
	return 0;
}

int pthread_mutex_lock(pthread_mutex_t *mutex) 
{
    pthread_t tid = pthread_self(); 
	lock_before(tid, (uint64_t)mutex);//加锁前处理
    lock_func(mutex);
	lock_after(tid, (uint64_t)mutex);//加锁后处理

}

int pthread_mutex_unlock(pthread_mutex_t *mutex) 
{
	pthread_t tid= pthread_self();
    unlock_func(mutex);
	unlock_after(tid, (uint64_t)mutex);//解锁后处理
}

static int init_mutex_lock_hook()
{
	lock_func = dlsym(RTLD_NEXT, "pthread_mutex_lock");
	unlock_func = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
}

第三步是整体调用关系构建,为了表达调用关系需要引入一个向量的概念,即需要记录调用的起点也需要记录调用的终点,形成一个锁的请求关系。起点就是未得到资源的线程,终点就是已经得到锁资源的的线程。为了记录这种关系需要引入图进行两个维度的记录,一个是纵向记录目前的被占用锁的节点,另一个是横向记录锁之间调用关系。

#define LOCK_NUM_MAX 	10
//首先定义图的结构
typedef struct lock_node{
	uint64_t thread_id;
	uint64_t mutex_id;
}lock_node;

typedef struct lock_vertex{
	lock_node node;
	struct lock_vertex *next;
}lock_vertex;

typedef struct lock_graph{
	struct lock_vertex vertex[LOCK_NUM_MAX];
	int num;
}lock_graph;

lock_graph *g_graph = NULL;
//图基本操作
lock_vertex *creat_vertex(lock_node node)
{
	lock_vertex *vertex = (lock_vertex *)calloc(1, sizeof(lock_vertex));
	if(!vertex)
	{
		printf("[%s:%d]calloc failed!\n");
		return NULL;
	}
	
	vertex->node = node;
	vertex->next = NULL;
	
	return vertex;
}

int search_vertex(lock_node node)
{
	if(!g_graph) return -1;
	
	int i = 0;
	for(i = 0; i < g_graph->num; i++)
	{
		if(g_graph->vertex[i].node.thread_id = node.thread_id)	return i;
	}
	
	return -1;
}

int add_vertex(lock_node node)
{
    if(!g_graph) return -1;

	if(search_vertex(node) == -1)
	{
		g_graph->vertex[g_graph->num].node.thread_id = node.thread_id;
		g_graph->vertex[g_graph->num].node.mutex_id = node.mutex_id;
		g_graph->num++;
	}

    return 0;
}

int add_edge(lock_node from, lock_node to)
{
    if(!g_graph) return -1;

	add_vertex(from);
	add_vertex(to);
	
	lock_vertex *v = &g_graph->vertex[search_vertex(from)];
	while(!v->next)
	{
		v = v->next;
	}
	v->next = creat_vertex(to);

    return 0;
}

int remove_edge(lock_node from, lock_node to)
{
    if(!g_graph) return -1;

    int idxf = search_vertex(from);
    int idxt = search_vertex(to);

    lock_vertex *v = &g_graph->vertex[idxf];
    lock_vertex *tmp = NULL;
    while(!v->next)
    {
        if(v->next->node.thread_id = to.thread_id)
        {
            tmp = v->next;
            v->next = v->next->next;
            free(tmp); 
            remove_vertex(to);
        }
         v = v->next;
    }

    return 0;
}

int remove_vertex(lock_node node)
{
    if(!g_graph) return -1;

    int idx = search_vertex(node);

    lock_vertex *v = &g_graph->vertex[idx];
    lock_vertex *tmp = NULL;
    while(!v->next)
    {
        tmp = v->next;
        v = v->next;
        free(tmp);  
    }

    g_graph->vertex[idx].node.thread_id = 0;
    g_graph->vertex[idx].node.mutex_id = 0;
    g_graph->num--;

    return 0;
}

第四步绘制线程间锁关系图:

//待续...
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值