`
free_bird816
  • 浏览: 212287 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

Asterisk内核框架

阅读更多

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

分享到:
评论

相关推荐

    最全Asterisk代码学习笔记

    3. **内核框架**:Asterisk的内核负责处理基本的通话处理和信号处理,包括SIP、IAX2等通信协议的解析和处理。它还包含事件循环机制,用于处理异步事件。 4. **配置文件参数读取**:Asterisk通过读取配置文件(如`...

    Newest Linux Rhino Drivers-开源

    Rhino Linux驱动程序是DAHHI框架的一部分,它为Rhino电话卡提供了必要的驱动支持,使Linux系统能够识别和控制该硬件。开源软件的特性意味着这些驱动程序的源代码对公众开放,允许开发者和社区成员审查、改进和贡献...

    拟阵约束下最大化子模函数的模型及其算法的一种熵聚类方法.pdf

    拟阵约束下最大化子模函数的模型及其算法的一种熵聚类方法.pdf

    电力市场领域中基于CVaR风险评估的省间交易商最优购电模型研究与实现

    内容概要:本文探讨了在两级电力市场环境中,针对省间交易商的最优购电模型的研究。文中提出了一个双层非线性优化模型,用于处理省内电力市场和省间电力交易的出清问题。该模型采用CVaR(条件风险价值)方法来评估和管理由新能源和负荷不确定性带来的风险。通过KKT条件和对偶理论,将复杂的双层非线性问题转化为更易求解的线性单层问题。此外,还通过实际案例验证了模型的有效性,展示了不同风险偏好设置对购电策略的影响。 适合人群:从事电力系统规划、运营以及风险管理的专业人士,尤其是对电力市场机制感兴趣的学者和技术专家。 使用场景及目标:适用于希望深入了解电力市场运作机制及其风险控制手段的研究人员和技术开发者。主要目标是为省间交易商提供一种科学有效的购电策略,以降低风险并提高经济效益。 其他说明:文章不仅介绍了理论模型的构建过程,还包括具体的数学公式推导和Python代码示例,便于读者理解和实践。同时强调了模型在实际应用中存在的挑战,如数据精度等问题,并指出了未来改进的方向。

    MATLAB/Simulink平台下四机两区系统风储联合调频技术及其高效仿真实现

    内容概要:本文探讨了在MATLAB/Simulink平台上针对四机两区系统的风储联合调频技术。首先介绍了四机两区系统作为经典的电力系统模型,在风电渗透率增加的情况下,传统一次调频方式面临挑战。接着阐述了风储联合调频技术的应用,通过引入虚拟惯性控制和下垂控制策略,提高了系统的频率稳定性。文章展示了具体的MATLAB/Simulink仿真模型,包括系统参数设置、控制算法实现以及仿真加速方法。最终结果显示,在风电渗透率为25%的情况下,通过风储联合调频,系统频率特性得到显著提升,仿真时间缩短至5秒以内。 适合人群:从事电力系统研究、仿真建模的技术人员,特别是关注风电接入电网稳定性的研究人员。 使用场景及目标:适用于希望深入了解风储联合调频机制及其仿真实现的研究人员和技术开发者。目标是掌握如何利用MATLAB/Simulink进行高效的电力系统仿真,尤其是针对含有高比例风电接入的复杂场景。 其他说明:文中提供的具体参数配置和控制算法有助于读者快速搭建类似的仿真环境,并进行相关研究。同时强调了参考文献对于理论基础建立的重要性。

    永磁同步电机无感控制:高频方波注入与滑膜观测器结合实现及其应用场景

    内容概要:本文介绍了永磁同步电机(PMSM)无感控制技术,特别是高频方波注入与滑膜观测器相结合的方法。首先解释了高频方波注入法的工作原理,即通过向电机注入高频方波电压信号,利用电机的凸极效应获取转子位置信息。接着讨论了滑膜观测器的作用,它能够根据电机的电压和电流估计转速和位置,具有较强的鲁棒性。两者结合可以提高无传感器控制系统的稳定性和精度。文中还提供了具体的Python、C语言和Matlab代码示例,展示了如何实现这两种技术。此外,简要提及了正弦波注入的相关论文资料,强调了其在不同工况下的优势。 适合人群:从事电机控制系统设计的研发工程师和技术爱好者,尤其是对永磁同步电机无感控制感兴趣的读者。 使用场景及目标:适用于需要减少传感器依赖、降低成本并提高系统可靠性的情况,如工业自动化设备、电动汽车等领域的电机控制。目标是掌握高频方波注入与滑膜观测器结合的具体实现方法,应用于实际工程项目中。 其他说明:文中提到的高频方波注入和滑膜观测器的结合方式,不仅提高了系统的性能,还在某些特殊情况下表现出更好的适应性。同时,附带提供的代码片段有助于读者更好地理解和实践这一技术。

    MATLAB中扩展卡尔曼滤波与双扩展卡尔曼滤波在电池参数辨识的应用

    内容概要:本文深入探讨了MATLAB中扩展卡尔曼滤波(EKF)和双扩展卡尔曼滤波(DEKF)在电池参数辨识中的应用。首先介绍了EKF的基本原理和代码实现,包括状态预测和更新步骤。接着讨论了DEKF的工作机制,即同时估计系统状态和参数,解决了参数和状态耦合估计的问题。文章还详细描述了电池参数辨识的具体应用场景,特别是针对电池管理系统中的荷电状态(SOC)估计。此外,提到了一些实用技巧,如雅可比矩阵的计算、参数初始值的选择、数据预处理方法等,并引用了几篇重要文献作为参考。 适合人群:从事电池管理系统开发的研究人员和技术人员,尤其是对状态估计和参数辨识感兴趣的读者。 使用场景及目标:适用于需要精确估计电池参数的实际项目,如电动汽车、储能系统等领域。目标是提高电池管理系统的性能,确保电池的安全性和可靠性。 其他说明:文章强调了实际应用中的注意事项,如数据处理、参数选择和模型优化等方面的经验分享。同时提醒读者关注最新的研究成果和技术进展,以便更好地应用于实际工作中。

    基于三菱FX3U PLC和威纶通触摸屏的分切机上下收放卷张力控制系统设计

    内容概要:本文详细介绍了在无电子凸轮功能情况下,利用三菱FX3U系列PLC和威纶通触摸屏实现分切机上下收放卷张力控制的方法。主要内容涵盖硬件连接、程序框架设计、张力检测与读取、PID控制逻辑以及触摸屏交互界面的设计。文中通过具体代码示例展示了如何初始化寄存器、读取张力传感器数据、计算张力偏差并实施PID控制,最终实现稳定的张力控制。此外,还讨论了卷径计算、速度同步控制等关键技术点,并提供了现场调试经验和优化建议。 适合人群:从事自动化生产设备维护和技术支持的专业人士,尤其是熟悉PLC编程和触摸屏应用的技术人员。 使用场景及目标:适用于需要对分切机进行升级改造的企业,旨在提高分切机的张力控制精度,确保材料切割质量,降低生产成本。通过本方案可以实现±3%的张力控制精度,满足基本生产需求。 其他说明:本文不仅提供详细的程序代码和硬件配置指南,还分享了许多实用的调试技巧和经验,帮助技术人员更好地理解和应用相关技术。

    基于S7系列PLC与组态王的三泵变频恒压供水系统设计与实现

    内容概要:本文详细介绍了一种基于西门子S7-200和S7-300 PLC以及组态王软件的三泵变频恒压供水系统。主要内容涵盖IO分配、接线图原理图、梯形图程序编写和组态画面设计四个方面。通过合理的硬件配置和精确的编程逻辑,确保系统能够在不同负载情况下保持稳定的供水压力,同时实现节能和延长设备使用寿命的目标。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉PLC编程和组态软件使用的专业人士。 使用场景及目标:适用于需要稳定供水的各种场合,如住宅小区、工厂等。目标是通过优化控制系统,提升供水效率,减少能源消耗,并确保系统的可靠性和安全性。 其他说明:文中提供了详细的实例代码和调试技巧,帮助读者更好地理解和实施该项目。此外,还分享了一些实用的经验教训,有助于避免常见的错误和陷阱。

    三相三线制SVG/STATCOM的Simulink仿真建模与控制策略解析

    内容概要:本文详细介绍了三相三线制静止无功发生器(SVG/STATCOM)在Simulink中的仿真模型设计与实现。主要内容涵盖ip-iq检测法用于无功功率检测、dq坐标系下的电流解耦控制、电压电流双闭环控制系统的设计、SVPWM调制技术的应用以及具体的仿真参数设置。文中不仅提供了理论背景,还展示了具体的Matlab代码片段,帮助读者理解各个控制环节的工作原理和技术细节。此外,文章还讨论了实际调试中遇到的问题及解决方案,强调了参数调整的重要性。 适合人群:从事电力系统自动化、电力电子技术研究的专业人士,特别是对SVG/STATCOM仿真感兴趣的工程师和研究人员。 使用场景及目标:适用于希望深入了解SVG/STATCOM工作原理并掌握其仿真方法的研究人员和工程师。目标是在实践中能够正确搭建和优化SVG/STATCOM的仿真模型,提高无功补偿的效果。 其他说明:文章提供了丰富的实例代码和调试技巧,有助于读者更好地理解和应用所学知识。同时,文中提及的一些经验和注意事项来源于实际项目,具有较高的参考价值。

    基于SIMULINK的风力机发电效率建模探究.pdf

    基于SIMULINK的风力机发电效率建模探究.pdf

    CarSim与Simulink联合仿真:基于MPC模型预测控制实现智能超车换道

    内容概要:本文介绍了如何将CarSim的动力学模型与Simulink的智能算法相结合,利用模型预测控制(MPC)实现车辆的智能超车换道。主要内容包括MPC控制器的设计、路径规划算法、联合仿真的配置要点以及实际应用效果。文中提供了详细的代码片段和技术细节,如权重矩阵设置、路径跟踪目标函数、安全超车条件判断等。此外,还强调了仿真过程中需要注意的关键参数配置,如仿真步长、插值设置等,以确保系统的稳定性和准确性。 适合人群:从事自动驾驶研究的技术人员、汽车工程领域的研究人员、对联合仿真感兴趣的开发者。 使用场景及目标:适用于需要进行自动驾驶车辆行为模拟的研究机构和企业,旨在提高超车换道的安全性和效率,为自动驾驶技术研发提供理论支持和技术验证。 其他说明:随包提供的案例文件已调好所有参数,可以直接导入并运行,帮助用户快速上手。文中提到的具体参数和配置方法对于初学者非常友好,能够显著降低入门门槛。

    基于MATLAB的信号与系统实验:常见信号生成、卷积积分、频域分析及Z变换详解

    内容概要:本文详细介绍了利用MATLAB进行信号与系统实验的具体步骤和技术要点。首先讲解了常见信号(如方波、sinc函数、正弦波等)的生成方法及其注意事项,强调了时间轴设置和参数调整的重要性。接着探讨了卷积积分的两种实现方式——符号运算和数值积分,指出了各自的特点和应用场景,并特别提醒了数值卷积时的时间轴重构和步长修正问题。随后深入浅出地解释了频域分析的方法,包括傅里叶变换的符号计算和快速傅里叶变换(FFT),并给出了具体的代码实例和常见错误提示。最后阐述了离散时间信号与系统的Z变换分析,展示了如何通过Z变换将差分方程转化为传递函数以及如何绘制零极点图来评估系统的稳定性。 适合人群:正在学习信号与系统课程的学生,尤其是需要完成相关实验任务的人群;对MATLAB有一定基础,希望通过实践加深对该领域理解的学习者。 使用场景及目标:帮助学生掌握MATLAB环境下信号生成、卷积积分、频域分析和Z变换的基本技能;提高学生解决实际问题的能力,避免常见的编程陷阱;培养学生的动手能力和科学思维习惯。 其他说明:文中不仅提供了详细的代码示例,还分享了许多实用的小技巧,如如何正确保存实验结果图、如何撰写高质量的实验报告等。同时,作者以幽默风趣的语言风格贯穿全文,使得原本枯燥的技术内容变得生动有趣。

    【KUKA 机器人移动编程】:mo2_motion_ptp_en.ppt

    KUKA机器人相关文档

    永磁同步电机(PMSM)无传感器控制:I/F启动与滑模观测器结合的技术实现及应用

    内容概要:本文详细介绍了无传感器永磁同步电机(PMSM)控制技术,特别是针对低速和中高速的不同控制策略。低速阶段采用I/F控制,通过固定电流幅值和斜坡加速的方式启动电机,确保平稳启动。中高速阶段则引入滑模观测器进行反电动势估算,从而精确控制电机转速。文中还讨论了两者之间的平滑切换逻辑,强调了参数选择和调试技巧的重要性。此外,提供了具体的伪代码示例,帮助读者更好地理解和实现这一控制方案。 适合人群:从事电机控制系统设计的研发工程师和技术爱好者。 使用场景及目标:适用于需要降低成本并提高可靠性的应用场景,如家用电器、工业自动化设备等。主要目标是掌握无传感器PMSM控制的基本原理及其优化方法。 其他说明:文中提到的实际案例和测试数据有助于加深理解,同时提醒开发者注意硬件参数准确性以及调试过程中可能出现的问题。

    智能家居与物联网培训材料.ppt

    智能家居与物联网培训材料.ppt

    Matlab实现车辆路径规划:基于TSP、CVRP、CDVRP、VRPTW的四大算法解析及应用

    内容概要:本文详细介绍了使用Matlab解决车辆路径规划问题的四种经典算法:TSP(旅行商问题)、CVRP(带容量约束的车辆路径问题)、CDVRP(带容量和距离双重约束的车辆路径问题)和VRPTW(带时间窗约束的车辆路径问题)。针对每个问题,文中提供了具体的算法实现思路和关键代码片段,如遗传算法用于TSP的基础求解,贪心算法和遗传算法结合用于CVRP的路径分割,以及带有惩罚函数的时间窗约束处理方法。此外,还讨论了性能优化技巧,如矩阵运算替代循环、锦标赛选择、2-opt局部优化等。 适合人群:具有一定编程基础,尤其是对物流调度、路径规划感兴趣的开发者和技术爱好者。 使用场景及目标:适用于物流配送系统的路径优化,旨在提高配送效率,降低成本。具体应用场景包括但不限于外卖配送、快递运输等。目标是帮助读者掌握如何利用Matlab实现高效的路径规划算法,解决实际业务中的复杂约束条件。 其他说明:文中不仅提供了详细的代码实现,还分享了许多实践经验,如参数设置、数据预处理、异常检测等。建议读者在实践中不断尝试不同的算法组合和优化策略,以应对更加复杂的实际问题。

    软考网络工程师2010-2014真题及答案

    软考网络工程师2010-2014真题及答案完整版 全国计算机软考 适合软考中级人群

    基于单片机的酒驾检测设计(51+1602+PCF8591+LED+BZ+KEY3)#0055

    包括:源程序工程文件、Proteus仿真工程文件、论文材料、配套技术手册等 1、采用51/52单片机作为主控芯片; 2、采用1602液晶显示:测量酒精值、酒驾阈值、醉驾阈值; 3、采用PCF8591进行AD模数转换; 4、LED指示:正常绿灯、酒驾黄灯、醉驾红灯; 5、可通过按键修改酒驾醉驾阈值;

Global site tag (gtag.js) - Google Analytics