函数原型如下:
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函数完成。
函数原型是:
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的一系列初始化。
该函数很简单,判断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