Asterisk内核框架(转帖)
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的启动过程: int main(int argc, char *argv[]) { int c; char filename[80] = ""; char hostname[MAXHOSTNAMELEN] = ""; char tmp[80]; char * xarg = NULL; int x; FILE *f; sigset_t sigs; int num; int isroot = 1; char *buf; char *runuser = NULL, *rungroup = NULL; /*保存命令行参数(argv[]->_argv[]),以便程序重启时使用*/ /* Remember original args for restart */ if (argc > sizeof(_argv) / sizeof(_argv[0]) - 1) { fprintf(stderr, "Truncating argument size to %d/n", (int)(sizeof(_argv) / sizeof(_argv[0])) - 1); argc = sizeof(_argv) / sizeof(_argv[0]) - 1; } for (x=0; x<argc; x++) _argv[x] = argv[x]; _argv[x] = NULL; if (geteuid() != 0) isroot = 0; /*命令如果是rasterisk,设置AST_OPT_FLAG_NO_FORK和AST_OPT_FLAG_REMOTE标志位*/ /* if the progname is rasterisk consider it a remote console */ if (argv[0] && (strstr(argv[0], "rasterisk")) != NULL) { ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE); } /*得到当前主机名,在启动时打印出来*/ if (gethostname(hostname, sizeof(hostname)-1)) ast_copy_string(hostname, "<Unknown>", sizeof(hostname)); /*获取当前的进程标识*/ ast_mainpid = getpid(); /*建立mu-law和a-law转换表*/ ast_ulaw_init(); ast_alaw_init(); /*为FFT逆变换(傅立叶逆变换)做一些初始化,用于在zaptel里进行callerid的DTMF检测*/ callerid_init(); /*初始化内置命令的_full_cmd字符串,并注册常用命令,ast_builtins_init() -> ast_cli_register_multiple() -> ast_cli_register() -> __ast_cli_register() */ ast_builtins_init(); /*初始化base64转换*/ ast_utils_init(); /* tty/tdd初始化*/ tdd_init(); /*设置用户历史命令的保存路径*/ if (getenv("HOME")) snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); /* Check for options */ /*检查命令行的输入参数,匹配参数范围是“mtThfFdvVqprRgciInx:U:G:C:L:M:”,不同的参数输入走到不同的case分支处理。有几个v,verbose级别就增加几*/ while ((c = getopt(argc, argv, "mtThfFdvVqprRgciInx:U:G:C:L:M:")) != -1) { switch (c) { #if HAVE_WORKING_FORK case 'F': ast_set_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK); break; case 'f': ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK); break; #endif case 'd': option_debug++; ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK); break; case 'c': ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_CONSOLE); break; case 'n': ast_set_flag(&ast_options, AST_OPT_FLAG_NO_COLOR); break; case 'r': ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE); break; case 'R': ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE | AST_OPT_FLAG_RECONNECT); break; case 'p': ast_set_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY); break; case 'v': option_verbose++; ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK); break; case 'm': ast_set_flag(&ast_options, AST_OPT_FLAG_MUTE); break; case 'M': if ((sscanf(optarg, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0)) option_maxcalls = 0; break; case 'L': if ((sscanf(optarg, "%lf", &option_maxload) != 1) || (option_maxload < 0.0)) option_maxload = 0.0; break; case 'q': ast_set_flag(&ast_options, AST_OPT_FLAG_QUIET); break; case 't': ast_set_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES); break; case 'T': ast_set_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP); break; case 'x': ast_set_flag(&ast_options, AST_OPT_FLAG_EXEC); xarg = ast_strdupa(optarg); break; case 'C': ast_copy_string(ast_config_AST_CONFIG_FILE, optarg, sizeof(ast_config_AST_CONFIG_FILE)); ast_set_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG); break; case 'I': ast_set_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING); break; case 'i': ast_set_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS); break; case 'g': ast_set_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE); break; case 'h': show_cli_help(); exit(0); case 'V': show_version(); exit(0); case 'U': runuser = ast_strdupa(optarg); break; case 'G': rungroup = ast_strdupa(optarg); break; case '?': exit(1); } } /*如果用了-c或者-v或者-r并且没有-x cmd参数,则打印欢迎信息*/ if (ast_opt_console || option_verbose || (ast_opt_remote && !ast_opt_exec)) { ast_register_verbose(console_verboser); WELCOME_MESSAGE; } /*如果没有开调试则简单打印Booting... */ if (ast_opt_console && !option_verbose) ast_verbose("[ Booting.../n"); /*显示控制台时,不论是本地还是远程,都不能使用-F参数,否则无效*/ if (ast_opt_always_fork && (ast_opt_remote || ast_opt_console)) { ast_log(LOG_WARNING, "'alwaysfork' is not compatible with console or remote console mode; ignored/n"); ast_clear_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK); } /* For remote connections, change the name of the remote connection. * We do this for the benefit of init scripts (which need to know if/when * the main asterisk process has died yet). */ if (ast_opt_remote) { strcpy(argv[0], "rasterisk"); for (x = 1; x < argc; x++) { argv[x] = argv[0] + 10; } } /*读取主配置文件,主配置文件是由make menuselect配置的*/ if (ast_opt_console && !option_verbose) ast_verbose("[ Reading Master Configuration ]/n"); ast_readconfig(); /*如果启动加了-g,取消core dump文件的大小限制*/ if (ast_opt_dump_core) { struct rlimit l; memset(&l, 0, sizeof(l)); l.rlim_cur = RLIM_INFINITY; l.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_CORE, &l)) { ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s/n", strerror(errno)); } } /*修改用户和组权限*/ if ((!rungroup) && !ast_strlen_zero(ast_config_AST_RUN_GROUP)) rungroup = ast_config_AST_RUN_GROUP; if ((!runuser) && !ast_strlen_zero(ast_config_AST_RUN_USER)) runuser = ast_config_AST_RUN_USER; #ifndef __CYGWIN__ if (isroot) ast_set_priority(ast_opt_high_priority); if (isroot && rungroup) { struct group *gr; gr = getgrnam(rungroup); if (!gr) { ast_log(LOG_WARNING, "No such group '%s'!/n", rungroup); exit(1); } if (setgid(gr->gr_gid)) { ast_log(LOG_WARNING, "Unable to setgid to %d (%s)/n", (int)gr->gr_gid, rungroup); exit(1); } if (setgroups(0, NULL)) { ast_log(LOG_WARNING, "Unable to drop unneeded groups/n"); exit(1); } if (option_verbose) ast_verbose("Running as group '%s'/n", rungroup); } if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)) { #ifdef HAVE_CAP int has_cap = 1; #endif /* HAVE_CAP */ struct passwd *pw; pw = getpwnam(runuser); if (!pw) { ast_log(LOG_WARNING, "No such user '%s'!/n", runuser); exit(1); } #ifdef HAVE_CAP if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { ast_log(LOG_WARNING, "Unable to keep capabilities./n"); has_cap = 0; } #endif /* HAVE_CAP */ if (!isroot && pw->pw_uid != geteuid()) { ast_log(LOG_ERROR, "Asterisk started as nonroot, but runuser '%s' requested./n", runuser); exit(1); } if (!rungroup) { if (setgid(pw->pw_gid)) { ast_log(LOG_WARNING, "Unable to setgid to %d!/n", (int)pw->pw_gid); exit(1); } if (isroot && initgroups(pw->pw_name, pw->pw_gid)) { ast_log(LOG_WARNING, "Unable to init groups for '%s'/n", runuser); exit(1); } } if (setuid(pw->pw_uid)) { ast_log(LOG_WARNING, "Unable to setuid to %d (%s)/n", (int)pw->pw_uid, runuser); exit(1); } if (option_verbose) ast_verbose("Running as user '%s'/n", runuser); #ifdef HAVE_CAP if (has_cap) { cap_t cap; cap = cap_from_text("cap_net_admin=ep"); if (cap_set_proc(cap)) ast_log(LOG_WARNING, "Unable to install capabilities./n"); if (cap_free(cap)) ast_log(LOG_WARNING, "Unable to drop capabilities./n"); } #endif /* HAVE_CAP */ } #endif /* __CYGWIN__ */ #ifdef linux if (geteuid() && ast_opt_dump_core) { if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { ast_log(LOG_WARNING, "Unable to set the process for core dumps after changing to a non-root user. %s/n", strerror(errno)); } } #endif /*初始化模拟终端ast_term_init(),默认是VT100*/ ast_term_init(); printf(term_end()); fflush(stdout); if (ast_opt_console && !option_verbose) ast_verbose("[ Initializing Custom Configuration Options ]/n"); /* custom config setup */ /*注册命令core show config mappings*/ register_config_cli(); /*配置文件的映射和绑定*/ read_config_maps(); if (ast_opt_console) { if (el_hist == NULL || el == NULL) ast_el_initialize(); if (!ast_strlen_zero(filename)) ast_el_read_history(filename); } /*设置和检查本地或远程终端的连接*/ if (ast_tryconnect()) { /* One is already running */ if (ast_opt_remote) { if (ast_opt_exec) { ast_remotecontrol(xarg); quit_handler(0, 0, 0, 0); exit(0); } printf(term_quit()); ast_remotecontrol(NULL); quit_handler(0, 0, 0, 0); exit(0); } else { ast_log(LOG_ERROR, "Asterisk already running on %s. Use 'asterisk -r' to connect./n", ast_config_AST_SOCKET); printf(term_quit()); exit(1); } } else if (ast_opt_remote || ast_opt_exec) { ast_log(LOG_ERROR, "Unable to connect to remote asterisk (does %s exist?)/n", ast_config_AST_SOCKET); printf(term_quit()); exit(1); } /* Blindly write pid file since we couldn't connect */ unlink(ast_config_AST_PID); f = fopen(ast_config_AST_PID, "w"); if (f) { fprintf(f, "%ld/n", (long)getpid()); fclose(f); } else ast_log(LOG_WARNING, "Unable to open pid file '%s': %s/n", ast_config_AST_PID, strerror(errno)); #if HAVE_WORKING_FORK if (ast_opt_always_fork || !ast_opt_no_fork) { #ifndef HAVE_SBIN_LAUNCHD daemon(1, 0); ast_mainpid = getpid(); /* Blindly re-write pid file since we are forking */ unlink(ast_config_AST_PID); f = fopen(ast_config_AST_PID, "w"); if (f) { fprintf(f, "%ld/n", (long)ast_mainpid); fclose(f); } else ast_log(LOG_WARNING, "Unable to open pid file '%s': %s/n", ast_config_AST_PID, strerror(errno)); #else ast_log(LOG_WARNING, "Mac OS X detected. Use '/sbin/launchd -d' to launch with the nofork option./n"); #endif } #endif /* Test recursive mutex locking. */ /*测试线程安全,避免出现死锁*/ if (test_for_thread_safety()) ast_verbose("Warning! Asterisk is not thread safe./n"); /*创建用于和控制台交互的服务器端socket接口*/ ast_makesocket(); /*加入信号集,设置掩码,以及注册信号的相应handler */ sigemptyset(&sigs); sigaddset(&sigs, SIGHUP); sigaddset(&sigs, SIGTERM); sigaddset(&sigs, SIGINT); sigaddset(&sigs, SIGPIPE); sigaddset(&sigs, SIGWINCH); pthread_sigmask(SIG_BLOCK, &sigs, NULL); signal(SIGURG, urg_handler); signal(SIGINT, __quit_handler); signal(SIGTERM, __quit_handler); signal(SIGHUP, hup_handler); signal(SIGCHLD, child_handler); signal(SIGPIPE, SIG_IGN); /* ensure that the random number generators are seeded with a different value every time Asterisk is started */ /*设置种子并初始化随机数发生器*/ srand((unsigned int) getpid() + (unsigned int) time(NULL)); initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool)); /*初始化日志模块*/ if (init_logger()) { printf(term_quit()); exit(1); } #ifdef HAVE_ZAPTEL { int fd; int x = 160; fd = open("/dev/zap/timer", O_RDWR); if (fd >= 0) { if (ioctl(fd, ZT_TIMERCONFIG, &x)) { 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); exit(1); } if ((x = ast_wait_for_input(fd, 300)) < 0) { 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"); exit(1); } if (!x) { const char zaptel_timer_error[] = { "Asterisk has detected a problem with your Zaptel configuration and will shutdown for your protection. You have options:" "/n/t1. You only have to compile Zaptel support into Asterisk if you need it. One option is to recompile without Zaptel support." "/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." "/n/t3. If you need Zaptel services, you must correctly configure Zaptel." }; ast_log(LOG_ERROR, "%s/n", zaptel_timer_error); exit(1); } close(fd); } } #endif /*注册threadstorage show allocations和threadstorage show summary这两个命令*/ threadstorage_init(); astobj2_init(); ast_autoservice_init(); /*加载配置文件/etc/asterisk/modules.conf中标记为preload的模块,再去掉标记为noload的模块*/ if (load_modules(1)) { printf(term_quit()); exit(1); } /* DNS manager的初始化*/ if (dnsmgr_init()) { printf(term_quit()); exit(1); } /*配置http服务器*/ ast_http_init(); /*注册两个命令core show channeltypes和core show channeltype */ ast_channels_init(); /*注册管理命令*/ if (init_manager()) { printf(term_quit()); exit(1); } /*用来创建一个调度上下文以及注册相应的命令,然后用do_reload来读取配置文件cdr.conf和创建后台线程do_cdr */ if (ast_cdr_engine_init()) { printf(term_quit()); exit(1); } /*用来创建一个后台线程轮巡设备的状态,如果发生变化则及时通告*/ if (ast_device_state_engine_init()) { printf(term_quit()); exit(1); } /*注册rtp,rtcp,stun相关的CLI命令,然后调用ast_rtp_reload()读取配置文件rtp.conf,设置相关参数*/ ast_rtp_init(); /*注册udptl相关的CLI命令,然后调用ast_udptl_reload()读取配置文件udptl.conf,设置相关参数*/ ast_udptl_init(); /*注册core show image formats */ if (ast_image_init()) { printf(term_quit()); exit(1); } /*注册core show file formats */ if (ast_file_init()) { printf(term_quit()); exit(1); } /*注册dialplan相关的CLI命令,然后调用ast_register_application来注册所有的app */ if (load_pbx()) { printf(term_quit()); exit(1); } /*注册与codec相关的CLI命令*/ if (init_framer()) { printf(term_quit()); exit(1); } /*注册与database相关的CLI命令,然后再注册两个管理命令DBGet和DBPut */ if (astdb_init()) { printf(term_quit()); exit(1); } /*读取配置文件enum.conf,初始化支持ENUM(e164)的子系统*/ if (ast_enum_init()) { printf(term_quit()); exit(1); } /* load_modules(0)加载所有其它需要加载的动态链接库*/ if (load_modules(0)) { printf(term_quit()); exit(1); } dnsmgr_start_refresh(); /* We might have the option of showing a console, but for now just do nothing... */ if (ast_opt_console && !option_verbose) ast_verbose(" ]/n"); if (option_verbose || ast_opt_console) ast_verbose(term_color(tmp, "Asterisk Ready./n", COLOR_BRWHITE, COLOR_BLACK, sizeof(tmp))); if (ast_opt_no_fork) consolethread = pthread_self(); /*创建管道*/ if (pipe(sig_alert_pipe)) sig_alert_pipe[0] = sig_alert_pipe[1] = -1; ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED); pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); #ifdef __AST_DEBUG_MALLOC __ast_mm_init(); #endif time(&ast_startuptime); /*注册asterisk相关的命令,比如stop,restart,halt等等*/ ast_cli_register_multiple(cli_asterisk, sizeof(cli_asterisk) / sizeof(struct ast_cli_entry)); if (ast_opt_console) { /* Console stuff now... */ /* Register our quit function */ char title[256]; pthread_attr_t attr; pthread_t dont_care; /*创建线程,轮询上面创建的sig_alert_pipe管道*/ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ast_pthread_create(&dont_care, &attr, monitor_sig_flags, NULL); pthread_attr_destroy(&attr); set_icon("Asterisk"); snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %ld)", hostname, (long)ast_mainpid); set_title(title); /*接收和处理控制台命令*/ for (;;) { buf = (char *)el_gets(el, &num); if (!buf && write(1, "", 1) < 0) goto lostterm; if (buf) { if (buf[strlen(buf)-1] == '/n') buf[strlen(buf)-1] = '/0'; consolehandler((char *)buf); } else if (ast_opt_remote && (write(STDOUT_FILENO, "/nUse EXIT or QUIT to exit the asterisk console/n", strlen("/nUse EXIT or QUIT to exit the asterisk console/n")) < 0)) { /* Whoa, stdout disappeared from under us... Make /dev/null's */ int fd; fd = open("/dev/null", O_RDWR); if (fd > -1) { dup2(fd, S |