Linux C/C++服务器
死锁检测组件
死锁存在的条件
死锁,是指多个线程或者进程在运行过程中因争夺资源而造成的一种僵局,当进程或者线程处于这种僵持状态,若无外力作用,它们将无法再向前推进。如下图所示,线程 A 想获取线程 B 的锁,线程 B 想获取线程 C 的锁,线程 C 想获取线程 D 的锁,线程 D 想获取线程 A 的锁,从而构建了一个资源获取环。
死锁的存在是因为有资源获取环的存在,所以只要能检测出资源获取环,就等同于检测出死锁的存在。
多线程死锁demo
typedef
定义回调函数的函数指针
typedef 的作用有以下几点:
- typedef 的一个重要用途是定义机器无关的类型。例如,定义一个叫“REAL”的浮点类型,该浮点类型在目标机器上可以获得最高的精度:
typedef long double REAL;
如果在不支持 long double 的机器上运行相关代码,只需要修改对应的 typedef 语句,
typedef double REAL;
- 使用 typedef 为现有类型创建别名,给变量定义一个易于记忆且意义明确的新名字,
typedef unsigned int UINT;
- 使用 typedef 简化一些比较复杂的类型声明,函数指针固定的写法:
typedef void (*PFunCallBack)(char* pMsg, unsigned int nMsgLen);
上述声明引入了 PFunCallBack 类型作为函数指针的同义字,PFunCallBack 类型定义的指针会指向一个函数,该函数包含两个类型分别为 char* 和 unsigned int 的参数,以及一个类型为 void 的返回值。通常,当函数的参数是一个回调函数时,就可能会使用 typedef 来简化声明。
例如,承接上面的示例的后续示例如下:
RedisSubCommand(const string& strKey, PFunCallBack pFunCallback, bool bOnlyOne);
注意:上述语句中类型名 PFunCallBack 与变量名 pFunCallback 的大小写区别。
RedisSubCommand 函数的第二个参数是一个回调函数,因此通过“PFunCallBack pFunCallback”(即,类型+变量)的形式,给出对应回调函数(pFunCallback)的地址。
在这个示例中,如果不使用 typedef 简化声明,RedisSubCommand 的函数声明内容如下:
RedisSubCommand(const string& strKey, void (*pFunCallback)(char* pMsg, unsigned int nMsgLen), bool bOnlyOne);
从上面两个函数声明可以看出,在不使用 typedef 的情况下,RedisSubCommand 函数的声明会复杂得多,不利于代码的维护,同时增加出错风险。
所以,在某些复杂的类型声明中,使用 typedef 进行声明的简化是很有必要的。
hook系统函数
作用:修改自己调用的系统函数 或者在系统函数上做一层封装,加一些日志信息等等
如何实现hook:
- 定义目标函数一样的类型(函数指针类型)
- 实现目标函数名一致
- dlsym
比如接下来,我们对pthread_mutex_lock和pthread_mutex_unlock做hook操作
#define _GNU_SOURCE
#include <dlfcn.h>
typedef int (*pthread_mutex_lock_t)(pthread_mutex_t *mutex); //函数指针类型别名,pthread_mutex_lock_t可以定义函数指针变量
typedef int (*pthread_mutex_unlock_t)(pthread_mutex_t *mutex);
pthread_mutex_lock_t pthread_mutex_lock_f; //定义一个函数指针变量
pthread_mutex_unlock_t pthread_mutex_unlock_f;
int pthread_mutex_lock(pthread_mutex_t *mutex) {
pthread_t selfid = pthread_self();
pthread_mutex_lock_f(mutex);
printf("pthread_mutex_lock: %ld, %p\n", selfid, mutex);
}
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
pthread_t selfid = pthread_self();
pthread_mutex_unlock_f(mutex);
printf("pthread_mutex_unlock: %ld, %p\n", selfid, mutex);
}
//hook修改内核函数很常用
//dlsym返回一个函数指针,只要有这个出现,程序就会执行我们自己写的pthread_mutex_lock这个函数,而不是去调用系统的此函数
void init_hook(void) {
pthread_mutex_lock_f = dlsym(RTLD_NEXT, "pthread_mutex_lock");
pthread_mutex_unlock_f = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
}
这样定义完后,我们在调用pthread_mutex_lock函数,会执行我们自己实现的pthread_mutex_lock函数而非系统中的此函数
死锁现象demo
死锁检测实现的原理
资源获取环可以采用图来存储,使用有向图来存储。线程 A 获取线程 B 已占用的锁,则为线程 A 指向线程 B。什么是线程 B 已占用的锁?运行过程线程 B 获取成功的锁。检测的原理采用另一个线程定时对图进程检测是否有环的存在。