上次读到src/bitcoind.cpp 中AppInit,大概的流程是
1,解析-?,-h,-help,-version,输出帮助和版本信息
2,解析-datadir参数,GetDataDir函数判定所指定数据目录是否合法
3,通过-conf参数,ReadConfigFile函数读取配置文件
4,通过-testnet和-regtest参数,ChainNameFromCommandLine函数设置的当前程序运行的网络
5,通过-printtoconsole等参数,InitLogging函数初始化日志记录以及打印方式
6,InitParameterInteraction函数初始化网络参数
7,AppInitBasicSetup函数注册相应的消息以及处理方式
8,AppInitParameterInteraction函数设置区块链运行参数
9,AppInitSanityChecks函数检查比特币运行时所需要的所有的库是否都运行正常
10,通过-daemon参数设置是否后台运行
11,AppInitMain函数运行主应用程序
12,启动失败中断操作和清理工作
AppInit函数代码如下:
代码如下:
54 //////////////////////////////////////////////////////////////////////////////
55 //
56 // Start
57 //
58 bool AppInit(int argc, char* argv[])
59 {
60 bool fRet = false;
61
62 //
63 // Parameters
64 //
65 // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
66 gArgs.ParseParameters(argc, argv);
67 //如果命令行参数是-?,-h,-help,-version的话首先构造版本和usage信息
68 // Process help and version before taking care about datadir
69 if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))
70 {
//构造版本字符串,例如:Bitcoin Core Daemon version v0.16.0rc2
71 std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";
72
//如果是-version,构造追加license信息
73 if (gArgs.IsArgSet("-version"))
74 {
75 strUsage += FormatParagraph(LicenseInfo());
76 }
//如果是-?,-h,-help,构造追加usage信息
77 else
78 {
79 strUsage += "\n" + _("Usage:") + "\n" +
80 " bitcoind [options] " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";
81
82 strUsage += "\n" + HelpMessage(HMM_BITCOIND);
83 }
84
85 fprintf(stdout, "%s", strUsage.c_str());
86 return true;
87 }
88
//检测数据目录
89 try
90 {
//解析-datadir参数,通过GetDataDir检测和处理-datadir参数指定的目录
91 if (!fs::is_directory(GetDataDir(false)))
92 {
93 fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
94 return false;
95 }
//通过-conf读取配置文件
96 try
97 {
98 gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
99 } catch (const std::exception& e) {
100 fprintf(stderr,"Error reading configuration file: %s\n", e.what());
101 return false;
102 }
//检测-testnet和-regtest设置的当前程序运行的网络
103 // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
104 try {
105 SelectParams(ChainNameFromCommandLine());
106 } catch (const std::exception& e) {
107 fprintf(stderr, "Error: %s\n", e.what());
108 return false;
109 }
110
//不合法的参数字符检测
111 // Error out when loose non-argument tokens are encountered on command line
112 for (int i = 1; i < argc; i++) {
113 if (!IsSwitchChar(argv[i][0])) {
114 fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
115 return false;
116 }
117 }
118
//设置-server参数为true,而-server参数表示是否接收RPC命令,
//这里因为是bitcoind,默认作为核心服务器接收bitcoin-cli以及bitcoin-tx传送的命令
119 // -server defaults to true for bitcoind but not for the GUI so do this here
120 gArgs.SoftSetBoolArg("-server", true);
121 // Set this early so that parameter interactions go to console
//初始化日志记录以及打印方式
122 InitLogging();
//初始化网络参数
123 InitParameterInteraction();
//注册相应的消息以及处理方式
124 if (!AppInitBasicSetup())
125 {
126 // InitError will have been called with detailed error, which ends up on console
127 return false;
128 }
//设置区块链运行参数
129 if (!AppInitParameterInteraction())
130 {
131 // InitError will have been called with detailed error, which ends up on console
132 return false;
133 }
//Sanity Check是用来检查比特币运行时所需要的所有的库是否都运行正常
134 if (!AppInitSanityChecks())
135 {
136 // InitError will have been called with detailed error, which ends up on console
137 return false;
138 }
//通过-daemon参数设置是否后台运行
139 if (gArgs.GetBoolArg("-daemon", false))
140 {
141 #if HAVE_DECL_DAEMON
142 fprintf(stdout, "Bitcoin server starting\n");
143
144 // Daemonize
145 if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
146 fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
147 return false;
148 }
149 #else
150 fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
151 return false;
152 #endif // HAVE_DECL_DAEMON
153 }
//后台运行后锁定数据目录
154 // Lock data directory after daemonization
155 if (!AppInitLockDataDirectory())
156 {
157 // If locking the data directory failed, exit immediately
158 return false;
159 }
//运行主应用程序
160 fRet = AppInitMain();
161 }
162 catch (const std::exception& e) {
163 PrintExceptionContinue(&e, "AppInit()");
164 } catch (...) {
165 PrintExceptionContinue(nullptr, "AppInit()");
166 }
167
//如果返回值fRet为false,那么强制结束所有线程;否则就等待所有线程运行结束
168 if (!fRet)
169 {
170 Interrupt();
171 } else {
172 WaitForShutdown();
173 }
//最后通过ShutDown()完成清理工作
174 Shutdown();
175
176 return fRet;
177 }
178
大概浏览了以下AppInit函数的实现,发现大部分都是在解析bitcoind的命令行参数,首先解析-?,-h,-help帮助信息和-version版本信息,91行执行GetDataDir函数,检查数据目录是否合法,通过-datadir参数进行设置,该目录下主要保存同步的区块信息,钱包信息,配置信息等等几乎所有的区块链运行信息都保存在这里,这个函数在src/util.cpp中实现,代码如下:
581 static fs::path pathCached;
582 static fs::path pathCachedNetSpecific;
583 static CCriticalSection csPathCached;
584
585 const fs::path &GetDataDir(bool fNetSpecific)
586 {
587
588 LOCK(csPathCached);
589
//判断fNetSpecific是否为true,true使用pathCachedNetSpecific(网络路径),否则使用
//pathCached(本地路径)
590 fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
591
//如果path不为空,返回path
592 // This can be called during exceptions by LogPrintf(), so we cache the
593 // value so we don't have to do memory allocations after that.
594 if (!path.empty())
595 return path;
596
//如果有通过-datadir参数指定目录
597 if (gArgs.IsArgSet("-datadir")) {
598 path = fs::system_complete(gArgs.GetArg("-datadir", ""));
//检测参数传入的路径是否为目录,不为目录的话,置空path返回
599 if (!fs::is_directory(path)) {
600 path = "";
601 return path;
602 }
//如果没有通过-datadir参数指定目录,使用GetDefaultDataDir获取默认目录
603 } else {
604 path = GetDefaultDataDir();
605 }
//如果事网络路径,修改path为BaseParams().DataDir();
606 if (fNetSpecific)
607 path /= BaseParams().DataDir();
608
//创建wallets目录
609 if (fs::create_directories(path)) {
610 // This is the first run, create wallets subdirectory too
611 fs::create_directories(path / "wallets");
612 }
613
614 return path;
615 }
AppInit函数105行: SelectParams(ChainNameFromCommandLine());设置的当前程序运行的网络,有三种:Main,Testnet,Regtest
ChainNameFromCommandLine函数,在src/chainparamsbase.cpp中实现,代码如下
90 std::string ChainNameFromCommandLine()
91 {
//获取命令行-regtest参数,是否为私有网
92 bool fRegTest = gArgs.GetBoolArg("-regtest", false);
//获取命令行-testnet参数,是否为测试网
93 bool fTestNet = gArgs.GetBoolArg("-testnet", false);
94 //不能同时配置两个参数
95 if (fTestNet && fRegTest)
96 throw std::runtime_error("Invalid combination of -regtest and -testnet.");
//如果为私网,返回私网
97 if (fRegTest)
98 return CBaseChainParams::REGTEST;
//如果为测试网,返回测试网
99 if (fTestNet)
100 return CBaseChainParams::TESTNET;
//不是以上两种的返回主网
101 return CBaseChainParams::MAIN;
102 }
ChainNameFromCommandLine
AppInit函数122行:InitLogging函数,初始化日志记录以及打印方式,在src/init.cpp中实现,代码如下
void InitLogging()
{
//通过-printtoconsole参数设置是否打印日志到终端
fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false);
//通过-logtimestamps参数设置每一条输出信息附带时间戳,默认值为附带
fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
//通过-logtimemicros设置时间戳精确到微秒精度,默认不附加
fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
//通过-logips设置输出信息中附加ip地址,默认不附加
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
std::string version_string = FormatFullVersion();
#ifdef DEBUG
version_string += " (debug build)";
#else
version_string += " (release build)";
#endif
LogPrintf(PACKAGE_NAME " version %s\n", version_string);
}
AppInit函数123行: InitParameterInteraction();初始化网络参数函数后的其他函数,下篇继续