搜索
您的当前位置:首页正文

Asterisk核心框架

来源:小奈知识网
Asterisk是一个开源的pbx系统,在公开的资料中,很难找到asterisk内核系统的详细描述。因此,很有必要写一篇内核框架的描述文档,作为内部培训文档,相互学习提高。

本文主要从三个层面来描述asterisk内核,即asterisk内核模块、内核启动过程、基本呼叫流程。一、asterisk内核模块

Asterisk由内部核心和外围动态可加载模块组成。内部核心由以下六个部分组成:PBX交换核心模块(PBX Switching Core)、调度和I/O管理模块(Scheduler and I/O Manager)、应用调用模块(ApplicationLauncher)、编解码转换模块(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是一个关键数据结构,它定义了注册builtinapplications的结构。

load_pbx函数用来注册builtin applications和一些命令行CLI命令(每个模块都有些CLI命令)。该函数在系统启动时被调用。pbx_exec是

Answer/BackGround/Busy/Goto/GotoIf/Hangup/Set等builtinapplications的执行入口函数,它被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的实体,

Asterisk内核框架

并将各自的回调函数赋值给它。例如 chan_sip.c中定义如下:/*! \\brief Definition of this channel for PBX channelregistration */

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注册为回调函数,供moduleload/unload/reload调用。2)Channel

以chan_开始的模块,如chan_sip.c、chan_h323.c、

chan_mgcp.c 、chan_iax2.c、 chan_zap.c等,对应代码保存在channels目录中。

channel注册、注销过程和application基本类似。由于每个channel需要和外部交互,都会在load_module中启用do_monitor线程来侦听外部tcp/udp端口,接收外部消息。

每个channel也定义了各自的cli命令和Function命令,例如chan_sip定义了sip debug/history/no/notify/prune/ reload/set/show等cli

命令和SIP_HEADER、CHECKSIPDOMAIN、SIPPEER、SIPCHANINFO等 Function命令。3)Functions

以Fun_开始的模块,例如Fun_db.c、func_moh.c、func_cdr.c等,对应代码保存在funcs目录中。

Function注册、注销过程也和application类似。

每个Function模块也定义了各自的Function命令,例如Fun_db.c就定义了DB、DB_EXISTS、DB_DELETE等Function命令。二、asterisk启动过程

主要就main函数讲解asterisk的启动过程:

1 int main(int argc, char *argv[]) 2 3 { 4

5 int c; 6

7 char filename[80] = \"\"; 8

9 char hostname[MAXHOSTNAMELEN] = \"\"; 10

11 char tmp[80]; 12

13 char * xarg = NULL; 14

15 int x; 16

17 FILE *f; 18

19 sigset_t sigs; 20

21 int num; 22

23 int isroot = 1; 24

25 char *buf; 26

27 char *runuser = NULL, *rungroup = NULL; 28

29 /*保存命令行参数(argv[]->_argv[]),以便程序重启时使用*/ 30

31 /* Remember original args for restart */ 32

33 if (argc > sizeof(_argv) / sizeof(_argv[0]) - 1) { 34

35 fprintf(stderr, \"Truncating argument size to %d\\n\(sizeof(_argv) / sizeof(_argv[0])) - 1); 36

37 argc = sizeof(_argv) / sizeof(_argv[0]) - 1; 38

39 } 40

41 for (x=0; x43 _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 | A 58

59 } 60

61 /*得到当前主机名,在启动时打印出来*/ 62

63 if (gethostname(hostname, sizeof(hostname)-1)) 64

65 ast_copy_string(hostname, \"\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 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 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 112

113 break; 114

115 case 'f': 116

117 ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FOR 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_FOR 128

129 break; 130

131 case 'c': 132

133 ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FOR 134

135 break; 136

137 case 'n': 138

139 ast_set_flag(&ast_options, AST_OPT_FLAG_NO_COL 140

141 break; 142

143 case 'r': 144

145 ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FOR 146

147 break; 148

149 case 'R': 150

151 ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FOR 152

153 break; 154

155 case 'p': 156

157 ast_set_flag(&ast_options, AST_OPT_FLAG_HIGH_P 158

159 break; 160

161 case 'v': 162

163 option_verbose++; 164

165 ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FOR

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\ 178

179 option_maxcalls = 0; 180

181 break; 182

183 case 'L': 184

185 if ((sscanf(optarg, \"%lf\ 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_ 200

201 break; 202

203 case 'T': 204

205 ast_set_flag(&ast_options, AST_OPT_FLAG_TIMEST 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, opt 220

221 ast_set_flag(&ast_options, AST_OPT_FLAG_OVERRI 222

223 break; 224

225 case 'I': 226

227 ast_set_flag(&ast_options, AST_OPT_FLAG_INTERN 228

229 break; 230

231 case 'i': 232

233 ast_set_flag(&ast_options, AST_OPT_FLAG_INIT_KE 234

235 break; 236

237 case 'g': 238

239 ast_set_flag(&ast_options, AST_OPT_FLAG_DUMP_C 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 && 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_conso 294

295 ast_log(LOG_WARNING, \"'alwaysfork' is not compatible w 296

297 ast_clear_flag(&ast_options, AST_OPT_FLAG_ALWAYS_F 298

299 } 300

301 /* For remote connections, change the name of the remote co 302

303 * We do this for the benefit of init scripts (which need to kno 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 342

343 } 344

345 } 346

347 /*修改用户和组权限*/ 348

349 if ((!rungroup) && !ast_strlen_zero(ast_config_AST_RUN_GRO

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\ 372

373 exit(1); 374

375 } 376

377 if (setgid(gr->gr_gid)) { 378

379 ast_log(LOG_WARNING, \"Unable to setgid to %d (%>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 388

389 exit(1); 390

391 } 392

393 if (option_verbose) 394

395 ast_verbose(\"Running as group '%s'\\n\

396

397 } 398

399 if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_R 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\ 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 capabilitie 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, b 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 %>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 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 (%>pw_uid, runuser); 462

463 exit(1); 464

465 } 466

467 if (option_verbose) 468

469 ast_verbose(\"Running as user '%s'\\n\ 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 capa 482

483 if (cap_free(cap)) 484

485 ast_log(LOG_WARNING, \"Unable to drop capab 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 root user. %s\\n\ 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 %r' to connect.\\n\ 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 aster 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': % 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 '% 630

631 #else 632

633 ast_log(LOG_WARNING, \"Mac OS X detected. Use '/sbind' 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 wit 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 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\ 712

713 if (fd >= 0) { 714

715 if (ioctl(fd, ZT_TIMERCONFIG, &x)) { 716

717 ast_log(LOG_ERROR, \"You have Zaptel built an 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 an 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 you 736

737 \"\\n\1. You only have to compile Zaptel su 738

739 \"\\n\2. You only have to load Zaptel driver 740

741 \"\\n\3. If you need Zaptel services, you m 742

743 }; 744

745 ast_log(LOG_ERROR, \"%s\\n\ 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 no 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\ 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) /

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_D 960

961 ast_pthread_create(&dont_care, &attr, monitor_sig_flags 962

963 pthread_attr_destroy(&attr); 964

965 set_icon(\"Asterisk\"); 966

967 snprintf(title, sizeof(title), \"Asterisk Console on '%s' (pid 968

969 set_title(title); 970

971 /*接收和处理控制台命令*/ 972

973 for (;;) { 974

975 buf = (char *)el_gets(el, &num); 976

977 if (!buf && write(1, \"\ 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_FILEN 990

991 strlen(\"\\nUse EXIT or QUIT to exit the asteris 992

993 /* Whoa, stdout disappeared from under us Make /dev/null's */ 994

995 int fd; 996

997 fd = open(\"/dev/null\ 998

999 if (fd > -1) {1000

1001 dup2(fd, STDOUT_FILENO);1002

1003 dup2(fd, STDIN_FILENO);1004

1005 } else1006

1007 ast_log(LOG_WARNING, \"Failed to open /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的structast_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释放呼叫。

因篇幅问题不能全部显示,请点此查看更多更全内容

Top