3. AppInit
用于AppInit函数有点长,咱们把函数拆开一点一点的看,我将该函数分成了5个部分,并通过数字来标识当前函数代码块。
// 1.
// 函数AppInit() 位置:src/bitcoind.cpp
boost::thread_group threadGroup; //定义线程组,用来管理线程
CScheduler scheduler;
bool fRet = false;
定义了一个线程组用来管理线程。CScheduler
这个类声明在/src/scheduler.h
。我们来看下CScheduler
中的两个函数
//类:CScheduler 文件位置:/src/scheduler.h
// Convenience method: call f once deltaSeconds from now
void scheduleFromNow(Function f, int64_t deltaSeconds);
// Another convenience method: call f approximately
// every deltaSeconds forever, starting deltaSeconds from now.
// To be more precise: every time f is finished, it
// is rescheduled to run deltaSeconds later. If you
// need more accurate scheduling, don't use this method.
void scheduleEvery(Function f, int64_t deltaSeconds);
scheduleFromNow()
作用是过一段时间执行Function f
。scheduleEvery()
作用是每隔一段时间来执行Function f
。可以单独新建线程来处理业务逻辑,不会干扰主线程。
CScheduler
的作用主要是用来管理后台任务
// 2.
//函数:AppInit() 位置:/src/bitcoind.cpp
gArgs.ParseParameters(argc, argv);
// Process help and version before taking care about datadir
if (IsArgSet("-?") || IsArgSet("-h") || IsArgSet("-help") || IsArgSet("-version"))
{
std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";
if (IsArgSet("-version"))
{
strUsage += FormatParagraph(LicenseInfo());
}
else
{
strUsage += "\n" + _("Usage:") + "\n" +
" bitcoind [options] " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";
strUsage += "\n" + HelpMessage(HMM_BITCOIND);
}
fprintf(stdout, "%s", strUsage.c_str());
return true;
}
gArgs
声明在/src/util.h
类型是ArgsManager
。函数ParseParameters(argc, argv)
解析bitcoind命令行输入的参数,并存入map中。函数定义在/src/util.cpp
。
上面的代码会将命令行参数为help或者version做显示处理。其他的命令忽略。
// 3.
try
{
// 检查文件目录的有效性
if (!boost::filesystem::is_directory(GetDataDir(false)))
{
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str());
return false;
}
try
{
// 读取配置文件
ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME));
} catch (const std::exception& e) {
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
return false;
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
SelectParams(ChainNameFromCommandLine());
} catch (const std::exception& e) {
fprintf(stderr, "Error: %s\n", e.what());
return false;
}
检查数据目录的正确性,数据目录的位置可以通过-datadir
来指定。文件目录主要存储了保存同步的区块信息,钱包信息,配置信息等等重要信息。
默认的数据目录位置:
Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
Mac: ~/Library/Application Support/Bitcoin
Unix: ~/.bitcoin
然后读取配置文件,配置文件默认的名称是bitcoin.conf存储在数据目录文件夹下 ~/.bitcoin/bitcoin.conf
。也可以通过-conf命令来指定配置文件的位置。当然这个文件也可以不设置。
// src/util.cpp
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
比特币网络主要有以下三种:
- main 主网络,如果不设置参数,默认连接到主网络
- testnet 测试网络,可以通过该网络来模拟主网络的操作,算力相对来说小一些。
- regtest 私有网络,供开发者开发调试
接下来通过ChainNameFromCommandLine
函数来获取当前要连接到哪个网络,之后通过SelectParams
函数来对该网络进行参数配置。
// src/chainparams.cpp
void SelectParams(const std::string& network)
{
SelectBaseParams(network);
globalChainParams = CreateChainParams(network);
}
SelectBaseParams
函数根据当前设置的网络来配置相应的RPC端口号。
- 主网络:nRPCPort = 8332
- 测试网络: nRPCPort = 18332
- 私有网络:nRPCPort = 18332
这些参数都分别存放在/src/chainparamsbase.cpp
文件的CBaseMainParams、CBaseTestNetParams、CBaseRegTestParams这三个类中
CreateChainParams
函数根据当前设置的网络来配置区块生成、共识参数、协议升级等重要参数。该参数的具体内容可以在/src/chainparams.cpp
文件的CMainParams类查看。
// 4.
// Error out when loose non-argument tokens are encountered on command line
//判断命令行参数是否正确,取第一个参数,如果是WIN32系统可以使用'-'或者'/',其他系统则是'-'
for (int i = 1; i < argc; i++) {
if (!IsSwitchChar(argv[i][0])) {
fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
exit(EXIT_FAILURE);
}
}
// 5.
// -server defaults to true for bitcoind but not for the GUI so do this here
gArgs.SoftSetBoolArg("-server", true);
// Set this early so that parameter interactions go to console
InitLogging();
InitParameterInteraction();
if (!AppInitBasicSetup())
{
// InitError will have been called with detailed error, which ends up on console
exit(EXIT_FAILURE);
}
if (!AppInitParameterInteraction())
{
// InitError will have been called with detailed error, which ends up on console
exit(EXIT_FAILURE);
}
if (!AppInitSanityChecks())
{
// InitError will have been called with detailed error, which ends up on console
exit(EXIT_FAILURE);
}
if (gArgs.GetBoolArg("-daemon", false))
{
#if HAVE_DECL_DAEMON
fprintf(stdout, "Bitcoin server starting\n");
// Daemonize
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
return false;
}
#else
fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
return false;
#endif // HAVE_DECL_DAEMON
}
// Lock data directory after daemonization
if (!AppInitLockDataDirectory())
{
// If locking the data directory failed, exit immediately
exit(EXIT_FAILURE);
}
fRet = AppInitMain(threadGroup, scheduler);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
} catch (...) {
PrintExceptionContinue(nullptr, "AppInit()");
}
if (!fRet)
{
Interrupt(threadGroup);
threadGroup.join_all();
} else {
WaitForShutdown(&threadGroup);
}
Shutdown();
return fRet;
}
用于这段代码内容较多,所以在这里做一个简单的讲解。后续内容会做一个详细讲解。
gArgs.SoftSetBoolArg("-server", true);
将 -server
参数设置为1
。先判断该参数是否存在,如果存在返回false,不存在返回true。
// src/util.cpp
// 如果-server存在,则返回false。不存在则返回true。
bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
return false;
ForceSetArg(strArg, strValue);
return true;
}
//当fValue设置为true时,将-server设置成1。否则设置为0。
bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
{
if (fValue)
return SoftSetArg(strArg, std::string("1"));
else
return SoftSetArg(strArg, std::string("0"));
}
下面的函数大概作用为:
- InitLogging() 初始化日志系统
- InitParameterInteraction() 初始化网络参数规则
- AppInitBasicSetup() 设置消息格式及处理回调逻辑。
- AppInitParameterInteraction() 设置区块链运行参数
- AppInitSanityChecks() 对依赖库进行检查
- gArgs.GetBoolArg("-daemon", false)
- AppInitMain() 主程序初始化
- ShutDown() 关闭进程
网友评论