引用地址: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的啓動過程:
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釋放呼叫。