引用地址: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释放呼叫。