根据百度百科对于死锁的定义:
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。可能很多人看到这个定义有点懵,我用一个示意图简单的描述一下:
从上图不难看出,线程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;
}
第四步绘制线程间锁关系图:
//待续...