FreeSwitch 模块加载过程

switch_loadable_module_load_module_ex

函数原型如下:

static switch_status_t

switch_loadable_module_load_module_ex(char *dir, char*fname, switch_bool_t runtime, switch_bool_t global, constchar **err)

传入这个函数的形参中,dir是路径名,fname是从配置文件中获取的共享库的名称。

在switch_loadable_module_init()中调用该函数,在该函数中,主要完成以下工作:

1.       这个函数下得操作是基于全局量loadable_modules的mutex保护下的。对一个 模块进行必要地操作时,包括将共享库载入进程的地址空间和完成共享库模块提供的application接口,codec接口,api接口,endpoint接口的哈希入链操作等,都是在这一层次上进行互斥保护的。//注:我有一点尚未明白,在函数中使用了switch_mutex_lock(loadable_modules.mutex)进行互斥加锁后,在函数中调用switch_loadable_module_process函数时又一次进行了加锁操作,按理来说,这应该是死锁了,但程序却依旧工作正常,可能是得益于apr库对mutex的实现机制(switch_mutex_lock函数只不过是apr相应的加锁函数的封装函数),但是在switch_loadable_module_process中加锁却又是必要的,因为在多线程的程序里,freeswitch允许了动态地加载卸载模块,而switch_loadable_module_process必然涉及到加载卸载操作过程中对于全局量的访问操作,因为需要将模块提供的各种api或其他接口,甚至于模块结构(模块的抽象)链入全局的哈希表队列中,而这些哈希表正是全局的,在程序中,也正好是由loadable_modules(switch_loadable_module_container类型)提供访问方法的。

2.       在freeswitch中,模块的加载分为两个部分进行,一部分就是共享库加载到进程的地址空间,这部分工作由switch_loadable_module_load_file函数完成,另一个工作是共享库提供的各种操作接口,必须加入到全局的哈希表中,由switch_loadable_module_process函数完成。

switch_loadable_module_load_file

函数原型是:

static switch_status_t switch_loadable_module_load_file(char *path, char*filename, switch_bool_t global, switch_loadable_module_t **new_module)

其中path是由上层函数,也就是switch_loadable_module_load_module_ex函数传入的共享库的路径,需要注意的是,该路径所享有的空间是由loadable_modules.pool资源此所提供的,也就是说由模块加载全局对象来进行管理,该字符串资源在整个程序生存期间是得不到释放的,只有在程序结束后,统一释放loadable_modules的资源时,才会统一得到释放,而对于某一具体的模块来说,却会给予相对独立的资源池,具体的分配,也是在switch_loadable_module_load_file中分配的,并于模块描述结构(switch_loadable_module_t)挂钩,以后,模块内部使用的资源将从该内存池中分配,当然,也包括模块描述结构本身。

New_module是一个指向模块描述结构的指针的指针,当在switch_loadable_module_load_file分配好模块描述结构,完成该结构的初始化,调用了模块加载函数之后,由该变量传回上层,并传给switch_loadable_module_process做加入全局哈希表的相应处理。

 

函数的局部自动变量分析函数功能和架构

通过分析函数内部在栈中分配的自动变量,能从另一个侧面很好地看出函数所做的事情

a.  switch_loadable_module_t*module = NULL;//很明显,这是一个模块描述结构,将在该函数中完成相应的模块初始化操作。其类型的定义如下:

struct switch_loadable_module {

  char *key;

  char*filename;

  int perm;

switch_loadable_module_interface_t*module_interface;

  switch_dso_lib_t lib;

  switch_module_load_t switch_module_load;

  switch_module_runtime_tswitch_module_runtime;

  switch_module_shutdown_tswitch_module_shutdown;

  switch_memory_pool_t *pool;

  switch_status_t status;

  switch_thread_t *thread;

  switch_bool_t shutting_down;

  calltime_t *time_record;

};

比较关键的一个字段是module_interface,这里指向的是模块提供的所有接口。

b.switch_dso_lib_t dso = NULL;//这实际上是一个共享库的句柄,在win32平台下它是一个HINSTANCE类型,在linux平台下它是一个void *指针,代表着共享库被加载到进程的地址空间后,开始的虚拟地址。Dso的含义是dynamic share object。

c. switch_loadable_module_function_table_t*interface_struct_handle = NULL;//该指针变量的在模块的加载过程显得很重要,一个共享库如果要加载到进程的地址空间,与程序本身进行交互,除了提供功能性的函数外,还需要遵循程序本身定义的给予模块的标准接口。对于freeswitch来说,它给予模块的第一个标准接口便是一个switch_loadable_module_function_table_t类型的结构对象(注意这里所说的是第一个),而当共享库加载到程序的地址空间后,freeswitch所做的第一件事就是找到共享库中的提供的switch_loadable_module_function_table_t对象所在的地址。而所谓的function_table,仅从字面意义来看便可知,该对象提供了一些列的函数结构,也就是说,这个对象实际是一个函数指针的组合,其定义如下:

typedef struct switch_loadable_module_function_table {

               int switch_api_version;

               switch_module_load_t load;

               switch_module_shutdown_t shutdown;

               switch_module_runtime_t runtime;

               switch_module_flag_t flags;

}switch_loadable_module_function_table_t;

从定义便一目了然了,freeswitch通过它指定给模块三件需要完成的任务:一,提供该对象地址,便于核心模块在加载动态模块时,能够通过该对象完成必要地操作。二,该对象应该提供模块在加载时调用的函数,具体体现就在于结构中的switch_module_load_t,这是一个函数指针。三,该对象务必提供其余两个函数shutdown和runtime,用于在模块卸载时完成必要的清理工作,以及在运行态时提供的必要操作。当然,runtime并不是必须提供的。

从上可以看出,freeswitch便是这样与模块进行交互的。

d. char *struct_name = NULL;//这个字符串,便是switch_loadable_module_function_table_t对象在共享库中的名字了,它遵循一定的标准,即模块名+module_interface,例如mod_conference_module_interface。Freeswitch正是通过改名字对共享库进行符号查找,找到function_table对象的虚拟地址的。

e. switch_module_load_tload_func_ptr = NULL;//这就很好理解了,它便是由function_table所提供的模块加载函数的指针,最终从模块中获取switch_loadable_module_function_table_t对象后,将对象里的load赋予该自动变量,然后通过load_func_ptr调用模块加载函数。

f. switch_loadable_module_interface_t*module_interface = NULL;//该自动变量描述了一个模块所提供的所有接口,对module_interface的使用如下:

load_func_ptr(&module_interface,pool);//函数指针,具体函数如mod_sofia_load.

以双针的形势将module_interface和为模块分配的资源池传入模块的加载函数,pool是一个switch_core_memory_t 类型的指针,也是在switch_loadable_module_load_file中为具体加载模块分配的资源池,在函数加载函数中(例如mod_sofia_load),将会从资源池为该module_interface分配内存,如mod_sofia_load中:

*module_interface =switch_loadable_module_create_module_interface(pool, modname);

这里的module_interface已经是传递下来的双指针,而pool便是传递下来的memory_pool的指针。

接下来,会生成各个功能性接口。这里比较有意思的是,对象化的编程思想,所有接口都被抽象成了对象的概念。而且也可以看出,一旦与具体模块相关后,所有的东西都是用模块自身的资源池进行管理了。

switch_loadable_module_interface_t定义如下:

//**********************************************************************************//

structswitch_loadable_module_interface {

   const char*module_name;

   switch_endpoint_interface_t*endpoint_interface;

   switch_timer_interface_t*timer_interface;

   switch_dialplan_interface_t*dialplan_interface;

   switch_codec_interface_t*codec_interface;

   switch_application_interface_t*application_interface;

   switch_api_interface_t*api_interface;

   switch_file_interface_t*file_interface;

   switch_speech_interface_t*speech_interface;

   switch_directory_interface_t*directory_interface;

   switch_chat_interface_t*chat_interface;

   switch_say_interface_t*say_interface;

   switch_asr_interface_t*asr_interface;

   switch_management_interface_t*management_interface;

   //以上是各个接口对象的指针

   switch_thread_rwlock_t*rwlock;

   int refs;

   switch_memory_pool_t*pool;//模块所使用的资源池,功模块范围内使用。以上的各个接口对象均从该资源池分配内存

};

    //**********************************************************************************//

 

在分析完自动变量的各种使用后,函数流程便很简单了:

1.  为模块分配资源池

2.  读取共享库到进程的地址空间

3.  根据struct_name查找共享库提供的function_table对象。

4.  调用funtion_table提供的load函数,分配module_interface接口和各种功能性接口对象。完成module_interface的初始化,以及将各种api,application,codec,endpoint函数添加到相应的接口对象字段。

5.  分配模块描述结构switch_loadable_module_t对象,并用module指针指向,完成module的一系列初始化。

 

switch_loadable_module_process

该函数很简单,判断module->module_interface中得各个接口对象是否为空,不为空就将相应的接口对象加入全局哈希表。

 

看下面的代码片段便可知;

if (new_module->module_interface->endpoint_interface) {

constswitch_endpoint_interface_t *ptr;

for (ptr =new_module->module_interface->endpoint_interface; ptr; ptr =ptr->next) {

if(!ptr->interface_name) {

      //打印出错信息

      ……

      } else {

…..//打印接口名字

      switch_core_hash_insert(loadable_modules.endpoint_hash,ptr->interface_name, (const void *) ptr)//将接口对象插入哈希表;

      if (switch_event_create(&event,SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {

         ……//生成SWITCH_EVENT_MODULE_LOAD的event事件。

      }

}//else

}//for

}//if

 

你可能感兴趣的:(function,Module,table,application,interface,Codec)