freeswitch初始化过程

http://blog.csdn.net/argpunk/article/details/7228593


FS 主函数main()

Freeswitch的主函数是在文件switch.c中定义的,该文件的260行是整个程序的入口,主函数主要完成的功能是包括,命令行解析,初始化apr库,构建全局内存池,模块加载和初始化核心组件。

 

初始化apr库是由apr_initialize()函数完成的,apr库是apache的可移植动态库,完成相关的内存池,线程管理的跨平台工作。该函数的调用在主函数的659行。

 

745行的switch_core_set_globals()主要是完成全局目录的设置。不过,在switch_core_init()中再一次调用了该函数。

 

747行的pid= getpid()获取程序的进程号。

 

754行利用apr_pool_create()创建一个匿名的内存池,由主函数中定义的switch_memory_pool_t*pool局部指针指向,但是可以知道,该内存池将作为程序的整个运行周期所使用。

 

本分析最关键的一点出现在784行,该行调用了switch_core_init_and_modload()函数,该函数完成了核心组件的初始化以及各个模块的动态加载。最终,形成了一个统一的系统。

 

switch_core_init_and_modload()

函数定义在switch_core.c文件中,第1526行。函数原型如下:

SWITCH_DECLARE(switch_status_t)

switch_core_init_and_modload(switch_core_flag_tflags, switch_bool_t console, const char **err)

其中,SWITCH_DECLARE(type)宏在windows下展开为

#define SWITCH_DECLARE(type) __declspec(dllexport) type __stdcall

主要用于将函数声明为dll的导出符号,这样,在其他模块中,便可以使用该函数了。而在其他系统平台上,该宏是一个空宏,例如在linux下,共享库的符号是全局的,不需要声明为导出符号。一般来说,freeswitch其他的动态加载模块所定义的函数不需要用该宏声明,在windows平台下,各个模块之间是隔离的,而核心模块中定义的函数大部分使用了该宏声明,因为其他模块需要大量使用核心模块中的核心函数,这里所指的核心模块是FreeSwitchCoreLib共享对象。

 

于是可以知道,switch_core_init_and_modload()函数可以在其他依赖于核心模块的动态加载模块中使用,这里主函数所在的模块是FreeSwitchConsole,依赖于核心模块,于是,便可以使用该函数来完成模块加载。

 

switch_core_init()

在该函数中调用了switch_core_init()函数,用来初始化一些全局化的信息,包括一个全局的switch_runtime结构,各种全局的哈希表,互斥变量。一条一条地分心如下:

①  全局的switch_runtime结构runtime部分字段的初始化——

代码段如下:

     if(runtime.runlevel > 0) {

         /* one percustomer */

         returnSWITCH_STATUS_SUCCESS;

     }

 

     runtime.runlevel++;//从这里可见,runlevel大于0是一个服务器已启动的标志,所以不必在进行

     //以下的初始化操作,直接返回SWITCH_STATUS_SUCCESS即可。

     runtime.dummy_cng_frame.data =runtime.dummy_data;

     runtime.dummy_cng_frame.datalen = sizeof(runtime.dummy_data);

     runtime.dummy_cng_frame.buflen = sizeof(runtime.dummy_data);

     switch_set_flag((&runtime.dummy_cng_frame),SFF_CNG);

 

     switch_set_flag((&runtime),SCF_NO_NEW_SESSIONS);

     runtime.hard_log_level = SWITCH_LOG_DEBUG;

     runtime.mailer_app = "sendmail";

     runtime.mailer_app_args = "-t";

     runtime.max_dtmf_duration =SWITCH_MAX_DTMF_DURATION;

     runtime.default_dtmf_duration =SWITCH_DEFAULT_DTMF_DURATION;

runtime.min_dtmf_duration= SWITCH_MIN_DTMF_DURATION;

 

接下来又重新初始化了一遍apr库,很奇怪,不知道是不是一个多余的步骤。^_^

     /* INIT APR andCreate the pool context */

     if(apr_initialize() != SWITCH_STATUS_SUCCESS) {

         *err = "FATALERROR! Could not initialize APR\n";

         returnSWITCH_STATUS_MEMERR;

     }

     if(!(runtime.memory_pool = switch_core_memory_init())) {

         *err = "FATALERROR! Could not allocate memory pool\n";

         returnSWITCH_STATUS_MEMERR;

}//从这里可以看见,全局的runtime是有一个内存池来管理它所需要的其他资源的。

②  安装时的目录信息的相关设置,与runtime结构挂钩,代码如下

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.base_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//主目录,即工程所在目录,一般为./bin,./表示安装路径

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.mod_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//模块所在目录,一般为安装目录./mod,./表示安装路径

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.conf_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//配置文件所在目录,一般为./conf

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.log_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//日志所在的目录。一般为./log

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.run_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//进程文件所在目录,一般为./run,进程文件为freeswitch.pid

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.db_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//数据库文件所在目录,一般为./db

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.script_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//脚本文件所在目录,一般为./script,存放系统需要执行的脚本文件,

//比较常用的由javascript脚本和lua脚本。

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.htdocs_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.grammar_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.recordings_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//录音文件所在目录

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.sounds_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//声音文件所在目录。

switch_dir_make_recursive(SWITCH_GLOBAL_dirs.temp_dir,SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);//临时目录。

③     全局的互斥变量和哈希表初始化,代码片段如下:

     switch_mutex_init(&runtime.uuid_mutex,SWITCH_MUTEX_NESTED, runtime.memory_pool);

     switch_mutex_init(&runtime.throttle_mutex,SWITCH_MUTEX_NESTED, runtime.memory_pool);

    switch_mutex_init(&runtime.session_hash_mutex,SWITCH_MUTEX_NESTED, runtime.memory_pool);

     switch_mutex_init(&runtime.global_mutex,SWITCH_MUTEX_NESTED, runtime.memory_pool);

switch_mutex_init(&runtime.global_var_mutex,SWITCH_MUTEX_NESTED, runtime.memory_pool);

以及

    switch_core_hash_init(&runtime.global_vars,runtime.memory_pool);

     switch_core_hash_init(&runtime.mime_types,runtime.memory_pool);

 

④     系统相关的很重要的初始化

1.       switch_core_set_globals()

由于在主函数中已经设置好了各个安装目录,所以此次调用将不做任何实际意义的工作。

2.       switch_core_session_init(runtime.memory_pool)

3.       load_mime_types()

4.       gethostname(hostname, sizeof(hostname))获取主机名

5.       switch_find_local_ip(guess_ip,sizeof(guess_ip), &mask, AF_INET)获取主机的ip地址。这里主要是获取ipv4的地址,下面还要重新调用一次该函数获取ipv6的地址。

6.        switch_console_init(runtime.memory_pool)初始化控制台。函数的实际代码如下:

a)   SWITCH_DECLARE(switch_status_t)switch_console_init(switch_memory_pool_t *pool)

b)   {

c)    switch_mutex_init(&globals.func_mutex,SWITCH_MUTEX_NESTED, pool);

d)    switch_core_hash_init(&globals.func_hash,pool);

e)    switch_console_add_complete_func("::console::list_uuid",(switch_console_complete_callback_t) switch_console_list_uuid);

f)    returnSWITCH_STATUS_SUCCESS;

g)        }

7.       switch_event_init(runtime.memory_pool)初始化freeswitch整个系统的事件机制,这个初始化很重要,在函数内部除了初始化一些互斥量,哈希队列,还创建了三个用于事件循环的队列,然后启动三个线程,分别代表了三个队列的时间循环处理线程。而所有的资源,都有runtime.memory_pool进行管理,event事件的循环处理见后续分析。

8.       switch_xml_init(runtime.memory_pool,err)进行xml配置文件相关的初始化。

9.       switch_log_init(runtime.memory_pool,runtime.colorize_console)日志系统的初始化。

10.    switch_load_core_config("switch.conf")读取全局的配置文件,然后根据该配置文件中的指令,依次读取后续的子目录下面的各个配置文件,详见后续分析。

11.    switch_core_state_machine_init(runtime.memory_pool)state_machine是整个FS系统的核心部位了,即通话状态机,根据各个channel的状态执行相应的状态处理函数,见后续分析。此处的函数为空函数。

12.    switch_core_sqldb_start()sql数据库的相关初始化。

13.    switch_rtp_init(runtime.memory_pool)rtp协议的初始化。函数内调用srtp_init()初始化rtp协议栈,freeswitch所用的rtp库是libsrtp。

14.    switch_scheduler_add_task(switch_epoch_time_now(NULL),heartbeat_callback, "heartbeat", "core", 0, NULL, SSHF_NONE |SSHF_NO_DEL)

在freeswitch中有一个task调度机制,这里讲heartbeat加入到task队列中。事件由

switch_scheduler_task_container_t结构描述,在switch_scheduler.c中,通过全局的

static struct {

switch_scheduler_task_container_t*task_list;

switch_mutex_t*task_mutex;

uint32_t task_id;

int task_thread_running;

switch_memory_pool_t *memory_pool;

} globals;

 

Globals变量对task队列进行管理。Task的调度的线程也是在switch_core_init()中启动的,具体的启动函数时switch_scheduler_task_thread_start().该函数内部生成的线程主函数为switch_scheduler_task_thread():函数里有主循环;

        while (globals.task_thread_running == 1) {

         if(task_thread_loop(0)) {

              break;

         }

         switch_yield(500000);

}通过层层剥离,会进入task_thread_loop中一次执行挂接在switch_scheduler.c中得全局globals的task队列。

 

 

switch_loadable_module_init()

在switch_core_init_and_modload()中还调用了switch_loadable_module_init(),这里就是根据目录信息加载各个动态模块的地方了。函数定义在switch_loadable_module.c文件中,属于核心组件的一部分。

 

函数内根据平台做了相关处理,在win32平台下,还需要通过函数switch_loadable_module_path_init()获取环境变量的相关信息。另外需要注意的是,该函数内部重新重新生成了一个memory_pool不再是上面描述的runtime的memory_pool了。代码如下:

switch_core_new_memory_pool(&loadable_modules.pool);

其中loadable_modules是一个文件作用域范围的全局量,

static structswitch_loadable_module_container loadable_modules;类型为

switch_loadable_module_container,定义如下:

//************* switch_loadable_module_container的定义*****************************//

structswitch_loadable_module_container {

switch_hash_t *module_hash;//存放各个模块结构的哈希表指针

switch_hash_t *endpoint_hash;// 存放各个endpoint_interface的哈希表指针

switch_hash_t *codec_hash; // 存放各个codec_interface的哈希表指针

switch_hash_t *dialplan_hash; // 存放各个diaplan_interface的哈希表指针

switch_hash_t *timer_hash;// // 存放各个计时器的哈希表指针

switch_hash_t *application_hash;//存放各个application_interface的哈希表指针

switch_hash_t *api_hash; // 存放各个api_interface的哈希表指针

switch_hash_t *file_hash;

switch_hash_t *speech_hash;

switch_hash_t *asr_hash;

switch_hash_t *directory_hash;

switch_hash_t *chat_hash;

switch_hash_t *say_hash;

switch_hash_t *management_hash;

switch_mutex_t *mutex;//全局互斥量

switch_memory_pool_t *pool;//用于模块相关的apr内存池

};

该结构包含了若该的哈希表指针,分别指向存放各个接口结构的哈希表。

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

 

接下来函数初始化了用于存放各个接口的哈希表,以及全局互斥量。

 

该函数是通过switch_loadable_module_load_module_ex((char *) SWITCH_GLOBAL_dirs.mod_dir, (char *) val, SWITCH_FALSE, global, &err)函数加载模块的。可见这里使用到了模块的目录信息SWITCH_GLOBAL_dirs.mod_dir。

 

 

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)

这里讲该函数顶定义成了一个static类型,只能在本文件中被调用。dir是上面个传下来的目录信息,fname是读取配置文件得到的需要加载的动态对象名(例如mod_conference.dll,mod_sofia.dll或mod_conference.so,mod_sofia.so等)

 

在该函数中,通过以下两个函数完成动态对象的加载:

1.  switch_loadable_module_load_file(path,file, global, &new_module),这里我是用了调用时的实参,globals并非上面提出的全局管理结构,而是一个SWITCH_STATUS的枚举对象。Path是加上了路径的完整文件名,而file仍然是配置文件中取得的名称。New_module是一个秒速模块的结构对象,具体的类型为

a)   struct switch_loadable_module {

b)        char *key;

c)        char*filename;

d)        int perm;

e)        switch_loadable_module_interface_t*module_interface;

f)        switch_dso_lib_t lib;

g)        switch_module_load_t switch_module_load;

h)        switch_module_runtime_tswitch_module_runtime;

i)        switch_module_shutdown_tswitch_module_shutdown;

j)        switch_memory_pool_t *pool;

k)        switch_status_t status;

l)        switch_thread_t *thread;

m)        switch_bool_t shutting_down;

n)        calltime_t *time_record;

o)   };

在switch_loadable_module_load_module_ex函数的开始出定义 

switch_loadable_module_t*new_module = NULL;

在switch_loadable_module_load_file函数中,会为每一个模块生成一个资源池

switch_core_new_memory_pool(&pool);

2.  switch_loadable_module_process(file,new_module)函数主要是将new_module以及module中定义的各个接口结构加入全局哈希表,在插入哈希表的过程中,由loadable_modules.mutex进行临界保护,举例如下:

①   switch_core_hash_insert(loadable_modules.module_hash, key,new_module);//将new_module

//插入loadable_modules.module_hash指向的哈希表。

②   if (new_module->module_interface->endpoint_interface){

constswitch_endpoint_interface_t *ptr;

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

switch_core_hash_insert(loadable_modules.endpoint_hash,ptr->interface_name, (const void *) ptr);

}//end if

//若new_module的module_interface中包含endpoint_interface,则将该endpoint_interface插入全局的endpoint_interface哈希表。

至此,模块加载也结束了。各个模块加载后各自进入自己的主线程中循环处理。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Freeswitch是一种开放源代码的语音和通信软件,提供语音、视频和多媒体通信能力。为了方便用户操作和管理,开发者为Freeswitch开发了简单图形化界面。 Freeswitch图形化界面基于Web技术实现,并采用Bootstrap框架和React.js技术。它具有简洁、易用的界面和友好的用户体验。用户只需在浏览器中输入相关的IP地址和端口号即可进入图形化界面。在界面中,用户可以登录、配置路由、管理通话、监控系统状态等操作。 图形化界面默认包含一些基本配置,例如:用户、呼入路由、呼出路由,用户可以根据自己的需求进行修改或者新增配置。用户也可以在界面中管理通话,例如:发起呼叫、挂断电话、进行转移等等。此外,Freeswitch图形化界面还提供系统日志、性能监控等功能,方便用户进行系统维护和管理。 总体来看,Freeswitch 1.10.7图形化界面为用户提供了方便、高效的使用体验,并提高了系统管理的可视化程度,这对于语音和通信行业的从业者及其管理者来说具有很大的意义。 ### 回答2: FreeSWITCH是一款强大的开源电话系统,其中1.10.7版本是最新的稳定版本。它支持Voice over IP(VoIP)和其他通信技术,如电话、传真和视频。同时,这款软件也支持多种协议,例如SIP、H.323和WebRTC,可以在 Linux、Windows 和 macOS 等不同平台上运行。 不过,对于一些初学者来说,命令行界面较为复杂,很难驾驭,因此出现了一些可以在图形界面下进行FreeSWITCH的配置和管理的工具。其中一些工具功能繁多,但使用起来相对复杂,很难快速掌握。而在这些工具中,Sangoma的FreePBX可能是比较著名的一个,但它需要安装在 FreeSWITCH 系统之外。 这里介绍一个能够在 FreeSWITCH 系统内完成图形化界面配置的工具,它是由FreeSWITCH的社区成员开发的。这个工具名为FreeSWITCH Desktop GUI (FSgui),目前已经实现了基础的系统配置以及呼叫中心方面的功能。 关于FSgui的使用,它提供了可视化的配置界面,在功能区中可以快捷地调整相关配置。例如,在呼叫中心方面,可以通过可视化配置,添加、编辑和删除IVR,管理呼叫、管理坐席分机等功能,缩短了用户配置的时间,从而提高效率。此外,其还支持SIP Trunks的管理,可以方便地配置多个SIP Trunks,以便于用户同时使用多种语音服务提供商。 总之,FreeSWITCH Desktop GUI 是一款简单易用的图形化工具,它为用户提供了快捷的配置方式,能够帮助使用者快速完成系统的配置与调整。同时,它是开源软件,可以在 GitHub 上进行下载。 ### 回答3: freeswitch是一款功能强大的开源电话交换机软件,但对于一些非技术人员来说,其操作和配置可能会比较复杂。为了能够更简单、更直观地使用freeswitch,一些第三方开发者开发了一些针对freeswitch的图形化界面,使得用户只需要通过鼠标点击和填写一些基本信息就可以完成freeswitch的配置和操作。 针对freeswitch 1.10.7,开发者已经开发出了一些好用的图形化界面,比如“FusionPBX”和“FreeSWITCH-GUI”。这些界面的主要特点是简单、易用、可扩展,可用于管理用户、呼叫路由、语音信箱等等freeswitch的相关服务。 以“FusionPBX”为例,其界面设计比较简洁,主要分为左侧的菜单栏和右侧的内容展示区。通过菜单栏的操作,用户可以完成一些相应的功能,比如添加、删除用户,配置呼叫路由,设置CallCenter等等。同时,FusionPBX也提供了一些实用的功能,比如在线音频播放和录制,呼叫会议等等。 值得注意的是,图形化界面只是对freeswitch的操作和配置进行了简化和优化,并不代表可以完全替代命令行操作。因此,对于freeswitch的使用者,仍需掌握一些基本的命令行操作和配置知识,以便更好地利用图形化界面完成自己的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值