SMV源码分析1

SMV代表Symbolic Model Verifier,是我研究领域中用到的一个小工具。SMV所基于的原理简单说来是:先把实际中的系统建模为有限状态系统,系统所要满足的性质用CTL时态逻辑表示,然后把它们作为SMV的输入,自动执行模型检测算法,得出性质在系统上是否成立,若不成立就给出反例,说明为什么不成立。

SMV是Carnegie Mellon大学开发,能方便在http://www-2.cs.cmu.edu/~modelcheck/smv.html获得源码。

SMV源码主要包含16个h文件和c文件,再加上词法规则文件和语法规则文件,共18个源码文件,9154行代码。下面命令分别统计h、c文件代码行数和h、c文件个数:

$ find . -name "*\.[hc]" -print | xargs wc -l | tail –n1
$ find . -name "*\.[hc]" -print | wc –l

SMV设计了一种语言(称为SMV语言),以方便描述有限状态系统和CTL时态逻辑,处理这种语言的词法语法分析程序,借助经典的工具Yacc和Lax生成。SMV实现所要求的时态逻辑和符合模型检测知识,当分析到相关代码时详细介绍。

 

1.main函数总体框架

1)信号处理代码:

 int i; 
/* Catch all the signals we can catch, except for segfaults.
     Core dumps are useful for debugging. 
     Also, stopping and resuming seems like a good idea. */
  for(i = 0 ; i < 32 ; i++)
    if((i != SIGSEGV) && (i != SIGCONT) && (i != SIGTSTP))
      signal(i, signal_handler);

解析:

signal的声明十分复杂:void (*signal(int sig, void (*func)(int))) (int);

下图说明如何分析这样的声明(来自《c专家编程》中文版64页)

 

signal函数的作用是设置信号处理程序,若成功则返为以前的信号处理配置,若出错则返回为SIG_ERR,它的定义一般是:#define SIG_ERR (void (*)())-1。《Unix环境高级编程》第10章有详细介绍。

2)存储管理初始代码:

init_storage();
addrstart = (char *) sbrk(0);

解析:

init_storage()的实现代码是:addrfree = addrlimit = (char *) sbrk(0); addrfree和addrlimit作用域是static。

sbrk(n)的作用,man页上有:

sbrk increments the program's data  space  by  increment  bytes. sbrk isn't a system call, it is just a C library wrapper.  Calling sbrk with an increment of 0 can be used to find the current location of the program break(如何翻译)。

3)命令行参数处理代码,略

4)其它初始代码:

  init_string();
  init_assoc();
  init_node();
  init_bdd();
  init_eval();

解析:

在smv中,字符串(string)、关联表(assoc)、结点(node)、bdd等数据结构的内存管理和快速查找(若需要的话),采用统一的模式。详见下面的存储管理和hash表实现。

5)调用yyparse(),进行词法语法分析。

6)调用符号模型检测实现函数:build_symbols()。

7)退出:my_exit(0);

 
2.通用存储管理(storage.c、storage.h)

1)SMV没有使用标准C库中的malloc/free,而是使用自己实现的函数(取名为malloc和free)进行内存分配和释放。

smv的malloc/free的实现:

static char *addrlimit;
static char *addrfree;

char *malloc(int n)
{
  if(n % 4)n=n+4-(n%4);  /* always allocate multiple of four bytes */
  while(addrfree + n > addrlimit)getmore();
  {
    char *r = addrfree;
    addrfree += n;
    return(r);
  }
} 

/* very simple implementation of free */
void free(char *p)
{
  return;
}
/* get ALLOCSIZE(2^16 = 2 << 15) more bytes from the O.S. */
void getmore()
{
  char *na;
  if(addrlimit != (char *)sbrk(0)){ /* in case someone else did sbrk */
    sbrk((4 - (sbrk(0) % 4)) % 4);
    addrfree = addrlimit = (char *)sbrk(0);
    if(((unsigned)addrlimit) % 4 != 0)
      rpterr("Failed to allocate %d bytes: addrlimit = %xH, na = %xH/n",
         ALLOCSIZE,(int)addrlimit,(int)na);
  }
  if((na = (char *)sbrk(ALLOCSIZE)) != addrlimit)
    rpterr("Failed to allocate %d bytes: addrlimit = %xH, na = %xH/n",
       ALLOCSIZE,(int)addrlimit,(int)na);
  addrlimit += ALLOCSIZE;
}
2)各种数据结构一般化为rec(记录),然后使用内存池管理rec。提供的接口:
typedef struct rec {
  struct rec *link;
} rec_rec, *rec_ptr; 

typedef struct mgr{ //管理内存池的结构
    rec_rec free; //指向空内存池(链表实现)头,初始=0
    int  rec_size; 
    int count;
    void (*free_hook)();
} mgr_rec, *mgr_ptr;

mgr_ptr new_mgr(int rec_size); //分配并初始化mgr结构,管理内存池
rec_ptr new_rec(mgr_ptr mp); //从内存池中,分配一个rec。
rec_ptr dup_rec(mgr_ptr mp, rec_ptr r);//复制一个rec
void free_rec(mgr_ptr mp, rec_ptr r); //释放一个rec,放入内存池

例如,先mp = new_mgr(sizeof(struct string))进行初始化,然后就能new_rec(mp)从内存池中分配一个string数据结构(若内存池没空间,就getmore得到新的内存池)。体会new_rec()等接口的通用性。图示:

 

3.通用hash表实现(hash.c、hash.h)

要快速找到某个rec,使用hash表是个相当好的方法。主要接口:

hash_ptr new_hash(size, hash_func, eq_func, mgr);
rec_ptr insert_hash(hash_ptr hash, rec_ptr r);
rec_ptr find_hash(hash_ptr hash, rec_ptr r);
图示:
 
4.通用存储管理和hash表的实例化

举例说明,使用string数据结构(string.c、string.h):

typedef struct string{
  //struct assoc是否先定义没有影响,我认为这里用struct string *更好理解
  struct assoc *link;  //作用是链式方法解决hash冲突
  char *text; 
} string_rec,*string_ptr;
初始化:
void init_string()
{
  string_mgr = new_mgr(sizeof(struct string));
  string_hash = new_hash(STRING_HASH_SIZE,string_hash_fun,
             string_eq_fun,string_mgr);
}
//借助hash表插入、查找
string_ptr find_string(char *x)
{
  string_rec a,*res;
  a.text = x;
  if(res = (string_ptr)find_hash(string_hash,&a))return(res);
  a.text = (char *)strcpy((char *)malloc(strlen(x)+1),x);
  return((string_ptr)insert_hash(string_hash,&a));//会调用dup_rec
}
 

你可能感兴趣的:(SMV源码分析1)