当前位置: 首页 > news >正文

将线程和锁构建图(后续通过检测环路以检测死锁)

流程

  1. 劫持pthread_mutex_lock/unlock,在加锁/解锁前后插入自定义逻辑。
  2. 分配并记录每个线程和互斥锁的ID,并映射到数组,方便查找。
  3. 构建有向图表示线程和锁的关系,持有:锁-->线程,请求:线程-->锁。
  4. 解锁时,清除图中关系并释放资源。
  5. 主线程创建4个线程,互相持有锁,进入死锁状态。

代码细节

  1. 函数劫持:用dlsym获取原版pthread_mutex_lock地址,并包装为函数,插入自定义内容。
  2. 映射数组:用两个数组分别记录锁和线程指针,下标即分配的ID。
  3. 创建节点:节点字段为ID(数组映射),next(下个节点的指针),flags(区分线程和锁)。
  4. 加锁:通过查看锁是否空闲判断应该是持有状态还是请求状态。
  5. 状态转换:通过该函数让锁从请求状态变为持有状态。
  6. 释放:额外维护节点数组,将从图中释放的节点回收到数组里,避免频繁malloc&free。

代码

#define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <pthread.h> #include <dlfcn.h> #include <stdlib.h> #define MAX 100 struct lock_node_s; struct pthread_node_s; // 节点 struct node_s { size_t id; // 线程/锁ID struct node_s *next; // 锁指向下一个表示锁被线程持有,线程指向下一个表示线程请求锁 int flag; // 0--线程,1--锁 }; // 创建数组,专门用来记录线程/锁和它对应的ID--即下标 pthread_mutex_t *rlocks[MAX] = {NULL}; pthread_t rthreads[MAX] = {0}; // 存储节点数组 struct node_s *nodes[200] = {NULL}; // 创建数组,检查锁是否已经被占有 size_t lockarr[MAX] = {0}; struct Graph { struct node_s *nodearr[200]; // 前100是线程指向锁,后100是锁指向线程 }; struct Graph graph; // 线程请求锁,线程同时只能请求一个锁(等待被其他线程占有的锁释放) void Request(size_t pid, size_t lid) { if (nodes[pid] == NULL) { struct node_s *nodep = (struct node_s *)malloc(sizeof(struct node_s)); nodep->id = pid; nodep->next = NULL; nodep->flag = 0; nodes[pid] = nodep; } if (nodes[lid] == NULL) { struct node_s *nodel = (struct node_s *)malloc(sizeof(struct node_s)); nodel->id = lid; nodel->next = NULL; nodel->flag = 1; nodes[lid + 100] = nodel; } if (lockarr[lid] == 0) // 如果为0,表示锁没有被持有,那么线程就可以持有锁,即锁指向线程 { graph.nodearr[lid + 100] = nodes[lid + 100]; graph.nodearr[lid + 100]->next = nodes[pid]; nodes[lid + 100] = NULL; nodes[pid] = NULL; } else // 如果不为0,表示锁被持有,那么线程只能请求锁,即线程指向锁 { graph.nodearr[pid] = nodes[pid]; graph.nodearr[pid]->next = nodes[lid + 100]; nodes[lid + 100] = NULL; nodes[pid] = NULL; } } // 记录锁和线程对应的ID,并将他们添加到图里 void Record(pthread_t thid, pthread_mutex_t *mtx) { // 先判断是否存在,如果不存在则查找空余创建,index和flag记录第一个空白位置 int exist = 0, index = 0, flag = 1; int lid = 0; for (lid = 0; lid < MAX; lid++) { if (rlocks[lid] == mtx) { exist = 1; break; } if (flag && rlocks[lid] == NULL) { index = lid; flag = 0; } } if (!exist) { lid = index; rlocks[lid] = mtx; } exist = 0, flag = 1; int pid = 0; for (pid = 0; pid < MAX; pid++) { if (rthreads[pid] == thid) { exist = 1; break; } if (flag && rthreads[pid] == 0) { index = pid; flag = 0; } } if (!exist) { pid = index; rthreads[pid] = thid; } // 线程去请求锁 Request(pid, lid); } // 线程持有锁,锁只能被一个线程持有 void Hold(pthread_t thid, pthread_mutex_t *mtx) { int tid = 0; for (tid = 0; tid < MAX; tid++) { if (rthreads[tid] == thid) { break; } } int lid = 0; for (lid = 0; lid < MAX; lid++) { if (rlocks[lid] == mtx) { break; } } // 本来是线程指向锁,现在线程持有锁了,改为锁指向线程 graph.nodearr[lid + 100] = graph.nodearr[tid]->next; graph.nodearr[lid + 100]->next = graph.nodearr[tid]; graph.nodearr[tid]->next = NULL; graph.nodearr[tid] = NULL; lockarr[lid] = 1; } void Release(pthread_t thid, pthread_mutex_t *mtx) { int tid = 0; for (tid = 0; tid < MAX; tid++) { if (rthreads[tid] == thid) { break; } } int lid = 0; for (lid = 0; lid < MAX; lid++) { if (rlocks[lid] == mtx) { break; } } // 回收锁和其指向的节点 int index = graph.nodearr[lid + 100]->id; nodes[index + 100] = graph.nodearr[lid + 100]; if (graph.nodearr[lid + 100]->next != NULL) { index = graph.nodearr[lid + 100]->next->id; nodes[index] = graph.nodearr[lid + 100]->next; // 将图中节点置空 graph.nodearr[lid + 100]->next = NULL; } graph.nodearr[lid + 100] = NULL; } // 构建函数指针 typedef int (*pthread_mutex_lock_t)(pthread_mutex_t *mtx); pthread_mutex_lock_t pthread_mutex_lock_f; typedef int (*pthread_mutex_unlock_t)(pthread_mutex_t *mtx); pthread_mutex_unlock_t pthread_mutex_unlock_f; // 劫持函数 void init_hook(void) { if (!pthread_mutex_lock_f) { pthread_mutex_lock_f = dlsym(RTLD_DEFAULT, "pthread_mutex_lock"); } if (!pthread_mutex_unlock_f) { pthread_mutex_unlock_f = dlsym(RTLD_DEFAULT, "pthread_mutex_unlock"); } } // 用我们自己的锁函数,再通过劫持,就可以把原版锁函数添上我们自己的一些东西 void my_lock(pthread_mutex_t *mtx) { printf("加锁 %ld, %p\n", pthread_self(), mtx); Record(pthread_self(), mtx); pthread_mutex_lock_f(mtx); printf("持有\n"); Hold(pthread_self(), mtx); } void my_unlock(pthread_mutex_t *mtx) { printf("解锁 %ld, %p\n", pthread_self(), mtx); pthread_mutex_unlock_f(mtx); printf("释放\n"); Release(pthread_self(), mtx); } // 初始化锁 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 *tb1(void *arg) { my_lock(&mtx1); sleep(1); my_lock(&mtx2); my_unlock(&mtx2); my_unlock(&mtx1); return NULL; } void *tb2(void *arg) { my_lock(&mtx2); sleep(1); my_lock(&mtx3); my_unlock(&mtx3); my_unlock(&mtx2); return NULL; } void *tb3(void *arg) { my_lock(&mtx3); sleep(1); my_lock(&mtx4); my_unlock(&mtx4); my_unlock(&mtx3); return NULL; } void *tb4(void *arg) { my_lock(&mtx4); sleep(1); my_lock(&mtx1); my_unlock(&mtx1); my_unlock(&mtx4); return NULL; } int main(int argc, char const *argv[]) { init_hook(); // 创建线程ID pthread_t t1, t2, t3, t4; // 创建线程 pthread_create(&t1, NULL, tb1, NULL); pthread_create(&t2, NULL, tb2, NULL); pthread_create(&t3, NULL, tb3, NULL); pthread_create(&t4, NULL, tb4, NULL); // 第二个函数表示线程那个函数的返回值 pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); pthread_join(t4, NULL); printf("complete\n"); return 0; }

https://github.com/0voice

http://www.jsqmd.com/news/478873/

相关文章:

  • 学长亲荐 9个降AI率软件降AIGC网站:本科生降AI率全测评与推荐
  • 2026年呼和浩特靠谱瓷砖大揭秘!哪种款式数量多你知道吗?
  • 哇塞!深圳市访客云产品1秒完成人证合一验证,你还不心动?
  • 拒绝纸上谈兵!破碎机振动治理,实测有效的定制化方案
  • 某端游外挂网络验证的分析与破解思路
  • 使用实时云渲染LarkXR顺利搭建云VR方案
  • Matlab中的一维无限能量艾里光束与一维有限能量艾里光束
  • 农经权二轮延包—带确认签字表的公示图
  • 方盾在口,安全在手:煤矿半面罩的职业健康使命
  • 轮毂电机分布式驱动车辆状态估计:EKF 与 UKF 的探索
  • 【Makefile 专家之路 | 基础篇】01. 万物起源:编译链接原理与 Makefile 的核心价值
  • Qt窗口模态设置
  • 别再拿AI生成“废片”了!Claude突发免费杀手锏,用动态白板降维打击传统图文交互
  • 讯维 AI 分布式可视化系统支持哪些 AI 智能应用,实际应用价值如何?
  • 老司机教你玩转智能避撞:从五次多项式到模型预测控制的那些事儿
  • 2026-03-14
  • YOLOv10全网首发:AAAI2026 | 融合PartialNet Block的C3k2-YOLO高效目标检测网络 | 轻量化涨点设计
  • 2026年好用的北京GEO优化公司排名出炉,哪家会是你的心头好?
  • 联合省选 2026
  • sdut-程序设计基础Ⅰ-实验四for循环(11-22)
  • 使用Conda和pip创建Python环境
  • 蓝牙连接不上的解决方法
  • 淘宝系逆向@阿里巴巴商家版-转人工逆向
  • 迁移学习中的负迁移风险:成因、检测与规避方案
  • 国家网络与信息安全信息通报中心通报OpenClaw安全风险预警
  • 三部六层电梯仿真群控联动系统:基于西门子S7-1200 PLC与博图v15.1及以上版本实现方...
  • 告别学术焦虑,您的 AI 级个人科研大脑 —— “openclaw论文助手”现已发布!
  • openclaw 使用飞书官方插件连接飞书
  • COMSOL光学模型中的等离激元BIC非偏振结构
  • 多任务学习的任务冲突问题