Asterisk Kernel analysis 1

一、内核初始化。

 

 main入口。Asterisk.c  进入内核。

 

 

对于重启,记录上一次main函数传过来的命令,

 

/* Remember original args for restart */

      if (argc > ARRAY_LEN(_argv) - 1) {

           fprintf(stderr, "Truncating argument size to %d/n", (int)ARRAY_LEN(_argv) - 1);

           argc = ARRAY_LEN(_argv) - 1;

      }

      for (x = 0; x < argc; x++)

           _argv[x] = argv[x];

      _argv[x] = NULL;

 

获取主机名,失败则设置为unknown.

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

           ast_copy_string(hostname, "<Unknown>", sizeof(hostname));

      ast_mainpid = getpid();

 

初始化各种语音编码,

ast_ulaw_init();

      ast_alaw_init();

/*FFT逆变换(傅立叶逆变换)做一些初始化,用于在zaptel里进行calleridDTMF检测*/
  

      callerid_init();

      ast_builtins_init();

      ast_utils_init();

      tdd_init();

ast_tps_init(); // 启动业务处理引擎,1.8新增,

      ast_fd_init();

 

解析启动参数

/* Check for options */

      while ((c = getopt(argc, argv, "mtThfdvVqprRgciInx: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;

 

 

 

注册控制台打印函数,,,

 

ast_readconfig(); //读取主配置文件asterisk.conf并根据配置初始化系统各种参数。

 

这里对asterisk配置文件的读取及解析实现做一下解释。

 

Asterisk配置文件以.conf结尾,当然了,解析文件引擎不考虑文件格式。

 

这里有两个结构,一个为 struct ast_config

 

struct ast_config {

      struct ast_category *root;

      struct ast_category *last;

      struct ast_category *current;

      struct ast_category *last_browse;         /*!< used to cache the last category supplied via category_browse */

      int include_level;

      int max_include_level;

    struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */

};

 

此结构读取一个文件,同时文件可以包含另一个文件,按目录保存,这里的目录用【xx】格式表示,

 asterisk.conf文件的[options]目录

;verbose = 3

;debug = 3

;alwaysfork = yes           ; Same as -F at startup.

nofork = yes 

单个目录用结构struct ast_category表示, 每个目录下包含注释,变量,

如上面的;verbose = 3

;debug = 3

即为注释,用struct ast_comment表示,

nofork = yes   

为变量, struct ast_variable 表示,是个名值对。

 

Asterisk配置文件解析过程,

1.调用ast_config_load2(文件名,falgs)读取文件,返回指向ast_config

的指针 cfg

2.调用ast_variable_browse(cfg, "catogery_name"),访问文件的一个目录,如上面的【options】即为一个目录,ast_variable_browse()返回目录下的一个名值对,用结构ast_variable表示,

struct ast_variable *v;

for (v = ast_variable_browse(cfg, "directories"); v; v = v->next)

循环遍历directories目录下面的变量,包含变量名及变量值,即可访问到真实的设置值。(!strcasecmp(v->name, "astctlgroup"))

 

文件解析完毕后调用 ast_category_destroy释放内存。

 

 

接下来 设置fd上限,对于select系统调用,linux上一个进程最多打开1024fd

 

sigaction(SIGCHLD, &child_handler, NULL); 注册 信号处理函数。

 

 

/* custom config setup */

注册控制台命令

register_config_cli();

//读取extconfig.conf文件,映射realtime到具体数据库引擎,表。。。

 

read_config_maps();

此函数内部,解析extconfig.conf文件,解析settings目录,

找数据库引擎,数据库表。

这里,如果你的数据库表为sipfriends则会提示不要用这个表,同时用sipuserssippeers表,替换。

最终调用append_mapping 完成数据库表的映射。映射关系用结构struct ast_config_map 表示,此结构内部保存数据库引擎,数据库表名字等。

 

映射玩之后 接下来调用ast_tryconnect连接远程Asterisk Server.

 

启动子进程或线程处理日历功能,1.8新增。

ast_pthread_create_detached(&dont_care, NULL, canary_thread, NULL);此线程每隔一分钟扫描一次日历文件。

 

/* Kill the canary when we exit */

         ast_register_atexit(canary_exit);

注册退出处理函数。struct ast_atexit 描述了注册信息,所有注册退出回调函数保存到atexits链表中。

struct ast_atexit {

     void (*func)(void);

     AST_RWLIST_ENTRY(ast_atexit) list;

};

static AST_RWLIST_HEAD_STATIC(atexits, ast_atexit);

 

接下来执行

if (ast_event_init()) {

         printf("%s", term_quit());

         exit(1);

     }

初始化asterisk事件引擎。这里事件引擎为新增内容,系统事件包括响铃,接听,等待等等一系列事件,事件包括类型,对应回调等,事件跟业务引擎结合。

 

接下来调用ast_makesocket 启动服务器登陆监听socket,处理远程cli连接。注册远程cli处理回调函数,

/*创建用于和控制台交互的服务器端socket接口*/
 

if (ast_register_verbose(network_verboser)) {

         ast_log(LOG_WARNING, "Unable to register network verboser?/n");

     }

 

绑定后创建线程处理所有客户端连接

ast_pthread_create_background(&lthread, NULL, listener, NULL);,

此线程负责接收所有连接,具体读写则创建线程netconsole处理。

 

/*设置种子并初始化随机数发生器*/
 
        srand((unsigned int) getpid() + (unsigned int) time(NULL));
 
        initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool));
  

if (init_logger()) {        /* Start logging subsystem */

         printf("%s", term_quit());

         exit(1);

     }

 

初始化logger系统。创建线程logger_thread,处理message链表。取出一条消息,调用logger_print_normal 处理此消息,根据消息类型分派给不同函数处理。如控制台,文件,syslog。。。

线程存储初始化,调试时提供线程信息

threadstorage_init();

Asterisk大对象类型初始化,实际上此类型为简化结构的内存分配及回收,通过引用计数来控制内存的回收,分配内存时即指定回调函数,这样不必考虑何时释放内存,同时,大对象的查找等通过hash查找,效率很高。

     astobj2_init();

 

下面函数

ao2_t_alloc(arg1, arg2, arg3)

ao2_t_ref(arg1,arg2,arg3)

ao2_t_container_alloc(arg1,arg2,arg3,arg4)

ao2_t_link(arg1, arg2, arg3)

ao2_t_unlink(arg1, arg2, arg3)

ao2_t_callback(arg1,arg2,arg3,arg4,arg5)

ao2_t_find(arg1,arg2,arg3,arg4)

ao2_t_iterator_next(arg1, arg2)

提供了操作大对象的方式,包括内存分配,引用计数增减,把对象放入哈希表,查找,遍历对象。

同时提供了debug 大对象接口REF_DEBUG。这些函数在sip协议栈及队列实现中都有所体现。

接下来初始化自服务引擎,此引擎的作用是监听channel上的事件,比如坐席接听后,此引擎负责监听双方的按键,根据按键走不通流程。

     ast_autoservice_init();

初始化定时器引擎。。,1.6开始才有,以前都用dahdi提供定时器,如meetme,提供三个实现

res_timing_pthread.so

res_timing_dahdi.so res_timing_timerfd.so (Beginning with Asterisk 1.6.2)

 

ast_timing_init

 

接下来if (ast_ssl_init()) {

         printf("%s", term_quit());

         exit(1);

     }

 

#ifdef AST_XML_DOCS

     /* Load XML documentation. */

     ast_xmldoc_load_documentation();

#endif

初始化sslxml_doc系统。

 

ast_channels_init();初始化channel 内存池,

 

if ((moduleresult = load_modules(1))) {     /* Load modules, pre-load only */

         printf("%s", term_quit());

         exit(moduleresult == -2 ? 2 : 1);

     }

 

加载模块,

int dnsmgr_init(void)//初始化dns管理引擎,创建调度器。

 

ast_http_init();    初始化asterisk http引擎。

if (init_manager()) {

         printf("%s", term_quit());

         exit(1);

     } 

初始化ami引擎,

if (ast_cdr_engine_init()) {

         printf("%s", term_quit());

         exit(1);

     }

Cdr引擎初始化。

 

if (ast_cel_engine_init()) {

         printf("%s", term_quit());

         exit(1);

     }

 

Cel引擎初始化。

if (ast_device_state_engine_init()) {

         printf("%s", term_quit());

         exit(1);

     }

设备状态引擎初始化。

设备有下面状态。

/*! /brief Device state strings for printing */

static const char * const devstatestring[][2] = {

       { /* 0 AST_DEVICE_UNKNOWN */     "Unknown",     "UNKNOWN"     }, /*!< Valid, but unknown state */

       { /* 1 AST_DEVICE_NOT_INUSE */   "Not in use",  "NOT_INUSE"   }, /*!< Not used */

       { /* 2 AST_DEVICE IN USE */      "In use",      "INUSE"       }, /*!< In use */

       { /* 3 AST_DEVICE_BUSY */        "Busy",        "BUSY"        }, /*!< Busy */

       { /* 4 AST_DEVICE_INVALID */     "Invalid",     "INVALID"     }, /*!< Invalid - not known to Asterisk */

       { /* 5 AST_DEVICE_UNAVAILABLE */ "Unavailable", "UNAVAILABLE" }, /*!< Unavailable (not registered) */

       { /* 6 AST_DEVICE_RINGING */     "Ringing",     "RINGING"     }, /*!< Ring, ring, ring */

       { /* 7 AST_DEVICE_RINGINUSE */   "Ring+Inuse",  "RINGINUSE"   }, /*!< Ring and in use */

       { /* 8 AST_DEVICE_ONHOLD */      "On Hold",      "ONHOLD"      }, /*!< On Hold */

};

 

关于设备状态,可以用来做全局排队,

int ast_device_state_engine_init(void)

{

       ast_cond_init(&change_pending, NULL);

       if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {

              ast_log(LOG_ERROR, "Unable to start device state change thread./n");

              return -1;

       }

 

       return 0;

}

 

初始化设备状态引擎创建一个线程,do_devstate_changes,此线程扫描设备状态队列,然后调用

do_state_change(current->device); 处理此设备状态,do_state_change 内部,调用_ast_device_state,获取设备的当前状态,获取状态后调用devstate_event(device, state);发射设备状态改变事件。_ast_device_state 内部获取状态有两种方式,一种为通道提供获取设备状态回调函数,另一种为通用状态获取。

 

 

if (load_pbx()) {

              printf("%s", term_quit());

              exit(1);

       }

初始化pbx引擎。注册pbx内嵌函数,如ast_answer,ast_hangup等。

 

/*! /brief Load indications module */

int ast_indications_init(void)

初始化indications引擎,此模块是一些时区,语言类型的初始化。

 

ast_features_init(); features引擎初始化,此模块为

 

int astdb_init(void) 初始化astdb,本地数据库引擎,开启一个线程处理数据库动作。

 

 

if (ast_enum_init()) {          //枚举引擎初始化。

              printf("%s", term_quit());

              exit(1);

       }

 

       if (ast_cc_init()) {   call completetion 模块初始化,1.8新增。

              printf("%s", term_quit());

              exit(1);

       }

 

 

if ((moduleresult = load_modules(0))) {        /* Load modules */

              printf("%s", term_quit());

              exit(moduleresult == -2 ? 2 : 1);

       }

 

加载动态模块,包括sip协议栈,队列,等等一些列可配置模块。

 

 

ast_stun_init(); 初始化 nat穿透模块。

run_startup_commands(); 解析cli.conf,处理启动时执行的命令,同时接受控制台命令。

猜你喜欢

转载自coolwhy1.iteye.com/blog/1771690