死锁检测实现

一、背景

  在工作项目使用多进程、多线程过程中,因争夺资源而造成一种资源竞态,所以需加锁处理。如下图所示,线程A想获取线程B的锁,线程B想获取线程C的锁,线程 C 想获取线程D的锁, 线程D想获取线程A的锁,从而构建了一个资源获取环,当进程或者线程申请的锁处于相互交叉锁住的情况,就会出现死锁,它们将无法继续运行。

  死锁的存在是因为有资源获取环的存在,所以只要能检测出资源获取环,就等同于检测出死锁的存在。

二、原理

  在不改变项目源代码的情况下,采用图算法来检测环的存在,使用有向图来存储;如线程A获取线程B已占用的锁(表示线程B获取锁成功),则为线程A指向线程B;启动一个线程定时对图进行检测是否有环的存在。

(1)数据结构

//数据/点struct node{uint64 thread_id;//线程IDuint64 lock_id;//锁IDint degress;};//数据和数据结构分开struct vertex{struct node *d;struct vertex *next;};struct graph{struct vertex list[THREAD_MAX];//存储图的所有节点int num;//已经使用了多少个struct node locklist[THREAD_MAX];int lockidx;pthread_mutex_t mutex;//预留:线程安全考虑,在对图修改时加锁};

(2)图的操作

    a.创建图节点

//创建图节点struct vertex *create_vertex(struct node *d){struct vertex *tex =(struct vertex*)calloc(1,sizeof(struct vertex));if(tex == NULL) return NULL;tex->d = d;tex->next = NULL;return tex;}

  b.查找节点

//查找节点,是否存在int search_vertex(struct node *d){int i;for (i = 0; i num; i++){if (tg->list[i].d->thread_id == d->thread_id){return i;}}

  c.添加节点

//添加节点,只是把添加的节点放到list中,还没有确定各节点间的指向,必须通过add_edge添加边来确定void add_vertex(struct node *d){if (search_vertex(d) == -1){tg->list[tg->num].d = d;//添加到list中tg->list[tg->num].next = NULL;tg->num++;//节点数累加}}

  d.添加边,指定方向

//添加边,指定方向,谁指向谁void add_edge(struct node *from, struct node *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);}

  e.检测节点间是否有边

//检测节点from和to间是否有边连接int verifty_edge(struct node *from, struct node *to){if(tg->num == 0) return 0;int idx = search_vertex(from);if(idx == -1) return 0;struct vertex *v = &(tg->list[idx]);while(v != NULL){if(v->d->thread_id == to->thread_id) return 1;v = v->next;}return 0;}

  f.删除边

//删除边void remove_edge(struct node *from, struct node *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->d->thread_id == to->thread_id){//找到要删除的节点remove = v->next;v->next = v->next->next;free(remove);break;}v = v->next;}}}

(3)图遍历

  本文采用图遍历中最为常用的深度优先搜索进行遍历,代码如下。

//dfs深度遍历int dfs(int idx){struct vertex *v = &tg->list[idx];if(visited[idx] == 1){//有环path[k++] = idx;print_deadlock();deadlock = 1;return 0;}visited[idx] =1;//被遍历到了,赋值为1,保证同一个节点只能遍历一次path[k++] = idx;while(v->next !=NULL){dfs(search_vertex(v->next->d));k–;v = v->next;}return 1;}//遍历图,任意从图的一个节点出发,对每一个节点进行dfs遍历int search_for_cycle(int idx){struct vertex *v = &tg->list[idx];visited[idx] = 1;k = 0;path[k++] = idx;while(v->next != NULL){int i = 0;for (; i num; i++){if(i == idx){continue;}visited[i] = 0;}for(i = 1; i next->d));v = v->next;}}

(4)启动检测

C++后台开发架构师免费学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以自行添加点击 正在跳转 群文件共享

  启动线程定时检测图是否有环,代码如下。

//从第0个节点开始dfsvoid check_dead_lock(){int i = 0;deadlock = 0;for(;i num; i++){if(deadlock == 1) break;search_for_cycle(i);}if(deadlock == 0){printf(“no deadlock”);}}//检测锁线程funcstatic void *thread_func(void *args){while(1){sleep(5);check_dead_lock();}}//启动检测锁线程void start_check(){tg = (struct graph*)malloc(sizeof(struct graph));tg->num = 0;tg->lockidx = 0;pthread_t tid;pthread_create(&tid,NULL,thread_func,NULL);}

(5)钩子hook

  为了不改变项目原代码,使用hook在应用程序调用系统加锁、解锁API时进行劫持,使其实际调用的是应用程序定义的加锁、解锁API;再进行加锁、解锁前,我们先去理解3个状态,加锁前、加锁后、解锁后,即:lock_before、lock_after、unlock_after,通过这三个函数与图构建起来,具体实现如下。

//1.没有被其他线程占用,不用处理//2.有被其它线程占用,就要把边构建起来//添加边void lock_before(uint64 thread_id, uint64 lockid){int idx = 0;for(;idx lockidx;idx++){if(tg->locklist[idx].lock_id == lockid){struct node from;from.thread_id = thread_id;add_vertex(&from);struct node to;to.thread_id = tg->locklist[idx].thread_id;tg->locklist[idx].degress++;add_vertex(&to);if(!verifty_edge(&from, &to)){add_edge(&from, &to);//添加边}}}}//1.没有被其它线程占用//先加入一个节点add_edge//2.有被占用//是进不来lock_after的////等unlock_after 释放后//mtx没有主人void lock_after(uint64 threadid, uint64 lockid) {int idx = 0;if(-1 == (idx = search_lock(lockid))){int eidx = search_empty_lock();tg->locklist[eidx].thread_id = threadid;tg->locklist[eidx].lock_id = lockid;inc(&tg->lockidx, 1);}else{struct node from;from.thread_id = threadid;struct node to;to.thread_id = tg->locklist[idx].thread_id;tg->locklist[idx].degress–;if(verifty_edge(&from, &to)){remove_edge(&from, &to);//不在死锁检测的圈里面了,所以删除边}tg->locklist[idx].thread_id = threadid;}}void unlock_after(uint64 threadid, uint64 lockid) {int idx = search_lock(lockid);if(tg->locklist[idx].degress == 0){tg->locklist[idx].thread_id = 0;tg->locklist[idx].lock_id = 0;}}

honk钩子主要实现pthread_mutex_lock、pthread_mutex_unlock的劫持,具体实现如下。

int pthread_mutex_lock(pthread_mutex_t *mutex){pthread_t selfid = pthread_self();lock_before(selfid, (uint64)mutex);pthread_mutex_lock_f(mutex);//执行系统加锁的入口函数lock_after(selfid, (uint64)mutex);}int pthread_mutex_unlock(pthread_mutex_t * mutex){pthread_t selfid = pthread_self();pthread_mutex_unlock_f(mutex);//执行系统解锁的入口函数unlock_after(selfid, (uint64)mutex);}static int init_hook(){pthread_mutex_lock_f = dlsym(RTLD_NEXT,”pthread_mutex_lock”);pthread_mutex_unlock_f = dlsym(RTLD_NEXT,”pthread_mutex_unlock”);}

(6)Demo

//测试样例pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t mtx3 = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t mtx4 = PTHREAD_MUTEX_INITIALIZER;void *th_func1(void *arg) {pthread_mutex_lock(&mtx1);sleep(1);pthread_mutex_lock(&mtx2); pthread_mutex_unlock(&mtx2);pthread_mutex_unlock(&mtx1);}void *th_func2(void *arg) {pthread_mutex_lock(&mtx2);sleep(1);pthread_mutex_lock(&mtx3);pthread_mutex_unlock(&mtx3);pthread_mutex_unlock(&mtx2);}void *th_func3(void *arg) {pthread_mutex_lock(&mtx3);sleep(1);pthread_mutex_lock(&mtx1);pthread_mutex_unlock(&mtx1);pthread_mutex_unlock(&mtx3);}void *th_func4(void *arg) {pthread_mutex_lock(&mtx2);sleep(1);pthread_mutex_lock(&mtx3);pthread_mutex_unlock(&mtx3);pthread_mutex_unlock(&mtx2);}int main(){init_hook();//初始化hookstart_check();//启动检测死锁线程pthread_t t1,t2,t3,t4;pthread_create(&t1,NULL,th_func1,NULL);pthread_create(&t2,NULL,th_func2,NULL);pthread_create(&t3,NULL,th_func3,NULL);pthread_create(&t4,NULL,th_func4,NULL);pthread_join(t1,NULL);pthread_join(t2,NULL);pthread_join(t3,NULL);pthread_join(t4,NULL);return 0;}

原文地址:死锁检测实现 – MrJuJu – 博客园

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
(0)
用户投稿
上一篇 2022年9月25日
下一篇 2022年9月25日

相关推荐

  • MySQL 索引原理详解

    一、 索引是什么? 1.1. 索引是什么 当一张表有 500 万条数据,在没有索引的 name 字段上执行一个查询: select * from user_innodb where…

    2022年6月20日
  • 数字藏品未来走向:脱虚向实与回归技术本质并行

    尽管相对于海外NFT产品,当下数字藏品存在所属权不明、公开透明性差等问题,但也有着监管可控、实体赋能等无可比拟的优势,而对其不足之处,数字藏品产业界也正加速突破。 从底层基础设施而…

    2022年8月16日
  • 什么原因导致了企业内耗严重,中高层管理者各自为政,沟通困难?

    企业内耗严重,中高层各自为政,沟通困难,主要表现在四个方面: 第一,企业董事长太过强势,中高层管理者怕担责,各自做好自己的事情,缺乏有效链接; 第二,企业缺少现代化管理体系,没有建…

    2022年4月15日
  • 洛克希德-马丁宣布将把区块链技术送上月球

    长期以来,加密货币的推动者一直称要去月球,现在看来区块链技术似乎很快就会真正到达太空。洛克希德-马丁于当地时间周一宣布,其计划在太空中托管一个分散的存储网络,因为它希望建立能够维持…

    2022年8月15日
  • 国家发改委谈新基建:下半年一批重大项目将开工建设

    来源:中新社 中新社北京6月16日电 (记者 王恩博)中国国家发改委新闻发言人孟玮16日谈及新型基础设施建设时透露,从目前掌握的储备项目情况看,下半年各地方和骨干企业将加大投资力度…

    2022年6月18日
  • 游戏本和掌机随意切换,GPD WIN Max 2为游戏而生

    游戏本和掌机都是我们常见的电子设备,但当两者融为一体时,对于玩家来说,这种设备就通吃几乎所有的游戏,这就是GPD推出的WIN Max 2全面掌上游戏本,这款产品不仅有着游戏本的性能…

    2022年7月27日
  • Python算法之哈夫曼编码

    问题:哈夫曼编码,英文名称Huffman Coding,有时也翻译为霍夫曼编码,在1952年提出的,是最好的编码方式。哈夫曼编码在电子通讯方面有着重要的应用,同时也广泛应用于数据压…

    2022年6月12日
  • 愉快买买买:选对CPU能省显卡的钱?

    稿源:中关村在线 游戏玩家经常面临的问题是,如何压榨有限的预算,让它发挥出最大的性能。不过30系显卡的高溢价让不少玩家的攒机梦想破灭,动辄数千元的溢价但显卡的价格比肩多年前整机的价…

    2022年8月4日
  • 加入集度汽车不足半年副总裁朱江离职,业内分析其发挥作用有限

    记者 | 周姝祺 编辑 | 新浪科技从两位知情人士获悉,集度汽车负责用户发展与运营的副总裁朱江已经离职,离职时间为4月中旬,距其加入集度汽车不足半年时间。 据一名熟悉朱江的知情人士…

    2022年6月14日
  • 惊呆了!手写4个mini版的tomcat

    写在前面 Apache Tomcat 是Java Servlet, JavaServer Pages (JSP),Java表达式语言和Java的WebSocket技术的一个开源实现…

    2022年7月11日

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息