原帖: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的启动过程:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->
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释放呼叫。