目标:
通过分析代码的启动流程,可以了解各功能模块的启动顺序,以及模块间的调用、依赖关系。为后续的学习、分析达到一种庖丁解牛的效果。
内容:
SRS服务器的入口函数(mian函数)在srs_main_server.cpp文件里,下面的代码为了方便理解,只列出了函数中关键的处理过程。
int main(int argc, char** argv) {
srs_error_t err = do_main(argc, argv); // srs_error_t是一个包含错误信息的对象指针
srs_freep(err); // 释放错误对象
}
srs_error_t do_main(int argc, char** argv)
{
srs_thread_initialize(); // 此函数内部创建_srs_log、_srs_context、_srs_config全局对象
// 并调用StateThreads库的初始化函数,创建Idle协程(负责epoll和轮询定时器)
// 创建全局管理对象SrsHybridServer、SrsLiveSourceManager、SrsRtcSourceManager、SrsResourceManager
_srs_context->set_id(_srs_context->generate_id()); // 生成一个随机字符串,
// 并关联到当前原始协程
_srs_config->parse_options(argc, argv); // 读取配置文件
SrsFileLog::initialize(); // 根据配置文件,初始化日志模块的输出方向(文件或控制台)和输出级别
run_directly_or_daemon(); // 此函数内部判断是否需要以后台模式运行,并启动全部服务
}
函数run_directly_or_daemon(),首先判断配置文件的设置,如果是后台运行模式,则通过连续调用fork启动孙进程,同时子进程退出,孙进程变为孤儿进程,因为Linux系统会让init进程接管孤儿进程,当孤儿进程结束时init进程会自动发现并清理,最终,防止出现僵尸进程。
srs_error_t run_directly_or_daemon() {
...... // 根据配置文件的设置,如果需要后台运行,则通过连续调用fork进入孙进程
// 子进程创建完孙子进程后就退出,则孙进程直接变为一个孤儿进程,
// Linux系统处理孤儿进程的方式是让init进程接管孤儿进程,而init进程会自动发现并清理属于自己的僵尸进程
run_hybrid_server(); // 启动服务
}
接下来,在run_hybrid_server()函数中注册各种流媒体服务对象
srs_error_t run_hybrid_server()
{
// _srs_hybrid指向一个全局SrsHybridServer对象
// 实际工作对象SrsServerAdapter和RtcServerAdapter被注入到SrsHybridServer对象内部
_srs_hybrid->register_server(new SrsServerAdapter()); // 创建并注册RTMP服务
#ifdef SRS_SRT
_srs_hybrid->register_server(new SrtServerAdapter()); // 创建并注册SRT服务
#endif
_srs_hybrid->register_server(new RtcServerAdapter()); // 创建并注册WebRTC服务
_srs_hybrid->initialize(); // 此函数内部分别启动几个周期定时器,并以遍历方式调用上面已注册服务器的initialize()函数
_srs_circuit_breaker->initialize(); // 此模块用于防止服务器过载,实现过载保护
_srs_hybrid->run(); // 此函数内部以遍历方式调用上面已注册服务器的run接口,并在最后
// 调用srs_usleep(SRS_UTIME_NO_TIMEOUT)使当前的原始协程进入休眠状态
_srs_hybrid->stop(); // 如果执行到这里,表示整个服务已结束,程序即将退出
}
最终,通过SrsHybridServer::run()函数启动各个流媒体服务模块,并通过srs_usleep()函数阻塞当前的原始协程
srs_error_t SrsHybridServer::run()
{
srs_error_t err = srs_success;
vector<ISrsHybridServer*>::iterator it;
for (it = servers.begin(); it != servers.end(); ++it) {
ISrsHybridServer* server = *it;
// 遍历调用已注册服务器模块的run()接口,将各种服务模块运行起来
if ((err = server->run()) != srs_success) {
return srs_error_wrap(err, "run server");
}
}
// Wait for all server to quit.
srs_usleep(SRS_UTIME_NO_TIMEOUT); // 最终,原始协程阻塞在这里,整个服务器启动完成
return err;
}
总结:
1、通过学习SRS的启动流程,我们对SRS有了进一步的认识,如果对其中state-threads库感兴趣,可以从srs_thread_initialize()函数内部开始深入分析;如果对配置文件的处理比较感兴趣,可以从SrsConfig::parse_options()函数内部开始分析;如果对服务器的后台启动方式感兴趣,可以关注run_directly_or_daemon()函数。
2、最终,我们可以很自然的看到SRS4.0包括3个主要的服务模块:SrsServerAdapter、SrtServerAdapter、RtcServerAdapter。我们可以根据需要对每个服务模块分别研究。