发明内容
本发明为了克服上述现有技术存在的不足之处,提出一种基于petri网的死锁检测方法,以期能快速检测出由读写锁、条件变量、信号量、栅栏引发的死锁,并给出死锁信息,使得基于petri网的死锁检测更加的全面。
本发明为解决技术问题采用如下技术方案:
本发明一种基于petri网的死锁检测方法,所述petri网是由若干个库所、变迁、有向弧和令牌构成的;所述库所与变迁通过所述有向弧进行连接,根据所述有向弧方向,将所述库所分为所述变迁的输入库所和变迁的输出库所;当所述变迁的输入库所拥有令牌,则表示所述变迁被允许执行;所述死锁的类型分为读写锁、条件变量、信号量和栅栏;其特点是,根据所述死锁的类型对待检测代码按如下步骤进行死锁检测:
步骤1、构建所述待检测代码对应的待检测petri网:
步骤1.1、对所述待检测代码编写所述读写锁的配置文件、所述条件变量的配置文件、所述信号量的配置文件和所述栅栏的配置文件;所述读写锁的配置文件包括:读写锁处理函数和读写锁变量;所述条件变量的配置文件包括:条件变量处理函数、互斥锁变量和条件变量;所述信号量的配置文件包括:信号量处理函数和信号量;所述栅栏的配置文件包括:栅栏处理函数和栅栏变量;
步骤1.2、对所述读写锁的配置文件、所述条件变量的配置文件、所述信号量的配置文件和所述栅栏的配置文件分别提取所述读写锁变量、互斥锁变量、条件变量、信号量和栅栏变量;并创建与所述读写锁变量相对应的库所、与所述互斥锁变量相对应的库所,与所述条件变量相对应的库所、与所述信号量相对应的库所以及与所述栅栏变量相对应的库所;
步骤1.3、对所述读写锁变量所对应的库所分别添加读写锁变量的辅助数据结构;所述读写锁变量的辅助数据结构用于存储所述读写锁变量的状态;所述读写锁变量的状态包括:加锁类型、资源数和加锁线程列表;
对所述条件变量所对应的库所分别添加条件变量的辅助数据结构;所述条件变量的辅助数据结构用于存储所述条件变量的状态;所述条件变量的状态为:是否满足条件;
对所述信号量所对应的库所分别添加信号量的辅助数据结构;所述信号量的辅助数据结构用于存储所述信号量的状态;所述信号量的状态为:信号量的值;
对所述栅栏变量所对应的库所分别添加栅栏变量的辅助数据结构;所述栅栏变量的辅助数据结构用于存储所述栅栏变量的状态;所述栅栏变量的状态包括:等待的线程个数、等待线程列表和可执行线程列表;
步骤1.4、将所述读写锁的配置文件中的每个读写锁处理函数对应于所述petri网中的一个读写锁的变迁,并分别绘制所述读写锁处理函数的petri网模型;
将所述条件变量的配置文件中的每个条件变量处理函数对应于所述petri网中的一个条件变量的变迁,并分别绘制所述条件变量处理函数的petri网模型;
将所述信号量的配置文件中的信号量处理函数对应于所述petri网中的一个信号量的变迁,并分别绘制所述信号量处理函数的petri网模型;
将所述栅栏的配置文件中的栅栏处理函数对应于所述petri网中的一个栅栏的变迁,并分别绘制所述栅栏处理函数的petri网模型;
步骤1.5、创建待构建petri网中所述待检测代码执行的初始函数main对应的库所,利用所述待检测代码中所包含的函数调用关系的先后顺序,以及所述函数调用关系中包含的读写锁处理函数、条件变量处理函数、信号量处理函数和/或栅栏处理函数;分别将所述读写锁处理函数的petri网模型、所述条件变量处理函数的petri网模型、所述信号量处理函数的petri网模型和/或所述栅栏处理函数的petri网模型依次添加到所述待构建petri网中,从而形成待检测petri网;
步骤2、对所述待检测petri网进行死锁检测:
步骤2.1、初始化所述待检测petri网的状态并进行存储,使得所述待检测代码中的初始函数main对应的库所拥有令牌;所述读写锁变量对应的库所拥有令牌;定义一个缓存栈;
步骤2.2、模拟执行所述待检测petri网,从所述待检测代码中初始函数main对应的库所开始执行;
步骤2.3、判断所述待检测petri网中,是否存在被允许执行的变迁;若存在,则执行步骤2.4;否则执行步骤2.6;
步骤2.4、将所述被允许执行的变迁存入所述缓存栈中并标记为未执行状态;
步骤2.5、将所述缓存栈中的栈顶元素对应的变迁标记为已执行状态并执行;所述已执行变迁对应的输入库所的令牌转移到输出库所上,所述待检测petri网的状态得到更新;存储所述更新的待检测petri网的状态后返回步骤2.3执行;
步骤2.6、判断所述待检测petri网中是否存在未执行状态的变迁,若存在,则表示存在死锁,并给出所有已执行状态的变迁,检测停止;否则,执行步骤2.7;
步骤2.7、将所述缓存栈中标记已执行状态的变迁出栈,直到遇到未执行状态的变迁为止;若所述出栈过程中没有遇到未执行状态的变迁,则表示所述待检测petri网中不存在死锁,检测停止;否则将所述栈顶元素对应的变迁标记为已执行状态并执行;所述已执行的变迁对应的输入库所的令牌转移到输出库所上,所述待检测petri网的状态得到更新;存储所述更新的待检测petri网的状态后返回步骤2.3执行。
与现有技术相比,本发明的有益效果在于:
1、与现有基于petri网的死锁检测算法相比,本发明可以检测出由读写锁、条件变量、信号量、栅栏引发的死锁,并给出出现死锁时,已执行的变迁,使得基于petri网的死锁检测更加的全面。
2、本发明是基于petri网模型的扩充,通过对读写锁、条件变量、信号量、栅栏对应的库所以及读写锁处理函数、条件变量处理函数、信号量处理函数、栅栏处理函数对应的变迁添加辅助数据结构,完善地表达了读写锁、条件变量、信号量以及栅栏的特性和读写锁处理函数、条件变量处理函数、信号量处理函数以及栅栏处理函数的执行过程。
3、本发明采用的死锁检测方法中使用栈存储被允许执行的变迁,可以快速高效的回退到上一个被允许且未执行的变迁,可以确保待检测petri网中所有可能的执行路径都被检测到。
4、本发明通过编写所述读写锁、条件变量、信号量、栅栏的配置文件,不仅可以处理读写锁、条件变量、信号量、栅栏自身所带的系统函数,还可以处理对读写锁处理函数、条件变量处理函数、信号量处理函数、栅栏处理函数包装后的函数。
具体实施方式
本实施例中,petri网是由若干个库所、变迁、有向弧和令牌构成的,库所与变迁通过有向弧进行连接,根据有向弧的方向,将库所分为变迁的输入库所和变迁的输出库所;当变迁的输入库所拥有令牌,则表示变迁被允许执行;死锁类型分为读写锁、条件变量、信号量和栅栏;由于条件变量要和互斥锁相联结使用,以避免出现条件竞争,本发明中一种基于petri网的死锁检测方法也检测到由互斥锁引发的死锁情况,并根据死锁类型分别或同时对待检测代码进行死锁检测并按如下步骤进行:
步骤1、构建待检测代码对应的待检测petri网:
步骤1.1、对待检测代码编写读写锁的配置文件、条件变量的配置文件、信号量的配置文件和栅栏的配置文件;读写锁的配置文件包括:读写锁处理函数和读写锁变量;条件变量的配置文件包括:条件变量处理函数、互斥锁变量和条件变量;信号量的配置文件包括:信号量处理函数和信号量;栅栏的配置文件包括:栅栏处理函数和栅栏变量;
本实施例中,以条件变量的配置文件为例,文件内容如下:
lock_func_name:pthread_cond_wait^ustc_cond_wait$1^1^condName$2^2^mutexName$
lock_func_name:pthread_cond_signal^ustc_cond_signal$1^1^condName$
lock_var_name:pthread_mutex_t^mutex$
lock_var_name:pthread_cond_t^cond$
在条件变量的配置文件中,条件变量处理函数的说明以lock_func_name:开始标记,后面接着是条件变量处理函数名^包装后的处理函数名$关心的参数索引$,以第一行为例,pthread_cond_wait函数是条件变量的一个处理函数,包装后的函数是ustc_cond_wait函数(这个据实际情况而定),需要关心的参数有两个:条件变量和互斥锁变量,1^1^condName中的两个1分别表示条件变量在pthread_cond_wait函数和ustc_cond_wait函数中所在参数的索引位置。在实际的应用中,根据需要去修改包装函数的名字及关心的参数索引。
待检测程序中使用的条件变量、互斥锁变量以lock_var_name:开始标记,后面接着是类型^变量名,如pthread_mutex_t^mutex,以‘^’为分隔,分别表示互斥锁变量的类型和名字。
步骤1.2、对读写锁的配置文件、条件变量的配置文件、信号量的配置文件和栅栏的配置文件分别提取读写锁变量、互斥锁变量、条件变量、信号量和栅栏变量;并创建与读写锁变量相对应的库所、与互斥锁变量相对应的库所、与条件变量相对应的库所、与信号量相对应的库所以及与栅栏变量相对应的库所;
本实施例中,将提取互斥锁变量mutex和条件变量cond,并分别创建这两个库所;
步骤1.3、对读写锁变量所对应的库所分别添加读写锁变量的辅助数据结构;读写锁变量的辅助数据结构用于存储读写锁变量的状态;读写锁变量的状态包括:加锁类型、资源数和加锁线程列表;
读写锁变量对应的库所初始状态为未加锁状态,资源数为0,线程列表为空;只有资源数为0的情况下才能加写锁,加锁成功后资源数减为-1,解锁时资源数加为0;只有资源数大于等于0的情况下才能加读锁,成功后,资源数加1,解锁时资源数减1。
对条件变量所对应的库所分别添加条件变量的辅助数据结构;条件变量的辅助数据结构用于存储条件变量的状态;条件变量的状态为:是否满足条件;
条件变量附加的是否满足条件的标志,初始为0,当有线程调用pthread_cond_signal函数条件触发时,标志修改为1,其他等待挂起的线程调用pthread_cond_wait函数成功,标志重新置为0。
对信号量所对应的库所分别添加信号量的辅助数据结构;信号量的辅助数据结构用于存储信号量的状态;信号量的状态为:信号量的值;
只要信号量的值大于0,其他线程调用sem_wait函数成功,成功后信号量的值减1,若信号量的值不大于0,则调用sem_wait函数的线程阻塞,直到有线程sem_post函数释放,释放后信号量的值加1.
对栅栏变量所对应的库所分别添加栅栏变量的辅助数据结构;栅栏变量的辅助数据结构用于存储栅栏变量的状态;栅栏变量的状态包括:等待的线程个数、等待线程列表和可执行线程列表;
线程调用pthread_barrier_wait函数时,会将线程加入栅栏的等待线程列表中,若等待线程列表中的线程数等于栅栏等待的线程个数,则将等待线程列表中的线程加入可执行线程列表,可执行线程列表中的线程可以越过栅栏继续执行后面的操作。等待线程列表清空,继续等待其他线程的到来。
步骤1.4、将读写锁的配置文件中的每个读写锁处理函数对应于待检测petri网中的一个读写锁的变迁,并分别绘制读写锁处理函数的petri网模型;
读写锁处理函数有:pthread_rwlock_rdlock函数、pthread_rwlock_wrlock函数、pthread_rwlock_unlock函数、pthread_rwlock_tryrdlock函数、pthread_rwlock_trywrlock函数,线程调用rdlock或者rwlock函数成功时需要获取读写锁资源,如图2所示,rwlock为读写锁变量对应的库所,Pi为petri网中当前分析的库所,库所Pi和rwlock为变迁T的输入库所,Pj为变迁T的输出库所,变迁T对应于pthread_rwlock_rdlock函数或者pthread_rwlock_wrlock函数,若库所Pi和rwlock拥有令牌,则变迁T被允许执行,变迁执行后,库所Pj拥有令牌。线程调用tryrdlock或者tryrwlock函数获取读写锁资源时,无论是否成功,函数都会立刻返回,如图3所示,变迁tryrdlock/trywrlock的输入库所只有Pi,读写锁变量对应的库所信息存储在变迁中,当输入库所Pi获得令牌时,tryrdlock/trywrlock变迁被允许,执行变迁后,Pj获得令牌。线程调用unlock函数释放读写锁时,会修改读写锁变量对应库所的辅助信息,如图4所示,Pi获得令牌后,通过变迁unlock,使得Pj和rwlock获得令牌。
将条件变量的配置文件中的每个条件变量处理函数对应于待检测petri网中的一个条件变量的变迁,并分别绘制条件变量处理函数的petri网模型;
在posix线程库中,关于条件变量的处理主要有两个函数:pthread_cond_wait函数和pthread_cond_signal函数,线程调用pthread_cond_wait函数等待一个条件变量的成立,调用pthread_cond_signal函数触发这个条件变量。
如图5所示,pthread_cond_wait函数内部的操作有(1)unlock互斥锁mutex和(2)wait条件变量cond,两者之间存在先后顺序关系。若Pi获得令牌,通过unlock变迁,释放了互斥锁mutex,然后通过Pj进入wait变迁,当互斥锁mutex和条件变量cond对应的库所均获得令牌时,wait变迁才被允许,输出库所Pk获得令牌。条件变量cond的条件满足是通过signal变迁实现的,如图6所示,若Px获得令牌,通过signal变迁,库所Py和条件变量cond对应的库所获得令牌。
将信号量的配置文件中的信号量处理函数对应于待检测petri网中的一个信号量的变迁,并分别绘制信号量处理函数的petri网模型;
信号量处理函数有sem_wait函数和sem_post函数,若信号量的值大于0,线程调用sem_wait函数就会成功,如图7所示,若信号量sem对应的库所的辅助数据中的值大于0,sem库所获得令牌,当库所Pi和sem所均获得令牌时,变迁sem_wait被允许,执行sem_wait变迁后库所Pj获得令牌。线程调用sem_post函数时,会使信号量的值加1,如图8所示,若Pi获得令牌,变迁sem_post被允许,执行sem_post变迁后,Pj和sem库所获得令牌,同时sem库所的辅助数据信号量的值会加1.
将栅栏函数的配置文件中的栅栏处理函数对应于待检测petri网中的一个栅栏的变迁,并分别绘制栅栏处理函数的petri网模型;
栅栏处理函数有pthread_barrier_init函数和pthread_barrier_wait函数,线程通过pthread_barrier_init函数获取初始设置栅栏需要等待的线程数,线程调用pthread_barrier_wait函数等待其他线程到达,当最后一个线程到达栅栏时,所有线程可以越过栅栏继续执行后面的操作,如图9所示,在执行wait变迁之前添加了一个null变迁,构造等待线程列表和可执行线程列表,当等待线程列表中的线程等于栅栏需要等待的线程数时,将等待线程列表中的线程加入可执行线程列表,执行变迁null后,Pj获得令牌,若栅栏变量barrier所对应的辅助数据结构中可执行列表不为空,则其所对应的库所获得令牌,wait变迁被允许,执行wait变迁,Pk获得令牌。
步骤1.5、创建待构建petri网中待检测代码执行的初始函数main对应的库所,利用待检测代码中所包含的函数调用关系的先后顺序,以及函数调用关系中包含的读写锁处理函数、条件变量处理函数、信号量处理函数和/或栅栏处理函数;分别将读写锁处理函数的petri网模型、条件变量处理函数的petri网模型、信号量处理函数的petri网模型和/或栅栏处理函数的petri网模型依次添加到待构建petri网中,从而形成待检测petri网;
步骤2、如图10所示,对所述待检测petri网进行死锁检测:
步骤2.1、初始化所述待检测petri网的状态并进行存储,使得所述待检测代码中的初始函数main对应的库所拥有令牌;所述读写锁变量对应的库所拥有令牌;定义一个缓存栈;
步骤2.2、模拟执行所述待检测petri网,从所述待检测代码中初始函数main对应的库所开始执行;
步骤2.3、判断所述待检测petri网中,是否存在被允许执行的变迁;若存在,则执行步骤2.4;否则执行步骤2.6;
步骤2.4、将所述被允许执行的变迁存入所述缓存栈中并标记为未执行状态;
步骤2.5、将所述缓存栈中的栈顶元素对应的变迁标记为已执行状态并执行;所述已执行变迁对应的输入库所的令牌转移到输出库所上,所述待检测petri网的状态得到更新;存储所述更新的待检测petri网的状态后返回步骤2.3执行;
步骤2.6、判断所述待检测petri网中是否存在未执行状态的变迁,若存在,则表示存在死锁,并给出所有已执行状态的变迁,检测停止;否则,执行步骤2.7;
步骤2.7、将所述缓存栈中标记已执行状态的变迁出栈,直到遇到未执行状态的变迁为止;若所述出栈过程中没有遇到未执行状态的变迁,则表示所述待检测petri网中不存在死锁,检测停止;否则将所述栈顶元素对应的变迁标记为已执行状态并执行;所述已执行的变迁对应的输入库所的令牌转移到输出库所上,所述待检测petri网的状态得到更新;存储所述更新的待检测petri网的状态后返回步骤2.3执行。
综合使用本发明可以检测出由读写锁、条件变量、信号量、栅栏引发的死锁,并给出死锁时,已执行的变迁信息,本发明通过使用配置文件,可以有效地对读写锁、条件变量、信号量、栅栏处理函数对应的包装函数的处理,并且可以快速的获得读写锁、条件变量、信号量、栅栏处理函数操作的锁变量的名字,使得基于petri网的死锁检测更加的高效和通用。