一、内核初始化。
从 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里进行callerid的DTMF检测*/
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目录下面的变量,v 包含变量名及变量值,即可访问到真实的设置值。(!strcasecmp(v->name, "astctlgroup")),
文件解析完毕后调用ast_category_destroy释放内存。
接下来 设置fd上限,对于select系统调用,linux上一个进程最多打开1024个fd。
sigaction(SIGCHLD, &child_handler, NULL); 注册 信号处理函数。
/* custom config setup */
注册控制台命令
register_config_cli();
//读取extconfig.conf文件,映射realtime到具体数据库引擎,表。。。
read_config_maps();
此函数内部,解析extconfig.conf文件,解析settings目录,
找数据库引擎,数据库表。
这里,如果你的数据库表为sipfriends则会提示不要用这个表,同时用sipusers及sippeers表,替换。
最终调用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(<hread, 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
初始化ssl,xml_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,处理启动时执行的命令,同时接受控制台命令。
标签:初始化,struct,Kernel,ast,Asterisk,analysis,init,引擎,exit From: https://blog.51cto.com/u_15747257/5948832