Asterisk内核框架

原帖:http://www.cppblog.com/eyesmart/archive/2009/09/03/95219.aspx


 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的启动过程:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->    1  int  main( int  argc,  char   * argv[])
   2 
   3  {
   4 
   5          int  c;
   6 
   7          char  filename[ 80 =   "" ;
   8 
   9          char  hostname[MAXHOSTNAMELEN]  =   "" ;
  10 
  11          char  tmp[ 80 ];
  12 
  13          char   *  xarg  =  NULL;
  14 
  15          int  x;
  16 
  17         FILE  * f;
  18 
  19         sigset_t sigs;
  20 
  21          int  num;
  22 
  23          int  isroot  =   1 ;
  24 
  25          char   * buf;
  26 
  27          char   * runuser  =  NULL,  * rungroup  =  NULL;
  28 
  29  /* 保存命令行参数(argv[]->_argv[]),以便程序重启时使用 */
  30 
  31          /*  Remember original args for restart  */
  32 
  33          if  (argc  >   sizeof (_argv)  /   sizeof (_argv[ 0 ])  -   1 ) {
  34 
  35                fprintf(stderr,  " Truncating argument size to %d\n " , ( int )( sizeof (_argv)  /   sizeof (_argv[ 0 ]))  -   1 );
  36 
  37                argc  =   sizeof (_argv)  /   sizeof (_argv[ 0 ])  -   1 ;
  38 
  39         }
  40 
  41          for  (x = 0 ; x < argc; x ++ )
  42 
  43                _argv[x]  =  argv[x];
  44 
  45         _argv[x]  =  NULL;
  46 
  47          if  (geteuid()  !=   0 )
  48 
  49                isroot  =   0 ;
  50 
  51  /* 命令如果是rasterisk,设置AST_OPT_FLAG_NO_FORK和AST_OPT_FLAG_REMOTE标志位 */
  52 
  53          /*  if the progname is rasterisk consider it a remote console  */
  54 
  55          if  (argv[ 0 &&  (strstr(argv[ 0 ],  " rasterisk " ))  !=  NULL) {
  56 
  57                ast_set_flag( & ast_options, AST_OPT_FLAG_NO_FORK  |  AST_OPT_FLAG_REMOTE);
  58 
  59         }
  60 
  61  /* 得到当前主机名,在启动时打印出来 */
  62 
  63          if  (gethostname(hostname,  sizeof (hostname) - 1 ))
  64 
  65                ast_copy_string(hostname,  " <Unknown> " sizeof (hostname));
  66 
  67  /* 获取当前的进程标识 */
  68 
  69         ast_mainpid  =  getpid();
  70 
  71  /* 建立mu-law和a-law转换表 */
  72 
  73         ast_ulaw_init();
  74 
  75         ast_alaw_init();
  76 
  77  /* 为FFT逆变换(傅立叶逆变换)做一些初始化,用于在zaptel里进行callerid的DTMF检测 */
  78 
  79         callerid_init();
  80 
  81  /* 初始化内置命令的_full_cmd字符串,并注册常用命令,ast_builtins_init() -> ast_cli_register_multiple() -> ast_cli_register() -> __ast_cli_register()  */
  82 
  83         ast_builtins_init();
  84 
  85  /* 初始化base64转换 */
  86 
  87         ast_utils_init();
  88 
  89  /*  tty/tdd初始化 */
  90 
  91         tdd_init();
  92 
  93  /* 设置用户历史命令的保存路径 */
  94 
  95          if  (getenv( " HOME " ))
  96 
  97                snprintf(filename,  sizeof (filename),  " %s/.asterisk_history " , getenv( " HOME " ));
  98 
  99          /*  Check for options  */
 100 
 101  /* 检查命令行的输入参数,匹配参数范围是“mtThfFdvVqprRgciInx:U:G:C:L:M:”,不同的参数输入走到不同的case分支处理。有几个v,verbose级别就增加几 */
 102 
 103          while  ((c  =  getopt(argc, argv,  " mtThfFdvVqprRgciInx:U:G:C:L:M: " ))  !=   - 1 ) {
 104 
 105                 switch  (c) {
 106 
 107  #if  HAVE_WORKING_FORK
 108 
 109                 case   ' F ' :
 110 
 111                       ast_set_flag( & ast_options, AST_OPT_FLAG_ALWAYS_FORK);
 112 
 113                        break ;
 114 
 115                 case   ' f ' :
 116 
 117                       ast_set_flag( & ast_options, AST_OPT_FLAG_NO_FORK);
 118 
 119                        break ;
 120 
 121  #endif
 122 
 123                 case   ' d ' :
 124 
 125                       option_debug ++ ;
 126 
 127                       ast_set_flag( & ast_options, AST_OPT_FLAG_NO_FORK);
 128 
 129                        break ;
 130 
 131                 case   ' c ' :
 132 
 133                       ast_set_flag( & ast_options, AST_OPT_FLAG_NO_FORK  |  AST_OPT_FLAG_CONSOLE);
 134 
 135                        break ;
 136 
 137                 case   ' n ' :
 138 
 139                       ast_set_flag( & ast_options, AST_OPT_FLAG_NO_COLOR);
 140 
 141                        break ;
 142 
 143                 case   ' r ' :
 144 
 145                       ast_set_flag( & ast_options, AST_OPT_FLAG_NO_FORK  |  AST_OPT_FLAG_REMOTE);
 146 
 147                        break ;
 148 
 149                 case   ' R ' :
 150 
 151                       ast_set_flag( & ast_options, AST_OPT_FLAG_NO_FORK  |  AST_OPT_FLAG_REMOTE  |  AST_OPT_FLAG_RECONNECT);
 152 
 153                        break ;
 154 
 155                 case   ' p ' :
 156 
 157                       ast_set_flag( & ast_options, AST_OPT_FLAG_HIGH_PRIORITY);
 158 
 159                        break ;
 160 
 161                 case   ' v ' :
 162 
 163                       option_verbose ++ ;
 164 
 165                       ast_set_flag( & ast_options, AST_OPT_FLAG_NO_FORK);
 166 
 167                        break ;
 168 
 169                 case   ' m ' :
 170 
 171                       ast_set_flag( & ast_options, AST_OPT_FLAG_MUTE);
 172 
 173                        break ;
 174 
 175                 case   ' M ' :
 176 
 177                        if  ((sscanf(optarg,  " %d " & option_maxcalls)  !=   1 ||  (option_maxcalls  <   0 ))
 178 
 179                              option_maxcalls  =   0 ;
 180 
 181                        break ;
 182 
 183                 case   ' L ' :
 184 
 185                        if  ((sscanf(optarg,  " %lf " & option_maxload)  !=   1 ||  (option_maxload  <   0.0 ))
 186 
 187                              option_maxload  =   0.0 ;
 188 
 189                        break ;
 190 
 191                 case   ' q ' :
 192 
 193                       ast_set_flag( & ast_options, AST_OPT_FLAG_QUIET);
 194 
 195                        break ;
 196 
 197                 case   ' t ' :
 198 
 199                       ast_set_flag( & ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES);
 200 
 201                        break ;
 202 
 203                 case   ' T ' :
 204 
 205                       ast_set_flag( & ast_options, AST_OPT_FLAG_TIMESTAMP);
 206 
 207                        break ;
 208 
 209                 case   ' x ' :
 210 
 211                       ast_set_flag( & ast_options, AST_OPT_FLAG_EXEC);
 212 
 213                       xarg  =  ast_strdupa(optarg);
 214 
 215                        break ;
 216 
 217                 case   ' C ' :
 218 
 219                       ast_copy_string(ast_config_AST_CONFIG_FILE, optarg,  sizeof (ast_config_AST_CONFIG_FILE));
 220 
 221                       ast_set_flag( & ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG);
 222 
 223                        break ;
 224 
 225                 case   ' I ' :
 226 
 227                       ast_set_flag( & ast_options, AST_OPT_FLAG_INTERNAL_TIMING);
 228 
 229                        break ;
 230 
 231                 case   ' i ' :
 232 
 233                       ast_set_flag( & ast_options, AST_OPT_FLAG_INIT_KEYS);
 234 
 235                        break ;
 236 
 237                 case   ' g ' :
 238 
 239                       ast_set_flag( & ast_options, AST_OPT_FLAG_DUMP_CORE);
 240 
 241                        break ;
 242 
 243                 case   ' h ' :
 244 
 245                       show_cli_help();
 246 
 247                       exit( 0 );
 248 
 249                 case   ' V ' :
 250 
 251                       show_version();
 252 
 253                       exit( 0 );
 254 
 255                 case   ' U ' :
 256 
 257                       runuser  =  ast_strdupa(optarg);
 258 
 259                        break ;
 260 
 261                 case   ' G ' :
 262 
 263                       rungroup  =  ast_strdupa(optarg);
 264 
 265                        break ;
 266 
 267                 case   ' ? ' :
 268 
 269                       exit( 1 );
 270 
 271                }
 272 
 273         }
 274 
 275  /* 如果用了-c或者-v或者-r并且没有-x cmd参数,则打印欢迎信息 */
 276 
 277          if  (ast_opt_console  ||  option_verbose  ||  (ast_opt_remote  &&   ! ast_opt_exec)) {
 278 
 279                ast_register_verbose(console_verboser);
 280 
 281                WELCOME_MESSAGE;
 282 
 283         }
 284 
 285  /* 如果没有开调试则简单打印Booting  */
 286 
 287          if  (ast_opt_console  &&   ! option_verbose)
 288 
 289                ast_verbose( " [ Booting\n " );
 290 
 291  /* 显示控制台时,不论是本地还是远程,都不能使用-F参数,否则无效 */
 292 
 293          if  (ast_opt_always_fork  &&  (ast_opt_remote  ||  ast_opt_console)) {
 294 
 295                ast_log(LOG_WARNING,  " 'alwaysfork' is not compatible with console or remote console mode; ignored\n " );
 296 
 297                ast_clear_flag( & ast_options, AST_OPT_FLAG_ALWAYS_FORK);
 298 
 299         }
 300 
 301          /*  For remote connections, change the name of the remote connection.
 302 
 303          * We do this for the benefit of init scripts (which need to know if/when
 304 
 305          * the main asterisk process has died yet).  */
 306 
 307          if  (ast_opt_remote) {
 308 
 309                strcpy(argv[ 0 ],  " rasterisk " );
 310 
 311                 for  (x  =   1 ; x  <  argc; x ++ ) {
 312 
 313                       argv[x]  =  argv[ 0 +   10 ;
 314 
 315                }
 316 
 317         }
 318 
 319  /* 读取主配置文件,主配置文件是由make menuselect配置的 */
 320 
 321          if  (ast_opt_console  &&   ! option_verbose)
 322 
 323                ast_verbose( " [ Reading Master Configuration ]\n " );
 324 
 325         ast_readconfig();
 326 
 327  /* 如果启动加了-g,取消core dump文件的大小限制 */
 328 
 329          if  (ast_opt_dump_core) {
 330 
 331                 struct  rlimit l;
 332 
 333                memset( & l,  0 sizeof (l));
 334 
 335                l.rlim_cur  =  RLIM_INFINITY;
 336 
 337                l.rlim_max  =  RLIM_INFINITY;
 338 
 339                 if  (setrlimit(RLIMIT_CORE,  & l)) {
 340 
 341                       ast_log(LOG_WARNING,  " Unable to disable core size resource limit: %s\n " , strerror(errno));
 342 
 343                }
 344 
 345         }
 346 
 347  /* 修改用户和组权限 */
 348 
 349          if  (( ! rungroup)  &&   ! ast_strlen_zero(ast_config_AST_RUN_GROUP))
 350 
 351                rungroup  =  ast_config_AST_RUN_GROUP;
 352 
 353          if  (( ! runuser)  &&   ! ast_strlen_zero(ast_config_AST_RUN_USER))
 354 
 355                runuser  =  ast_config_AST_RUN_USER;
 356 
 357  #ifndef __CYGWIN__
 358 
 359          if  (isroot)
 360 
 361                ast_set_priority(ast_opt_high_priority);
 362 
 363          if  (isroot  &&  rungroup) {
 364 
 365                 struct  group  * gr;
 366 
 367                gr  =  getgrnam(rungroup);
 368 
 369                 if  ( ! gr) {
 370 
 371                       ast_log(LOG_WARNING,  " No such group '%s'!\n " , rungroup);
 372 
 373                       exit( 1 );
 374 
 375                }
 376 
 377                 if  (setgid(gr -> gr_gid)) {
 378 
 379                       ast_log(LOG_WARNING,  " Unable to setgid to %d (%s)\n " , ( int )gr -> gr_gid, rungroup);
 380 
 381                       exit( 1 );
 382 
 383                }
 384 
 385                 if  (setgroups( 0 , NULL)) {
 386 
 387                       ast_log(LOG_WARNING,  " Unable to drop unneeded groups\n " );
 388 
 389                       exit( 1 );
 390 
 391                }
 392 
 393                 if  (option_verbose)
 394 
 395                       ast_verbose( " Running as group '%s'\n " , rungroup);
 396 
 397         }
 398 
 399          if  (runuser  &&   ! ast_test_flag( & ast_options, AST_OPT_FLAG_REMOTE)) {
 400 
 401  #ifdef HAVE_CAP
 402 
 403                 int  has_cap  =   1 ;
 404 
 405  #endif  /* HAVE_CAP */
 406 
 407                 struct  passwd  * pw;
 408 
 409                pw  =  getpwnam(runuser);
 410 
 411                 if  ( ! pw) {
 412 
 413                       ast_log(LOG_WARNING,  " No such user '%s'!\n " , runuser);
 414 
 415                       exit( 1 );
 416 
 417                }
 418 
 419  #ifdef HAVE_CAP
 420 
 421                 if  (prctl(PR_SET_KEEPCAPS,  1 0 0 0 )) {
 422 
 423                       ast_log(LOG_WARNING,  " Unable to keep capabilities.\n " );
 424 
 425                       has_cap  =   0 ;
 426 
 427                }
 428 
 429  #endif  /* HAVE_CAP */
 430 
 431                 if  ( ! isroot  &&  pw -> pw_uid  !=  geteuid()) {
 432 
 433                       ast_log(LOG_ERROR,  " Asterisk started as nonroot, but runuser '%s' requested.\n " , runuser);
 434 
 435                       exit( 1 );
 436 
 437                }
 438 
 439                 if  ( ! rungroup) {
 440 
 441                        if  (setgid(pw -> pw_gid)) {
 442 
 443                              ast_log(LOG_WARNING,  " Unable to setgid to %d!\n " , ( int )pw -> pw_gid);
 444 
 445                              exit( 1 );
 446 
 447                       }
 448 
 449                        if  (isroot  &&  initgroups(pw -> pw_name, pw -> pw_gid)) {
 450 
 451                              ast_log(LOG_WARNING,  " Unable to init groups for '%s'\n " , runuser);
 452 
 453                              exit( 1 );
 454 
 455                       }
 456 
 457                }
 458 
 459                 if  (setuid(pw -> pw_uid)) {
 460 
 461                       ast_log(LOG_WARNING,  " Unable to setuid to %d (%s)\n " , ( int )pw -> pw_uid, runuser);
 462 
 463                       exit( 1 );
 464 
 465                }
 466 
 467                 if  (option_verbose)
 468 
 469                       ast_verbose( " Running as user '%s'\n " , runuser);
 470 
 471  #ifdef HAVE_CAP
 472 
 473                 if  (has_cap) {
 474 
 475                       cap_t cap;
 476 
 477                       cap  =  cap_from_text( " cap_net_admin=ep " );
 478 
 479                        if  (cap_set_proc(cap))
 480 
 481                              ast_log(LOG_WARNING,  " Unable to install capabilities.\n " );
 482 
 483                        if  (cap_free(cap))
 484 
 485                              ast_log(LOG_WARNING,  " Unable to drop capabilities.\n " );
 486 
 487                }
 488 
 489  #endif  /* HAVE_CAP */
 490 
 491         }
 492 
 493  #endif  /* __CYGWIN__ */
 494 
 495  #ifdef linux
 496 
 497          if  (geteuid()  &&  ast_opt_dump_core) {
 498 
 499                 if  (prctl(PR_SET_DUMPABLE,  1 0 0 0 <   0 ) {
 500 
 501                       ast_log(LOG_WARNING,  " Unable to set the process for core dumps after changing to a non-root user. %s\n " , strerror(errno));
 502 
 503                }   
 504 
 505         }
 506 
 507  #endif
 508 
 509  /* 初始化模拟终端ast_term_init(),默认是VT100 */
 510 
 511         ast_term_init();
 512 
 513         printf(term_end());
 514 
 515         fflush(stdout);
 516 
 517          if  (ast_opt_console  &&   ! option_verbose)
 518 
 519                ast_verbose( " [ Initializing Custom Configuration Options ]\n " );
 520 
 521          /*  custom config setup  */
 522 
 523  /* 注册命令core show config mappings */
 524 
 525         register_config_cli();
 526 
 527  /* 配置文件的映射和绑定 */
 528 
 529         read_config_maps();
 530 
 531          if  (ast_opt_console) {
 532 
 533                 if  (el_hist  ==  NULL  ||  el  ==  NULL)
 534 
 535                       ast_el_initialize();
 536 
 537                 if  ( ! ast_strlen_zero(filename))
 538 
 539                       ast_el_read_history(filename);
 540 
 541         }
 542 
 543  /* 设置和检查本地或远程终端的连接 */
 544 
 545          if  (ast_tryconnect()) {
 546 
 547                 /*  One is already running  */
 548 
 549                 if  (ast_opt_remote) {
 550 
 551                        if  (ast_opt_exec) {
 552 
 553                              ast_remotecontrol(xarg);
 554 
 555                              quit_handler( 0 0 0 0 );
 556 
 557                              exit( 0 );
 558 
 559                       }
 560 
 561                       printf(term_quit());
 562 
 563                       ast_remotecontrol(NULL);
 564 
 565                       quit_handler( 0 0 0 0 );
 566 
 567                       exit( 0 );
 568 
 569                }  else  {
 570 
 571                       ast_log(LOG_ERROR,  " Asterisk already running on %s.  Use 'asterisk -r' to connect.\n " , ast_config_AST_SOCKET);
 572 
 573                       printf(term_quit());
 574 
 575                       exit( 1 );
 576 
 577                }
 578 
 579         }  else   if  (ast_opt_remote  ||  ast_opt_exec) {
 580 
 581                ast_log(LOG_ERROR,  " Unable to connect to remote asterisk (does %s exist?)\n " , ast_config_AST_SOCKET);
 582 
 583                printf(term_quit());
 584 
 585                exit( 1 );
 586 
 587         }
 588 
 589          /*  Blindly write pid file since we couldn't connect  */
 590 
 591         unlink(ast_config_AST_PID);
 592 
 593         f  =  fopen(ast_config_AST_PID,  " w " );
 594 
 595          if  (f) {
 596 
 597                fprintf(f,  " %ld\n " , ( long )getpid());
 598 
 599                fclose(f);
 600 
 601         }  else
 602 
 603                ast_log(LOG_WARNING,  " Unable to open pid file '%s': %s\n " , ast_config_AST_PID, strerror(errno));
 604 
 605  #if  HAVE_WORKING_FORK
 606 
 607          if  (ast_opt_always_fork  ||   ! ast_opt_no_fork) {
 608 
 609  #ifndef HAVE_SBIN_LAUNCHD
 610 
 611                daemon( 1 0 );
 612 
 613                ast_mainpid  =  getpid();
 614 
 615                 /*  Blindly re-write pid file since we are forking  */
 616 
 617                unlink(ast_config_AST_PID);
 618 
 619                f  =  fopen(ast_config_AST_PID,  " w " );
 620 
 621                 if  (f) {
 622 
 623                       fprintf(f,  " %ld\n " , ( long )ast_mainpid);
 624 
 625                       fclose(f);
 626 
 627                }  else
 628 
 629                       ast_log(LOG_WARNING,  " Unable to open pid file '%s': %s\n " , ast_config_AST_PID, strerror(errno));
 630 
 631  #else
 632 
 633                ast_log(LOG_WARNING,  " Mac OS X detected.  Use '/sbin/launchd -d' to launch with the nofork option.\n " );
 634 
 635  #endif
 636 
 637         }
 638 
 639  #endif
 640 
 641          /*  Test recursive mutex locking.  */
 642 
 643  /* 测试线程安全,避免出现死锁 */
 644 
 645          if  (test_for_thread_safety())
 646 
 647                ast_verbose( " Warning! Asterisk is not thread safe.\n " );
 648 
 649  /* 创建用于和控制台交互的服务器端socket接口 */
 650 
 651         ast_makesocket();
 652 
 653  /* 加入信号集,设置掩码,以及注册信号的相应handler  */
 654 
 655         sigemptyset( & sigs);
 656 
 657         sigaddset( & sigs, SIGHUP);
 658 
 659         sigaddset( & sigs, SIGTERM);
 660 
 661         sigaddset( & sigs, SIGINT);
 662 
 663         sigaddset( & sigs, SIGPIPE);
 664 
 665         sigaddset( & sigs, SIGWINCH);
 666 
 667         pthread_sigmask(SIG_BLOCK,  & sigs, NULL);
 668 
 669         signal(SIGURG, urg_handler);
 670 
 671         signal(SIGINT, __quit_handler);
 672 
 673         signal(SIGTERM, __quit_handler);
 674 
 675         signal(SIGHUP, hup_handler);
 676 
 677         signal(SIGCHLD, child_handler);
 678 
 679         signal(SIGPIPE, SIG_IGN);
 680 
 681          /*  ensure that the random number generators are seeded with a different value every time
 682 
 683            Asterisk is started
 684 
 685          */
 686 
 687  /* 设置种子并初始化随机数发生器 */
 688 
 689         srand((unsigned  int ) getpid()  +  (unsigned  int ) time(NULL));
 690 
 691         initstate((unsigned  int ) getpid()  *   65536   +  (unsigned  int ) time(NULL), randompool,  sizeof (randompool));
 692 
 693  /* 初始化日志模块 */
 694 
 695          if  (init_logger()) {
 696 
 697                printf(term_quit());
 698 
 699                exit( 1 );
 700 
 701         }
 702 
 703  #ifdef HAVE_ZAPTEL
 704 
 705         {
 706 
 707                 int  fd;
 708 
 709                 int  x  =   160 ;
 710 
 711                fd  =  open( " /dev/zap/timer " , O_RDWR);
 712 
 713                 if  (fd  >=   0 ) {
 714 
 715                        if  (ioctl(fd, ZT_TIMERCONFIG,  & x)) {
 716 
 717                              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);
 718 
 719                              exit( 1 );
 720 
 721                       }
 722 
 723                        if  ((x  =  ast_wait_for_input(fd,  300 ))  <   0 ) {
 724 
 725                              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 " );
 726 
 727                              exit( 1 );
 728 
 729                       }
 730 
 731                        if  ( ! x) {
 732 
 733                               const   char  zaptel_timer_error[]  =  {
 734 
 735                                      " Asterisk has detected a problem with your Zaptel configuration and will shutdown for your protection.  You have options: "
 736 
 737                                      " \n\t1. You only have to compile Zaptel support into Asterisk if you need it.  One option is to recompile without Zaptel support. "
 738 
 739                                      " \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. "
 740 
 741                                      " \n\t3. If you need Zaptel services, you must correctly configure Zaptel. "
 742 
 743                              };
 744 
 745                              ast_log(LOG_ERROR,  " %s\n " , zaptel_timer_error);
 746 
 747                              exit( 1 );
 748 
 749                       }
 750 
 751                       close(fd);
 752 
 753                }
 754 
 755         }
 756 
 757  #endif
 758 
 759  /* 注册threadstorage show allocations和threadstorage show summary这两个命令 */
 760 
 761         threadstorage_init();
 762 
 763         astobj2_init();
 764 
 765         ast_autoservice_init();
 766 
 767  /* 加载配置文件/etc/asterisk/modules.conf中标记为preload的模块,再去掉标记为noload的模块 */
 768 
 769          if  (load_modules( 1 )) {
 770 
 771                printf(term_quit());
 772 
 773                exit( 1 );
 774 
 775         }
 776 
 777  /*  DNS manager的初始化 */
 778 
 779          if  (dnsmgr_init()) {
 780 
 781                printf(term_quit());
 782 
 783                exit( 1 );
 784 
 785         }
 786 
 787  /* 配置http服务器 */
 788 
 789         ast_http_init();
 790 
 791  /* 注册两个命令core show channeltypes和core show channeltype  */
 792 
 793         ast_channels_init();
 794 
 795  /* 注册管理命令 */
 796 
 797          if  (init_manager()) {
 798 
 799                printf(term_quit());
 800 
 801                exit( 1 );
 802 
 803         }
 804 
 805  /* 用来创建一个调度上下文以及注册相应的命令,然后用do_reload来读取配置文件cdr.conf和创建后台线程do_cdr  */
 806 
 807          if  (ast_cdr_engine_init()) {
 808 
 809                printf(term_quit());
 810 
 811                exit( 1 );
 812 
 813         }
 814 
 815  /* 用来创建一个后台线程轮巡设备的状态,如果发生变化则及时通告 */
 816 
 817          if  (ast_device_state_engine_init()) {
 818 
 819                printf(term_quit());
 820 
 821                exit( 1 );
 822 
 823         }
 824 
 825  /* 注册rtp,rtcp,stun相关的CLI命令,然后调用ast_rtp_reload()读取配置文件rtp.conf,设置相关参数 */
 826 
 827         ast_rtp_init();
 828 
 829  /* 注册udptl相关的CLI命令,然后调用ast_udptl_reload()读取配置文件udptl.conf,设置相关参数 */
 830 
 831         ast_udptl_init();
 832 
 833  /* 注册core show image formats  */
 834 
 835          if  (ast_image_init()) {
 836 
 837                printf(term_quit());
 838 
 839                exit( 1 );
 840 
 841         }
 842 
 843  /* 注册core show file formats  */
 844 
 845          if  (ast_file_init()) {
 846 
 847                printf(term_quit());
 848 
 849                exit( 1 );
 850 
 851         }
 852 
 853  /* 注册dialplan相关的CLI命令,然后调用ast_register_application来注册所有的app  */
 854 
 855          if  (load_pbx()) {
 856 
 857                printf(term_quit());
 858 
 859                exit( 1 );
 860 
 861         }
 862 
 863  /* 注册与codec相关的CLI命令 */
 864 
 865          if  (init_framer()) {
 866 
 867                printf(term_quit());
 868 
 869                exit( 1 );
 870 
 871         }
 872 
 873  /* 注册与database相关的CLI命令,然后再注册两个管理命令DBGet和DBPut  */
 874 
 875          if  (astdb_init()) {
 876 
 877                printf(term_quit());
 878 
 879                exit( 1 );
 880 
 881         }
 882 
 883  /* 读取配置文件enum.conf,初始化支持ENUM(e164)的子系统 */
 884 
 885          if  (ast_enum_init()) {
 886 
 887                printf(term_quit());
 888 
 889                exit( 1 );
 890 
 891         }
 892 
 893  /*  load_modules(0)加载所有其它需要加载的动态链接库 */
 894 
 895          if  (load_modules( 0 )) {
 896 
 897                printf(term_quit());
 898 
 899                exit( 1 );
 900 
 901         }
 902 
 903         dnsmgr_start_refresh();
 904 
 905          /*  We might have the option of showing a console, but for now just
 906 
 907            do nothing  */
 908 
 909          if  (ast_opt_console  &&   ! option_verbose)
 910 
 911                ast_verbose( "  ]\n " );
 912 
 913          if  (option_verbose  ||  ast_opt_console)
 914 
 915                ast_verbose(term_color(tmp,  " Asterisk Ready.\n " , COLOR_BRWHITE, COLOR_BLACK,  sizeof (tmp)));
 916 
 917          if  (ast_opt_no_fork)
 918 
 919                consolethread  =  pthread_self();
 920 
 921  /* 创建管道 */
 922 
 923          if  (pipe(sig_alert_pipe))
 924 
 925                sig_alert_pipe[ 0 =  sig_alert_pipe[ 1 =   - 1 ;
 926 
 927         ast_set_flag( & ast_options, AST_OPT_FLAG_FULLY_BOOTED);
 928 
 929         pthread_sigmask(SIG_UNBLOCK,  & sigs, NULL);
 930 
 931  #ifdef __AST_DEBUG_MALLOC
 932 
 933         __ast_mm_init();
 934 
 935  #endif    
 936 
 937         time( & ast_startuptime);
 938 
 939  /* 注册asterisk相关的命令,比如stop,restart,halt等等 */
 940 
 941         ast_cli_register_multiple(cli_asterisk,  sizeof (cli_asterisk)  /   sizeof ( struct  ast_cli_entry));
 942 
 943          if  (ast_opt_console) {
 944 
 945                 /*  Console stuff now  */
 946 
 947                 /*  Register our quit function  */
 948 
 949                 char  title[ 256 ];
 950 
 951                pthread_attr_t attr;
 952 
 953                pthread_t dont_care;
 954 
 955  /* 创建线程,轮询上面创建的sig_alert_pipe管道 */
 956 
 957                pthread_attr_init( & attr);
 958 
 959                pthread_attr_setdetachstate( & attr, PTHREAD_CREATE_DETACHED);
 960 
 961                ast_pthread_create( & dont_care,  & attr, monitor_sig_flags, NULL);
 962 
 963                pthread_attr_destroy( & attr);
 964 
 965                set_icon( " Asterisk " );
 966 
 967                snprintf(title,  sizeof (title),  " Asterisk Console on '%s' (pid %ld) " , hostname, ( long )ast_mainpid);
 968 
 969                set_title(title);
 970 
 971  /* 接收和处理控制台命令 */
 972 
 973                 for  (;;) {
 974 
 975                       buf  =  ( char   * )el_gets(el,  & num);
 976 
 977                        if  ( ! buf  &&  write( 1 "" 1 <   0 )
 978 
 979                               goto  lostterm;
 980 
 981                        if  (buf) {
 982 
 983                               if  (buf[strlen(buf) - 1 ==   ' \n ' )
 984 
 985                                     buf[strlen(buf) - 1 =   ' \0 ' ;
 986 
 987                              consolehandler(( char   * )buf);
 988 
 989                       }  else   if  (ast_opt_remote  &&  (write(STDOUT_FILENO,  " \nUse EXIT or QUIT to exit the asterisk console\n " ,
 990 
 991                                 strlen( " \nUse EXIT or QUIT to exit the asterisk console\n " ))  <   0 )) {
 992 
 993                               /*  Whoa, stdout disappeared from under us Make /dev/null's  */
 994 
 995                               int  fd;
 996 
 997                              fd  =  open( " /dev/null " , O_RDWR);
 998 
 999                               if  (fd  >   - 1 ) {
1000 
1001                                     dup2(fd, STDOUT_FILENO);
1002 
1003                                     dup2(fd, STDIN_FILENO);
1004 
1005                              }  else
1006 
1007                                     ast_log(LOG_WARNING,  " Failed to open /dev/null to recover from dead console. Bad things will happen!\n " );
1008 
1009                               break ;
1010 
1011                       }
1012 
1013                }
1014 
1015         }
1016 
1017         monitor_sig_flags(NULL);
1018 
1019  lostterm:
1020 
1021          return   0 ;
1022 
1023  }

三、asterisk基本呼叫流程

从内核的角度去分析问题时,弄清楚呼叫流程是非常关键的,只有理清了呼叫流程,才能从流程的各个环节细节中分析出问题所在。

Asterisk所有功能都是基于函数调用的模式,呼叫流程也不例外。因此如何从一团乱麻似的内核函数调用中理出函数调用执行路线,是解读呼叫流程的关键。

所有呼叫都跟astersisk的channel有关。这路通话都包含一个incoming连接和一个outbound连接。每个电话都是通过对应的channel程序建立起来的,比如Chan_sip,Chan_zap,Chan_iax2等等。每一类的channel,都拥有自己私有的 channel数据结构,例如chan_sip的struct sip_pvt结构,这些私有的结构从属于一个通用的Asterisk通道数据结构中,具体定义在channel.h的struct ast_channe中。

下图是asterisk 的呼叫流程图:

我们以sip的呼叫过程为例来描述,其他channel的呼叫过程基本类似。

Astersik下注册的sip用户主动发起一个呼叫的函数调用过程(incoming)如下:

do_monitor->sipsock_read->handle_request->handle_request_invite->sip_new/ast_pbx_start->pbx_thread->__ast_pbx_run

-> ast_spawn_extension ->pbx_extension_helper->pbx_exec->执行dialplan

当Chan_sip模块被加载时,会启动一个独立的监听线程do_monitor,不断侦听sip端口上的外部消息;

当sip用户拨叫被叫号码后,chan_sip的do_monitor调用sipsock_read函数,在sip端口收到invite消息,然后就调用handle_request和handle_request_invite进行处理。

在handle_request_invite中,首先解析invite消息,对该sip用户的业务属性分析,确认被叫可达,然后就调用sip_new申请channel资源,并调用ast_pbx_start函数启动一个pbx_thread线程来专门处理该呼叫。

pbx_thread线程调用__ast_pbx_run。

__ast_pbx_run是一个衔接dialplan和内核的关键函数,它首先调用ast_exists_extension函数,根据分机号码的context属性,匹配到对应的dialplan;然后进入一个for死循环,逐条执行dialplan对应的context中的语句。

pbx_extension_helper函数调用pbx_extension_helper,在pbx_extension_helper中调用 pbx_find_extension找到对应的context后,通过verbose打印dialplan执行语句“Executing ……”,同时调用pbx_exec执行该dialplan。执行到dial语句呼叫被叫。

在等待被叫接通的过程中,完成媒体协商过程,向主叫发送180、200OK消息接通呼叫。

当其他用户呼叫asterisk的sip用户时,函数调用过程(outbound)如下: Dial->dial_exec->dial_exec_full->ast_request/ast_call/wait_for_answer/ ast_bridge_call

呼叫执行到dial时,pbx_exec调用application dial的接口函数dial_exec,dial_exec调用dial_exec_full。

在dial_exec_full中,首先调用ast_request,在ast_request调用chan_sip对应的回调函数 sip_request_call为该被叫sip用户申请channel资源。然后调用ast_call,在ast_call中调用chan_sip对应的回调函数sip_call向被叫发送INVITE消息,呼叫被叫SIP用户。

然后该呼叫线程会调用wait_for_answer等待被叫接通。

在呼叫接通后,也即wait_for_answer函数返回,在dial_exec_full中调用ast_bridge_call桥接媒体,这样呼叫就正式接通了。

当chan_sip的侦听线程接收到BYE消息,则调用handle_request_bye找到相应的channel,执行hangup释放呼叫。

 

你可能感兴趣的:(Asterisk内核框架)