(轉)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釋放呼叫。

發佈了25 篇原創文章 · 獲贊 1 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章