Asterisk内核框架

 

标题:Asterisk内核框架(转帖)
2009-07-15 13:37:23

Asterisk内核框架(转帖)

 

Asterisk是一个开源的pbx系统,在公开的资料中,很难找到asterisk内核系统的详细描述。因此,很有必要写一篇内核框架的描述文档,作为内部培训文档,相互学习提高。

本文主要从三个层面来描述asterisk内核,即asterisk内核模块、内核启动过程、基本呼叫流程。

一、             asterisk内核模块

Asterisk由内部核心和外围动态可加载模块组成。内部核心由以下六个部分组成:PBX交换核心模块(PBX Switching Core)、调度和I/O管理模块(Scheduler and I/O Manager)、应用调用模块(Application Launcher)、编解码转换模块(Codec Translator)、动态模块加载器模块(Dynamic Module Loader)和CDR生成模块(CDR Core)。

外围动态可加载模块包括以App_开始的Applications、以Func_开始的Functions、以Res_开始的Resources、以Chan_开始的channels、以Codec_开始的codec编解码模块等。

1.         内核模块

1)        PBX交换核心模块(PBX Switching Core):

l         pbx.c

pbx.c是asterisk的核心模块,每路呼叫都需要经过它调度。pbx实现了builtin applications,也就是内置的应用,比如最常见的Answer,Hangup, Background,Wait等等。

struct ast_app是一个关键数据结构,它定义了注册builtin applications的结构。

load_pbx函数用来注册builtin applications和一些命令行CLI命令(每个模块都有些CLI命令)。该函数在系统启动时被调用。

pbx_exec是Answer/BackGround/Busy/Goto/GotoIf/Hangup/Set等builtin applications的执行入口函数,它被pbx_extension_helper调用。

ast_pbx_start函数是每路呼叫的起点。

2)        调度和I/O管理模块(Scheduler and I/O Manager):

l         Channel.c:

Channel.c/channel.h定义了channel操作的结构体和接口函数。

struct ast_channel_tech结构体是所有channel都要用到的关键结构体,它定义channel操作的一系列回调函数指针,如call、hangup、answer等。每个channel模块都会定义ast_channel_tech的实体,并将各自的回调函数赋值给它。例如chan_sip.c中定义如下:

/*! /brief Definition of this channel for PBX channel registration */

static const struct ast_channel_tech sip_tech = {

       .type = "SIP",

       .description = "Session Initiation Protocol (SIP)",

       .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),

       .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,

       .requester = sip_request_call,

       .devicestate = sip_devicestate,

       .call = sip_call,

       .hangup = sip_hangup,

       .answer = sip_answer,

       .read = sip_read,

       .write = sip_write,

       .write_video = sip_write,

       .indicate = sip_indicate,

       .transfer = sip_transfer,

       .fixup = sip_fixup,

       .send_digit_begin = sip_senddigit_begin,

       .send_digit_end = sip_senddigit_end,

       .bridge = ast_rtp_bridge,

       .send_text = sip_sendtext,

       .func_channel_read = acf_channel_read,

};

ast_call、ast_hangup、ast_answer等函数分别实现ast_channel_tech中的call、hangup、answer等回调函数的调用。

struct ast_channel结构体定义了channel的上下文参数,它是每个参与呼叫的channel必不可少的,都会调用ast_channel_alloc来申请ast_channel。

l         io.c

io.c实现了asterisk跟外部交互时的I/O管理,如chan_sip为了从外部接收SIP信令,调用ast_io_add添加IO接口,并调用ast_io_wait实现外部消息接收。

3)        应用调用模块(Application Launcher):

在pbx.c中定义了一系列的应用调用接口。

applications模块定义了application回调函数并注册后,在pbx.c中通过应用调用接口回调执行。

应用调用接口的关键函数是pbx_extension_helper,它执行dialplan,在cli上打印“Executing ……”,并抛出ami event事件,同时调用pbx_exec执行application回调函数。

4)        编解码转换模块(Codec Translator):

Translate.c:

struct ast_translator:编码转换描述结构体,它定义了编码转换的名称、回调函数、运行时选项。

struct ast_trans_pvt:编码转换上下文描述结构体。

ast_register_translator:编码转换注册接口函数,供各编码模块调用,注册struct ast_translator类型的结构体变量。

ast_unregister_translator:编码转换注销函数

ast_translate:编码转换的执行函数。

codec_gsm.c/codec_...:对应各种编码的编解码执行模块,如g.711alaw/g.711ulaw/gsm等。

5)        动态模块加载器模块(Dynamic Module Loader):

该模块主要是Module.h。

Module.h中定义了struct ast_module_info结构,用来保存各模块的注册、注销回调函数,以及模块描述信息。

load_module、unload_module,每个应用模块的注册、注销函数,由各个模块自行定义为static函数。

AST_MODULE_INFO_STANDARD:注册接口、注销接口、模块描述信息等模块信息的登记接口。它是一个宏定义,动态模块调用它时,首先定义类型为ast_module_info的__mod_info静态结构变量,保存模块信息,并定义__attribute__ ((constructor)) __reg_module和__attribute__ ((destructor)) __unreg_module,在程序启动和退出时调用。

6)        CDR生成模块(CDR Core):

Cdr.c:

ast_cdr_register:cdr driver注册,供cdr_mysql等调用,注册话单保存的回调函数。

ast_cdr_engine_init:CDR模块初始化,注册cdr status、加载cdr.conf、启动CDR线程。

ast_cdr_detach:产生话单的接口函数,呼叫结束时被调用。

2.         外围可加载模块:

1)        Applications

以app_开始的模块,如app_dial.c、app_db.c、app_queue.c、app_record.c、app_meetme.c等,代码保存在apps目录中。每个application模块都定义了load_module函数和unload_module函数,分别用来注册和注销application。

load_module函数调用ast_register_application函数,注册application命令,例如app_dial模块注册Dial:res = ast_register_application(app, dial_exec, synopsis, descrip)。

unload_module函数调用ast_unregister_application函数,注销application命令。

每个application模块都会使用AST_MODULE_INFO_STANDARD宏来登记模块信息__mod_info。AST_MODULE_INFO_STANDARD将load_module和unload_module注册为回调函数,供module load/unload/reload调用。

2)        Channel

以chan_开始的模块,如chan_sip.c、chan_h323.c、chan_mgcp.c 、chan_iax2.c、 chan_zap.c等,对应代码保存在channels目录中。

channel注册、注销过程和application基本类似。由于每个channel需要和外部交互,都会在load_module中启用do_monitor线程来侦听外部tcp/udp端口,接收外部消息。

每个channel也定义了各自的cli命令和Function命令,例如chan_sip定义了sip debug/history/no/notify/prune/ reload/set/show等cli命令和SIP_HEADER、CHECKSIPDOMAIN、SIPPEER、SIPCHANINFO等Function命令。

3)        Functions

以Fun_开始的模块,例如Fun_db.c、func_moh.c、func_cdr.c等,对应代码保存在funcs目录中。

Function注册、注销过程也和application类似。

每个Function模块也定义了各自的Function命令,例如Fun_db.c就定义了DB、DB_EXISTS、DB_DELETE等Function命令。

二、             asterisk启动过程

主要就main函数讲解asterisk的启动过程:

int main(int argc, char *argv[])

{

       int c;

       char filename[80] = "";

       char hostname[MAXHOSTNAMELEN] = "";

       char tmp[80];

       char * xarg = NULL;

       int x;

       FILE *f;

       sigset_t sigs;

       int num;

       int isroot = 1;

       char *buf;

       char *runuser = NULL, *rungroup = NULL;

/*保存命令行参数(argv[]->_argv[]),以便程序重启时使用*/

       /* Remember original args for restart */

       if (argc > sizeof(_argv) / sizeof(_argv[0]) - 1) {

              fprintf(stderr, "Truncating argument size to %d/n", (int)(sizeof(_argv) / sizeof(_argv[0])) - 1);

              argc = sizeof(_argv) / sizeof(_argv[0]) - 1;

       }

       for (x=0; x<argc; x++)

              _argv[x] = argv[x];

       _argv[x] = NULL;

       if (geteuid() != 0)

              isroot = 0;

/*命令如果是rasterisk,设置AST_OPT_FLAG_NO_FORK和AST_OPT_FLAG_REMOTE标志位*/

       /* if the progname is rasterisk consider it a remote console */

       if (argv[0] && (strstr(argv[0], "rasterisk")) != NULL) {

              ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);

       }

/*得到当前主机名,在启动时打印出来*/

       if (gethostname(hostname, sizeof(hostname)-1))

              ast_copy_string(hostname, "<Unknown>", sizeof(hostname));

/*获取当前的进程标识*/

       ast_mainpid = getpid();

/*建立mu-law和a-law转换表*/

       ast_ulaw_init();

       ast_alaw_init();

/*为FFT逆变换(傅立叶逆变换)做一些初始化,用于在zaptel里进行callerid的DTMF检测*/

       callerid_init();

/*初始化内置命令的_full_cmd字符串,并注册常用命令,ast_builtins_init() -> ast_cli_register_multiple() -> ast_cli_register() -> __ast_cli_register() */

       ast_builtins_init();

/*初始化base64转换*/

       ast_utils_init();

/* tty/tdd初始化*/

       tdd_init();

/*设置用户历史命令的保存路径*/

       if (getenv("HOME"))

              snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));

       /* Check for options */

/*检查命令行的输入参数,匹配参数范围是“mtThfFdvVqprRgciInx:U:G:C:L:M:”,不同的参数输入走到不同的case分支处理。有几个v,verbose级别就增加几*/

       while ((c = getopt(argc, argv, "mtThfFdvVqprRgciInx:U:G:C:L:M:")) != -1) {

              switch (c) {

#if HAVE_WORKING_FORK

              case 'F':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK);

                     break;

              case 'f':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);

                     break;

#endif

              case 'd':

                     option_debug++;

                     ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);

                     break;

              case 'c':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_CONSOLE);

                     break;

              case 'n':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_NO_COLOR);

                     break;

              case 'r':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);

                     break;

              case 'R':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE | AST_OPT_FLAG_RECONNECT);

                     break;

              case 'p':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY);

                     break;

              case 'v':

                     option_verbose++;

                     ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);

                     break;

              case 'm':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_MUTE);

                     break;

              case 'M':

                     if ((sscanf(optarg, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0))

                            option_maxcalls = 0;

                     break;

              case 'L':

                     if ((sscanf(optarg, "%lf", &option_maxload) != 1) || (option_maxload < 0.0))

                            option_maxload = 0.0;

                     break;

              case 'q':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_QUIET);

                     break;

              case 't':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES);

                     break;

              case 'T':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP);

                     break;

              case 'x':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_EXEC);

                     xarg = ast_strdupa(optarg);

                     break;

              case 'C':

                     ast_copy_string(ast_config_AST_CONFIG_FILE, optarg, sizeof(ast_config_AST_CONFIG_FILE));

                     ast_set_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG);

                     break;

              case 'I':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING);

                     break;

              case 'i':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS);

                     break;

              case 'g':

                     ast_set_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE);

                     break;

              case 'h':

                     show_cli_help();

                     exit(0);

              case 'V':

                     show_version();

                     exit(0);

              case 'U':

                     runuser = ast_strdupa(optarg);

                     break;

              case 'G':

                     rungroup = ast_strdupa(optarg);

                     break;

              case '?':

                     exit(1);

              }

       }

/*如果用了-c或者-v或者-r并且没有-x cmd参数,则打印欢迎信息*/

       if (ast_opt_console || option_verbose || (ast_opt_remote && !ast_opt_exec)) {

              ast_register_verbose(console_verboser);

              WELCOME_MESSAGE;

       }

/*如果没有开调试则简单打印Booting... */

       if (ast_opt_console && !option_verbose)

              ast_verbose("[ Booting.../n");

/*显示控制台时,不论是本地还是远程,都不能使用-F参数,否则无效*/

       if (ast_opt_always_fork && (ast_opt_remote || ast_opt_console)) {

              ast_log(LOG_WARNING, "'alwaysfork' is not compatible with console or remote console mode; ignored/n");

              ast_clear_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK);

       }

       /* For remote connections, change the name of the remote connection.

        * We do this for the benefit of init scripts (which need to know if/when

        * the main asterisk process has died yet). */

       if (ast_opt_remote) {

              strcpy(argv[0], "rasterisk");

              for (x = 1; x < argc; x++) {

                     argv[x] = argv[0] + 10;

              }

       }

/*读取主配置文件,主配置文件是由make menuselect配置的*/

       if (ast_opt_console && !option_verbose)

              ast_verbose("[ Reading Master Configuration ]/n");

       ast_readconfig();

/*如果启动加了-g,取消core dump文件的大小限制*/

       if (ast_opt_dump_core) {

              struct rlimit l;

              memset(&l, 0, sizeof(l));

              l.rlim_cur = RLIM_INFINITY;

              l.rlim_max = RLIM_INFINITY;

              if (setrlimit(RLIMIT_CORE, &l)) {

                     ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s/n", strerror(errno));

              }

       }

/*修改用户和组权限*/

       if ((!rungroup) && !ast_strlen_zero(ast_config_AST_RUN_GROUP))

              rungroup = ast_config_AST_RUN_GROUP;

       if ((!runuser) && !ast_strlen_zero(ast_config_AST_RUN_USER))

              runuser = ast_config_AST_RUN_USER;

#ifndef __CYGWIN__

       if (isroot)

              ast_set_priority(ast_opt_high_priority);

       if (isroot && rungroup) {

              struct group *gr;

              gr = getgrnam(rungroup);

              if (!gr) {

                     ast_log(LOG_WARNING, "No such group '%s'!/n", rungroup);

                     exit(1);

              }

              if (setgid(gr->gr_gid)) {

                     ast_log(LOG_WARNING, "Unable to setgid to %d (%s)/n", (int)gr->gr_gid, rungroup);

                     exit(1);

              }

              if (setgroups(0, NULL)) {

                     ast_log(LOG_WARNING, "Unable to drop unneeded groups/n");

                     exit(1);

              }

              if (option_verbose)

                     ast_verbose("Running as group '%s'/n", rungroup);

       }

       if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)) {

#ifdef HAVE_CAP

              int has_cap = 1;

#endif /* HAVE_CAP */

              struct passwd *pw;

              pw = getpwnam(runuser);

              if (!pw) {

                     ast_log(LOG_WARNING, "No such user '%s'!/n", runuser);

                     exit(1);

              }

#ifdef HAVE_CAP

              if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {

                     ast_log(LOG_WARNING, "Unable to keep capabilities./n");

                     has_cap = 0;

              }

#endif /* HAVE_CAP */

              if (!isroot && pw->pw_uid != geteuid()) {

                     ast_log(LOG_ERROR, "Asterisk started as nonroot, but runuser '%s' requested./n", runuser);

                     exit(1);

              }

              if (!rungroup) {

                     if (setgid(pw->pw_gid)) {

                            ast_log(LOG_WARNING, "Unable to setgid to %d!/n", (int)pw->pw_gid);

                            exit(1);

                     }

                     if (isroot && initgroups(pw->pw_name, pw->pw_gid)) {

                            ast_log(LOG_WARNING, "Unable to init groups for '%s'/n", runuser);

                            exit(1);

                     }

              }

              if (setuid(pw->pw_uid)) {

                     ast_log(LOG_WARNING, "Unable to setuid to %d (%s)/n", (int)pw->pw_uid, runuser);

                     exit(1);

              }

              if (option_verbose)

                     ast_verbose("Running as user '%s'/n", runuser);

#ifdef HAVE_CAP

              if (has_cap) {

                     cap_t cap;

                     cap = cap_from_text("cap_net_admin=ep");

                     if (cap_set_proc(cap))

                            ast_log(LOG_WARNING, "Unable to install capabilities./n");

                     if (cap_free(cap))

                            ast_log(LOG_WARNING, "Unable to drop capabilities./n");

              }

#endif /* HAVE_CAP */

       }

#endif /* __CYGWIN__ */

#ifdef linux

       if (geteuid() && ast_opt_dump_core) {

              if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {

                     ast_log(LOG_WARNING, "Unable to set the process for core dumps after changing to a non-root user. %s/n", strerror(errno));

              }   

       }

#endif

/*初始化模拟终端ast_term_init(),默认是VT100*/

       ast_term_init();

       printf(term_end());

       fflush(stdout);

       if (ast_opt_console && !option_verbose)

              ast_verbose("[ Initializing Custom Configuration Options ]/n");

       /* custom config setup */

/*注册命令core show config mappings*/

       register_config_cli();

/*配置文件的映射和绑定*/

       read_config_maps();

       if (ast_opt_console) {

              if (el_hist == NULL || el == NULL)

                     ast_el_initialize();

              if (!ast_strlen_zero(filename))

                     ast_el_read_history(filename);

       }

/*设置和检查本地或远程终端的连接*/

       if (ast_tryconnect()) {

              /* One is already running */

              if (ast_opt_remote) {

                     if (ast_opt_exec) {

                            ast_remotecontrol(xarg);

                            quit_handler(0, 0, 0, 0);

                            exit(0);

                     }

                     printf(term_quit());

                     ast_remotecontrol(NULL);

                     quit_handler(0, 0, 0, 0);

                     exit(0);

              } else {

                     ast_log(LOG_ERROR, "Asterisk already running on %s.  Use 'asterisk -r' to connect./n", ast_config_AST_SOCKET);

                     printf(term_quit());

                     exit(1);

              }

       } else if (ast_opt_remote || ast_opt_exec) {

              ast_log(LOG_ERROR, "Unable to connect to remote asterisk (does %s exist?)/n", ast_config_AST_SOCKET);

              printf(term_quit());

              exit(1);

       }

       /* Blindly write pid file since we couldn't connect */

       unlink(ast_config_AST_PID);

       f = fopen(ast_config_AST_PID, "w");

       if (f) {

              fprintf(f, "%ld/n", (long)getpid());

              fclose(f);

       } else

              ast_log(LOG_WARNING, "Unable to open pid file '%s': %s/n", ast_config_AST_PID, strerror(errno));

#if HAVE_WORKING_FORK

       if (ast_opt_always_fork || !ast_opt_no_fork) {

#ifndef HAVE_SBIN_LAUNCHD

              daemon(1, 0);

              ast_mainpid = getpid();

              /* Blindly re-write pid file since we are forking */

              unlink(ast_config_AST_PID);

              f = fopen(ast_config_AST_PID, "w");

              if (f) {

                     fprintf(f, "%ld/n", (long)ast_mainpid);

                     fclose(f);

              } else

                     ast_log(LOG_WARNING, "Unable to open pid file '%s': %s/n", ast_config_AST_PID, strerror(errno));

#else

              ast_log(LOG_WARNING, "Mac OS X detected.  Use '/sbin/launchd -d' to launch with the nofork option./n");

#endif

       }

#endif

       /* Test recursive mutex locking. */

/*测试线程安全,避免出现死锁*/

       if (test_for_thread_safety())

              ast_verbose("Warning! Asterisk is not thread safe./n");

/*创建用于和控制台交互的服务器端socket接口*/

       ast_makesocket();

/*加入信号集,设置掩码,以及注册信号的相应handler */

       sigemptyset(&sigs);

       sigaddset(&sigs, SIGHUP);

       sigaddset(&sigs, SIGTERM);

       sigaddset(&sigs, SIGINT);

       sigaddset(&sigs, SIGPIPE);

       sigaddset(&sigs, SIGWINCH);

       pthread_sigmask(SIG_BLOCK, &sigs, NULL);

       signal(SIGURG, urg_handler);

       signal(SIGINT, __quit_handler);

       signal(SIGTERM, __quit_handler);

       signal(SIGHUP, hup_handler);

       signal(SIGCHLD, child_handler);

       signal(SIGPIPE, SIG_IGN);

       /* ensure that the random number generators are seeded with a different value every time

          Asterisk is started

       */

/*设置种子并初始化随机数发生器*/

       srand((unsigned int) getpid() + (unsigned int) time(NULL));

       initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool));

/*初始化日志模块*/

       if (init_logger()) {

              printf(term_quit());

              exit(1);

       }

#ifdef HAVE_ZAPTEL

       {

              int fd;

              int x = 160;

              fd = open("/dev/zap/timer", O_RDWR);

              if (fd >= 0) {

                     if (ioctl(fd, ZT_TIMERCONFIG, &x)) {

                            ast_log(LOG_ERROR, "You have Zaptel built and drivers loaded, but the Zaptel timer test failed to set ZT_TIMERCONFIG to %d./n", x);

                            exit(1);

                     }

                     if ((x = ast_wait_for_input(fd, 300)) < 0) {

                            ast_log(LOG_ERROR, "You have Zaptel built and drivers loaded, but the Zaptel timer could not be polled during the Zaptel timer test./n");

                            exit(1);

                     }

                     if (!x) {

                            const char zaptel_timer_error[] = {

                                   "Asterisk has detected a problem with your Zaptel configuration and will shutdown for your protection.  You have options:"

                                   "/n/t1. You only have to compile Zaptel support into Asterisk if you need it.  One option is to recompile without Zaptel support."

                                   "/n/t2. You only have to load Zaptel drivers if you want to take advantage of Zaptel services.  One option is to unload zaptel modules if you don't need them."

                                   "/n/t3. If you need Zaptel services, you must correctly configure Zaptel."

                            };

                            ast_log(LOG_ERROR, "%s/n", zaptel_timer_error);

                            exit(1);

                     }

                     close(fd);

              }

       }

#endif

/*注册threadstorage show allocations和threadstorage show summary这两个命令*/

       threadstorage_init();

       astobj2_init();

       ast_autoservice_init();

/*加载配置文件/etc/asterisk/modules.conf中标记为preload的模块,再去掉标记为noload的模块*/

       if (load_modules(1)) {

              printf(term_quit());

              exit(1);

       }

/* DNS manager的初始化*/

       if (dnsmgr_init()) {

              printf(term_quit());

              exit(1);

       }

/*配置http服务器*/

       ast_http_init();

/*注册两个命令core show channeltypes和core show channeltype */

       ast_channels_init();

/*注册管理命令*/

       if (init_manager()) {

              printf(term_quit());

              exit(1);

       }

/*用来创建一个调度上下文以及注册相应的命令,然后用do_reload来读取配置文件cdr.conf和创建后台线程do_cdr */

       if (ast_cdr_engine_init()) {

              printf(term_quit());

              exit(1);

       }

/*用来创建一个后台线程轮巡设备的状态,如果发生变化则及时通告*/

       if (ast_device_state_engine_init()) {

              printf(term_quit());

              exit(1);

       }

/*注册rtp,rtcp,stun相关的CLI命令,然后调用ast_rtp_reload()读取配置文件rtp.conf,设置相关参数*/

       ast_rtp_init();

/*注册udptl相关的CLI命令,然后调用ast_udptl_reload()读取配置文件udptl.conf,设置相关参数*/

       ast_udptl_init();

/*注册core show image formats */

       if (ast_image_init()) {

              printf(term_quit());

              exit(1);

       }

/*注册core show file formats */

       if (ast_file_init()) {

              printf(term_quit());

              exit(1);

       }

/*注册dialplan相关的CLI命令,然后调用ast_register_application来注册所有的app */

       if (load_pbx()) {

              printf(term_quit());

              exit(1);

       }

/*注册与codec相关的CLI命令*/

       if (init_framer()) {

              printf(term_quit());

              exit(1);

       }

/*注册与database相关的CLI命令,然后再注册两个管理命令DBGet和DBPut */

       if (astdb_init()) {

              printf(term_quit());

              exit(1);

       }

/*读取配置文件enum.conf,初始化支持ENUM(e164)的子系统*/

       if (ast_enum_init()) {

              printf(term_quit());

              exit(1);

       }

/* load_modules(0)加载所有其它需要加载的动态链接库*/

       if (load_modules(0)) {

              printf(term_quit());

              exit(1);

       }

       dnsmgr_start_refresh();

       /* We might have the option of showing a console, but for now just

          do nothing... */

       if (ast_opt_console && !option_verbose)

              ast_verbose(" ]/n");

       if (option_verbose || ast_opt_console)

              ast_verbose(term_color(tmp, "Asterisk Ready./n", COLOR_BRWHITE, COLOR_BLACK, sizeof(tmp)));

       if (ast_opt_no_fork)

              consolethread = pthread_self();

/*创建管道*/

       if (pipe(sig_alert_pipe))

              sig_alert_pipe[0] = sig_alert_pipe[1] = -1;

       ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED);

       pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);

#ifdef __AST_DEBUG_MALLOC

       __ast_mm_init();

#endif   

       time(&ast_startuptime);

/*注册asterisk相关的命令,比如stop,restart,halt等等*/

       ast_cli_register_multiple(cli_asterisk, sizeof(cli_asterisk) / sizeof(struct ast_cli_entry));

       if (ast_opt_console) {

              /* Console stuff now... */

              /* Register our quit function */

              char title[256];

              pthread_attr_t attr;

              pthread_t dont_care;

/*创建线程,轮询上面创建的sig_alert_pipe管道*/

              pthread_attr_init(&attr);

              pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

              ast_pthread_create(&dont_care, &attr, monitor_sig_flags, NULL);

              pthread_attr_destroy(&attr);

              set_icon("Asterisk");

              snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %ld)", hostname, (long)ast_mainpid);

              set_title(title);

/*接收和处理控制台命令*/

              for (;;) {

                     buf = (char *)el_gets(el, &num);

                     if (!buf && write(1, "", 1) < 0)

                            goto lostterm;

                     if (buf) {

                            if (buf[strlen(buf)-1] == '/n')

                                   buf[strlen(buf)-1] = '/0';

                            consolehandler((char *)buf);

                     } else if (ast_opt_remote && (write(STDOUT_FILENO, "/nUse EXIT or QUIT to exit the asterisk console/n",

                               strlen("/nUse EXIT or QUIT to exit the asterisk console/n")) < 0)) {

                            /* Whoa, stdout disappeared from under us... Make /dev/null's */

                            int fd;

                            fd = open("/dev/null", O_RDWR);

                            if (fd > -1) {

                                   dup2(fd, S

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Asterisk是一款开源的通信平台和电话私交换系统(PBX),能够支持语音、视频和实时通信服务。以下是关于Asterisk教程的一些重要信息: 1. 安装:Asterisk可在多个操作系统上运行,如Linux、Windows和Mac OS。安装Asterisk的步骤取决于所选操作系统和版本。一般来说,需要先下载Asterisk软件包,然后按照官方文档中的指导进行软件的编译和安装。 2. 配置:Asterisk的配置可以通过编辑配置文件来完成。主要的配置文件是"extensions.conf",它用于定义呼叫路由和转接规则。其他配置文件如"sip.conf"用于设置SIP(Session Initiation Protocol)通信协议的参数。 3. 通话:通过Asterisk可以完成一对一的语音通话、会议、语音信箱等多种通话场景。可以通过拨打扩展号、电话号码或者IP地址来建立通话。Asterisk的应用程序和功能可以实现呼叫转移、录音、音频会议和语音识别等特殊需求。 4. 网关和接口:Asterisk支持多种通信协议和接口,包括 SIP、ISDN、PRI和Analog等。可以将Asterisk与传统电话网络(PSTN)和互联网电话服务(VoIP)相连接,从而实现电话信号的互通。 5. 扩展功能:Asterisk有丰富的扩展功能和应用程序,可以通过为Asterisk添加额外的模块来扩展其功能。比如,Asterisk可以与数据库、Web服务器、IVR(Interactive Voice Response)系统和CTI(Computer Telephony Integration)系统进行集成。 总之,通过学习Asterisk教程,您可以了解到如何安装、配置和使用Asterisk,以及利用其强大的功能来构建企业级的通信系统。无论您是个人用户还是企业用户,Asterisk都是一个强大而灵活的解决方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值