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

   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释放呼叫。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值