Asterisk是一個(gè)開源的pbx系統(tǒng),在公開的資料中,很難找到asterisk內(nèi)核系統(tǒng)的詳細(xì)描述。因此,很有必要寫一篇內(nèi)核框架的描述文檔,作為內(nèi)部培訓(xùn)文檔,相互學(xué)習(xí)提高。
本文主要從三個(gè)層面來描述asterisk內(nèi)核,即asterisk內(nèi)核模塊、內(nèi)核啟動(dòng)過程、基本呼叫流程。
一、asterisk內(nèi)核模塊
Asterisk由內(nèi)部核心和外圍動(dòng)態(tài)可加載模塊組成。內(nèi)部核心由以下六個(gè)部分組成:PBX交換核心模塊(PBX Switching
Core)、調(diào)度和I/O管理模塊(Scheduler and I/O Manager)、應(yīng)用調(diào)用模塊(Application
Launcher)、編解碼轉(zhuǎn)換模塊(Codec Translator)、動(dòng)態(tài)模塊加載器模塊(Dynamic Module
Loader)和CDR生成模塊(CDR Core)。
外圍動(dòng)態(tài)可加載模塊包括以App_開始的Applications、以Func_開始的Functions、以Res_開始的Resources、以Chan_開始的channels、以Codec_開始的codec編解碼模塊等。
1.內(nèi)核模塊
1) PBX交換核心模塊(PBX Switching Core):
l pbx.c
pbx.c是asterisk的核心模塊,每路呼叫都需要經(jīng)過它調(diào)度。pbx實(shí)現(xiàn)了builtin applications,也就是內(nèi)置的應(yīng)用,比如最常見的Answer,Hangup, Background,Wait等等。
struct ast_app是一個(gè)關(guān)鍵數(shù)據(jù)結(jié)構(gòu),它定義了注冊builtin applications的結(jié)構(gòu)。
load_pbx函數(shù)用來注冊builtin applications和一些命令行CLI命令(每個(gè)模塊都有些CLI命令)。該函數(shù)在系統(tǒng)啟動(dòng)時(shí)被調(diào)用。
pbx_exec是Answer/BackGround/Busy/Goto/GotoIf/Hangup/Set等builtin applications的執(zhí)行入口函數(shù),它被pbx_extension_helper調(diào)用。
ast_pbx_start函數(shù)是每路呼叫的起點(diǎn)。
2) 調(diào)度和I/O管理模塊(Scheduler and I/O Manager):
l Channel.c:
Channel.c/channel.h定義了channel操作的結(jié)構(gòu)體和接口函數(shù)。
struct
ast_channel_tech結(jié)構(gòu)體是所有channel都要用到的關(guān)鍵結(jié)構(gòu)體,它定義channel操作的一系列回調(diào)函數(shù)指針,如call、
hangup、answer等。每個(gè)channel模塊都會(huì)定義ast_channel_tech的實(shí)體,并將各自的回調(diào)函數(shù)賦值給它。例如
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等函數(shù)分別實(shí)現(xiàn)ast_channel_tech中的call、hangup、answer等回調(diào)函數(shù)的調(diào)用。
struct ast_channel結(jié)構(gòu)體定義了channel的上下文參數(shù),它是每個(gè)參與呼叫的channel必不可少的,都會(huì)調(diào)用ast_channel_alloc來申請ast_channel。
l io.c
io.c實(shí)現(xiàn)了asterisk跟外部交互時(shí)的I/O管理,如chan_sip為了從外部接收SIP信令,調(diào)用ast_io_add添加IO接口,并調(diào)用ast_io_wait實(shí)現(xiàn)外部消息接收。
3)應(yīng)用調(diào)用模塊(Application Launcher):
在pbx.c中定義了一系列的應(yīng)用調(diào)用接口。
applications模塊定義了application回調(diào)函數(shù)并注冊后,在pbx.c中通過應(yīng)用調(diào)用接口回調(diào)執(zhí)行。
應(yīng)用調(diào)用接口的關(guān)鍵函數(shù)是pbx_extension_helper,它執(zhí)行dialplan,在cli上打印“Executing ……”,并拋出ami event事件,同時(shí)調(diào)用pbx_exec執(zhí)行application回調(diào)函數(shù)。
4) 編解碼轉(zhuǎn)換模塊(Codec Translator):
Translate.c:
struct ast_translator:編碼轉(zhuǎn)換描述結(jié)構(gòu)體,它定義了編碼轉(zhuǎn)換的名稱、回調(diào)函數(shù)、運(yùn)行時(shí)選項(xiàng)。
struct ast_trans_pvt:編碼轉(zhuǎn)換上下文描述結(jié)構(gòu)體。
ast_register_translator:編碼轉(zhuǎn)換注冊接口函數(shù),供各編碼模塊調(diào)用,注冊struct ast_translator類型的結(jié)構(gòu)體變量。
ast_unregister_translator:編碼轉(zhuǎn)換注銷函數(shù)
ast_translate:編碼轉(zhuǎn)換的執(zhí)行函數(shù)。
codec_gsm.c/codec_...:對應(yīng)各種編碼的編解碼執(zhí)行模塊,如g.711alaw/g.711ulaw/gsm等。
5)動(dòng)態(tài)模塊加載器模塊(Dynamic Module Loader):
該模塊主要是Module.h。
Module.h中定義了struct ast_module_info結(jié)構(gòu),用來保存各模塊的注冊、注銷回調(diào)函數(shù),以及模塊描述信息。
load_module、unload_module,每個(gè)應(yīng)用模塊的注冊、注銷函數(shù),由各個(gè)模塊自行定義為static函數(shù)。
AST_MODULE_INFO_STANDARD:注冊接口、注銷接口、模塊描述信息等模塊信息的登記接口。它是一個(gè)宏定義,動(dòng)態(tài)模塊調(diào)用它時(shí),
首先定義類型為ast_module_info的__mod_info靜態(tài)結(jié)構(gòu)變量,保存模塊信息,并定義__attribute__
((constructor)) __reg_module和__attribute__ ((destructor))
__unreg_module,在程序啟動(dòng)和退出時(shí)調(diào)用。
6)CDR生成模塊(CDR Core):
Cdr.c:
ast_cdr_register:cdr driver注冊,供cdr_mysql等調(diào)用,注冊話單保存的回調(diào)函數(shù)。
ast_cdr_engine_init:CDR模塊初始化,注冊cdr status、加載cdr.conf、啟動(dòng)CDR線程。
ast_cdr_detach:產(chǎn)生話單的接口函數(shù),呼叫結(jié)束時(shí)被調(diào)用。
2.外圍可加載模塊:
1)Applications
以app_開始的模塊,如app_dial.c、app_db.c、app_queue.c、app_record.c、app_meetme.c
等,代碼保存在apps目錄中。每個(gè)application模塊都定義了load_module函數(shù)和unload_module函數(shù),分別用來注冊和注
銷application。
load_module函數(shù)調(diào)用ast_register_application函數(shù),注冊application命令,例如app_dial模
塊注冊Dial:res = ast_register_application(app, dial_exec, synopsis,
descrip)。
unload_module函數(shù)調(diào)用ast_unregister_application函數(shù),注銷application命令。
每個(gè)application模塊都會(huì)使用AST_MODULE_INFO_STANDARD宏來登記模塊信息__mod_info。
AST_MODULE_INFO_STANDARD將load_module和unload_module注冊為回調(diào)函數(shù),供module
load/unload/reload調(diào)用。
2)Channel
以chan_開始的模塊,如chan_sip.c、chan_h323.c、chan_mgcp.c 、chan_iax2.c、 chan_zap.c等,對應(yīng)代碼保存在channels目錄中。
channel注冊、注銷過程和application基本類似。由于每個(gè)channel需要和外部交互,都會(huì)在load_module中啟用do_monitor線程來偵聽外部tcp/udp端口,接收外部消息。
每個(gè)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等,對應(yīng)代碼保存在funcs目錄中。
Function注冊、注銷過程也和application類似。
每個(gè)Function模塊也定義了各自的Function命令,例如Fun_db.c就定義了DB、DB_EXISTS、DB_DELETE等Function命令。
二、asterisk啟動(dòng)過程
主要就main函數(shù)講解asterisk的啟動(dòng)過程:
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 /*保存命令行參數(shù)(argv[]->_argv[]),以便程序重啟時(shí)使用*/
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,設(shè)置AST_OPT_FLAG_NO_FORK和AST_OPT_FLAG_REMOTE標(biāo)志位*/
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 /*得到當(dāng)前主機(jī)名,在啟動(dòng)時(shí)打印出來*/
62
63 if (gethostname(hostname, sizeof(hostname)-1))
64
65 ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
66
67 /*獲取當(dāng)前的進(jìn)程標(biāo)識(shí)*/
68
69 ast_mainpid = getpid();
70
71 /*建立mu-law和a-law轉(zhuǎn)換表*/
72
73 ast_ulaw_init();
74
75 ast_alaw_init();
76
77 /*為FFT逆變換(傅立葉逆變換)做一些初始化,用于在zaptel里進(jìn)行callerid的DTMF檢測*/
78
79 callerid_init();
80
81 /*初始化內(nèi)置命令的_full_cmd字符串,并注冊常用命令,ast_builtins_init() -> ast_cli_register_multiple() -> ast_cli_register() -> __ast_cli_register() */
82
83 ast_builtins_init();
84
85 /*初始化base64轉(zhuǎn)換*/
86
87 ast_utils_init();
88
89 /* tty/tdd初始化*/
90
91 tdd_init();
92
93 /*設(shè)置用戶歷史命令的保存路徑*/
94
95 if (getenv("HOME"))
96
97 snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
98
99 /* Check for options */
100
101 /*檢查命令行的輸入?yún)?shù),匹配參數(shù)范圍是“mtThfFdvVqprRgciInx:U:G:C:L:M:”,不同的參數(shù)輸入走到不同的case分支處理。有幾個(gè)v,verbose級(jí)別就增加幾*/
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參數(shù),則打印歡迎信息*/
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 /*如果沒有開調(diào)試則簡單打印Booting
*/
286
287 if (ast_opt_console && !option_verbose)
288
289 ast_verbose("[ Booting
\n");
290
291 /*顯示控制臺(tái)時(shí),不論是本地還是遠(yuǎn)程,都不能使用-F參數(shù),否則無效*/
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 /*如果啟動(dòng)加了-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 /*修改用戶和組權(quán)限*/
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(),默認(rèn)是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 /*設(shè)置和檢查本地或遠(yuǎn)程終端的連接*/
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 /*測試線程安全,避免出現(xiàn)死鎖*/
644
645 if (test_for_thread_safety())
646
647 ast_verbose("Warning! Asterisk is not thread safe.\n");
648
649 /*創(chuàng)建用于和控制臺(tái)交互的服務(wù)器端socket接口*/
650
651 ast_makesocket();
652
653 /*加入信號(hào)集,設(shè)置掩碼,以及注冊信號(hào)的相應(yīng)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 /*設(shè)置種子并初始化隨機(jī)數(shù)發(fā)生器*/
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這兩個(gè)命令*/
760
761 threadstorage_init();
762
763 astobj2_init();
764
765 ast_autoservice_init();
766
767 /*加載配置文件/etc/asterisk/modules.conf中標(biāo)記為preload的模塊,再去掉標(biāo)記為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服務(wù)器*/
788
789 ast_http_init();
790
791 /*注冊兩個(gè)命令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 /*用來創(chuàng)建一個(gè)調(diào)度上下文以及注冊相應(yīng)的命令,然后用do_reload來讀取配置文件cdr.conf和創(chuàng)建后臺(tái)線程do_cdr */
806
807 if (ast_cdr_engine_init()) {
808
809 printf(term_quit());
810
811 exit(1);
812
813 }
814
815 /*用來創(chuàng)建一個(gè)后臺(tái)線程輪巡設(shè)備的狀態(tài),如果發(fā)生變化則及時(shí)通告*/
816
817 if (ast_device_state_engine_init()) {
818
819 printf(term_quit());
820
821 exit(1);
822
823 }
824
825 /*注冊rtp,rtcp,stun相關(guān)的CLI命令,然后調(diào)用ast_rtp_reload()讀取配置文件rtp.conf,設(shè)置相關(guān)參數(shù)*/
826
827 ast_rtp_init();
828
829 /*注冊u(píng)dptl相關(guān)的CLI命令,然后調(diào)用ast_udptl_reload()讀取配置文件udptl.conf,設(shè)置相關(guān)參數(shù)*/
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相關(guān)的CLI命令,然后調(diào)用ast_register_application來注冊所有的app */
854
855 if (load_pbx()) {
856
857 printf(term_quit());
858
859 exit(1);
860
861 }
862
863 /*注冊與codec相關(guān)的CLI命令*/
864
865 if (init_framer()) {
866
867 printf(term_quit());
868
869 exit(1);
870
871 }
872
873 /*注冊與database相關(guān)的CLI命令,然后再注冊兩個(gè)管理命令DBGet和DBPut */
874
875 if (astdb_init()) {
876
877 printf(term_quit());
878
879 exit(1);
880
881 }
882
883 /*讀取配置文件enum.conf,初始化支持ENUM(e164)的子系統(tǒng)*/
884
885 if (ast_enum_init()) {
886
887 printf(term_quit());
888
889 exit(1);
890
891 }
892
893 /* load_modules(0)加載所有其它需要加載的動(dòng)態(tài)鏈接庫*/
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 /*創(chuàng)建管道*/
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相關(guān)的命令,比如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 /*創(chuàng)建線程,輪詢上面創(chuàng)建的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 /*接收和處理控制臺(tái)命令*/
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基本呼叫流程
從內(nèi)核的角度去分析問題時(shí),弄清楚呼叫流程是非常關(guān)鍵的,只有理清了呼叫流程,才能從流程的各個(gè)環(huán)節(jié)細(xì)節(jié)中分析出問題所在。
Asterisk所有功能都是基于函數(shù)調(diào)用的模式,呼叫流程也不例外。因此如何從一團(tuán)亂麻似的內(nèi)核函數(shù)調(diào)用中理出函數(shù)調(diào)用執(zhí)行路線,是解讀呼叫流程的關(guān)鍵。
所有呼叫都跟astersisk的channel有關(guān)。這路通話都包含一個(gè)incoming連接和一個(gè)outbound連接。每個(gè)電話都是通過對應(yīng)
的channel程序建立起來的,比如Chan_sip,Chan_zap,Chan_iax2等等。每一類的channel,都擁有自己私有的
channel數(shù)據(jù)結(jié)構(gòu),例如chan_sip的struct
sip_pvt結(jié)構(gòu),這些私有的結(jié)構(gòu)從屬于一個(gè)通用的Asterisk通道數(shù)據(jù)結(jié)構(gòu)中,具體定義在channel.h的struct
ast_channe中。
下圖是asterisk 的呼叫流程圖:
我們以sip的呼叫過程為例來描述,其他channel的呼叫過程基本類似。
Astersik下注冊的sip用戶主動(dòng)發(fā)起一個(gè)呼叫的函數(shù)調(diào)用過程(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->執(zhí)行dialplan
當(dāng)Chan_sip模塊被加載時(shí),會(huì)啟動(dòng)一個(gè)獨(dú)立的監(jiān)聽線程do_monitor,不斷偵聽sip端口上的外部消息;
當(dāng)sip用戶撥叫被叫號(hào)碼后,chan_sip的do_monitor調(diào)用sipsock_read函數(shù),在sip端口收到invite消息,然后就調(diào)用handle_request和handle_request_invite進(jìn)行處理。
在handle_request_invite中,首先解析invite消息,對該sip用戶的業(yè)務(wù)屬性分析,確認(rèn)被叫可達(dá),然后就調(diào)用sip_new申請channel資源,并調(diào)用ast_pbx_start函數(shù)啟動(dòng)一個(gè)pbx_thread線程來專門處理該呼叫。
pbx_thread線程調(diào)用__ast_pbx_run。
__ast_pbx_run是一個(gè)銜接dialplan和內(nèi)核的關(guān)鍵函數(shù),它首先調(diào)用ast_exists_extension函數(shù),根據(jù)分機(jī)號(hào)碼
的context屬性,匹配到對應(yīng)的dialplan;然后進(jìn)入一個(gè)for死循環(huán),逐條執(zhí)行dialplan對應(yīng)的context中的語句。
pbx_extension_helper函數(shù)調(diào)用pbx_extension_helper,在pbx_extension_helper中調(diào)用
pbx_find_extension找到對應(yīng)的context后,通過verbose打印dialplan執(zhí)行語句“Executing
……”,同時(shí)調(diào)用pbx_exec執(zhí)行該dialplan。執(zhí)行到dial語句呼叫被叫。
在等待被叫接通的過程中,完成媒體協(xié)商過程,向主叫發(fā)送180、200OK消息接通呼叫。
當(dāng)其他用戶呼叫asterisk的sip用戶時(shí),函數(shù)調(diào)用過程(outbound)如下:
Dial->dial_exec->dial_exec_full->ast_request/ast_call/wait_for_answer/
ast_bridge_call
呼叫執(zhí)行到dial時(shí),pbx_exec調(diào)用application dial的接口函數(shù)dial_exec,dial_exec調(diào)用dial_exec_full。
在dial_exec_full中,首先調(diào)用ast_request,在ast_request調(diào)用chan_sip對應(yīng)的回調(diào)函數(shù)
sip_request_call為該被叫sip用戶申請channel資源。然后調(diào)用ast_call,在ast_call中調(diào)用chan_sip對應(yīng)
的回調(diào)函數(shù)sip_call向被叫發(fā)送INVITE消息,呼叫被叫SIP用戶。
然后該呼叫線程會(huì)調(diào)用wait_for_answer等待被叫接通。
在呼叫接通后,也即wait_for_answer函數(shù)返回,在dial_exec_full中調(diào)用ast_bridge_call橋接媒體,這樣呼叫就正式接通了。
當(dāng)chan_sip的偵聽線程接收到BYE消息,則調(diào)用handle_request_bye找到相應(yīng)的channel,執(zhí)行hangup釋放呼叫。