主页 > imtoken官方app > 比特币源码分析(十四)- 可执行程序- Bitcoind

比特币源码分析(十四)- 可执行程序- Bitcoind

imtoken官方app 2024-01-17 05:14:40

Linux系统中的一切都可以看作是一个文件,文件又可以分为:普通文件、目录文件、链接文件和设备文件。 文件描述符(file descriptor)是内核为了高效管理打开的文件而创建的索引。 它是一个非负整数(通常是一个小整数),用来指代打开的文件,所有执行I/O操作的系统调用都经过文件描述符。 程序刚启动时,0为标准输入,1为标准输出,2为标准错误。 如果此时打开一个新文件,它的文件描述符将为3。POSIX标准要求每次打开一个文件(包括socket),必须使用当前进程中可用的最小文件描述符代码。 因此,在网络通信过程中,如果不注意,可能会发生串扰。

为了防止某个进程耗尽所有文件资源,内核还为单个进程的最大打开文件数设置了一个默认值(称为用户级限制)。 默认值一般为1024,可以使用ulimit -n命令查看。 在 Web 服务器中,优化服务器的最常见方法之一是更改系统默认的最大文件描述符大小。

从上面的文章我们知道,文件描述符是打开文件的一个索引。 代码首先计算用户设置的绑定地址nBind的个数,然后在命令行中-maxconnections。 该参数的默认值为 125。

// src/net.h line 75/** 要维护的最大对等连接数。 */static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;123

然后开始计算最大连接数。 公式中涉及到几个变量,分别位于以下位置,

// src/compat.h line 27#define FD_SETSIZE 1024 // fd_set 中 fd 的最大数量// src/init.cpp line 89#define MIN_CORE_FILEDESCRIPTORS 150// src/net.h line 61/** addnode 的最大数量outgoing nodes */static const int MAX_ADDNODE_CONNECTIONS = 8;123456789FD_SETSIZ:上面参考文章中提到,表示系统对单个进程的用户级别限制,取值为1024。 MIN_CORE_FILEDESCRIPTORS:直译为最小数核心文件描述符的,但是没看懂意思= = MAX_ADDNODE_CONNECTIONS:最大addnode连接数,没看懂意思==

虽然没看懂这个公式的原理,但是功能大概懂了,先判断文件描述符的个数是否足够,不够就报错退出程序; 然后判断命令行设置的-maxconnections是否超过了系统支持的最大连接数,如果超过了,那么会提示强制设置为系统的最大连接数。

0x02 Step 3: parameter-to-internal-flagsif (gArgs.IsArgSet("-debug")) {// 特殊情况:如果设置了-debug=0/-nodebug,关闭调试消息 const std::vector categories = gArgs.GetArgs("-debug");if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) {for (const auto& cat : categories) {uint32_t flag = 0;if (!GetLogCategory(&flag, &cat)) {InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));continue ; }logCategories |= flag;}}}// 现在删除显式排除的日志类别for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {uint32_t flag = 0;if (!GetLogCategory(&flag , &cat)) {InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));continue;}logCategories &= ~flag;}12345678910111213141516171819202122232425

第一段代码是确定应将哪些目录写入调试日志。 所有目录包括以下类型(src/util.cpp 第 220 行),

const CLogCategoryDe​​sc LogCategories[] ={ {BCLog::NONE, "0"}, {BCLog::NET, "net"}, {BCLog::TOR, "tor"}, {BCLog::MEMPOOL, "mempool"} , {BCLog::HTTP, "http"}, {BCLog::BENCH, "bench"}, {BCLog::ZMQ, "zmq"}, {BCLog::DB, "db"}, {BCLog::RPC , "rpc"}, {BCLog::ESTIMATEFEE, "estimatefee"}, {BCLog::ADDRMAN, "addrman"}, {BCLog::SELECTCOINS, "selectcoins"}, {BCLog::REINDEX, "reindex"}, {BCLog::CMPCTBLOCK, "cmpctblock"}, {BCLog::RAND, "rand"}, {BCLog::PRUNE, "prune"}, {BCLog::PROXY, "proxy"}, {BCLog::MEMPOOLREJ, “mempoolrej”},{BCLog::LIBEVENT,“libevent”},{BCLog::COINDB,“coindb”},{BCLog::QT,“qt”},{BCLog::LEVELDB,“leveldb”},{ BCLog::ALL, "1"}, {BCLog::ALL, "all"},};123456789101112131415161718192021222324252627

每个目录对应一个编号。 代码中的logCategories变量是所有日志目录的集合,类型为uint32_t比特币源码,目录对应的每个数字对应32位中的一位,所以每次做 | 操作,就是把当前的目录号加入到集合中。 而后面的-debugexclude参数是把不想记录日志的目录从集合中删除。 这时候用到&=~flag操作,~表示每一位取反,反码表示当前目录号从集合中移除。

下一段代码中接下来的几个if语句是检查不支持的参数或更改参数的命令,这样比较容易理解。

// Checkmempool 和 checkblockindex 在 regtest 模式下默认为 trueint ratio = std::min(std::max(gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);if (比率!= 0) {mempool.setSanityCheck(1.0 / 比率);}fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());fCheckpointsEnabled = gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_EN);

根据帮助信息中的解释,

-checkmempool:指示执行完整性检查的事务数。

-checkblockindex:定时检查mapBlockIndex、setBlockIndexCandidates、chainActive和mapBlockUnlinked变量的一致性。

-checkpoints:该变量默认为1,表示不验证现有链; 如果为0,表示检查部分checkpoint的区块信息是否正确,所有的checkpoint信息也保存在chainparams中的checkpointdata中。

代码首先判断chainparams中的DefaultConsistencyChecks是否为真。 如果此变量为假,则 ratio=0比特币源码,即不执行健全性检查。 Sanity check 之前在#t2 的 CTxMemPool 类中引入,意思是检查 mempool 中所有交易的一致性(没有双花,所有输入都是合法的)。 SelectParams函数中也引入了变量chainparams。 首先根据设置的网络MAIN、TESTNET或REGTEST选择相应的参数。 三个网络根据src/chainparams.cpp为参数定义不同的值。 对于 DefaultConsistencyChecks Parameters,MAIN 和 TESTNET 都是 false,而这个变量在 REGTEST 中是 true。

hashAssumeValid = uint256S(gArgs.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));if (!hashAssumeValid.IsNull())LogPrintf("假设块 %s 的祖先具有有效签名。\ n", hashAssumeValid.GetHex());elseLogPrintf("正在验证所有区块的签名。\n");12345

-assumevalid=blockid:表示假定blockid之前的所有块都是正确的,即不需要进一步验证。 如果未设置,则必须验证之前所有块的签名信息。

// mempool limitsint64_t nmempoolsizemax = gargs.getArg(“ - maxmempool”,default_max_mempool_size) * 1000000; inmempoolsizemin = gargs.getArgs.getArg(“ -limitdescendemsiiz) return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));//增量中继费用设置内存池中 BIP 125 替换所需的最低费用增加// 以及由于内存池限制而导致内存池最小费用增加到高于被逐出的 txs 费率的金额。if (gArgs.IsArgSet("-incrementalrelayfee")){CAmount n = 0;if (!ParseMoney(gArgs.GetArg("- incrementalrelayfee", ""), n))return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")));incrementalRelayFee = CFeeRate(n);}1234567891011121314

下一段代码首先计算出mempool的最大值,然后乘以1000000,将单位从MB转换为B,然后计算出最小限制。 ,然后乘以 40 表示一个事务族可以容纳至少 40 个这样的事务。

-limitdescendantsize:如果一笔交易在内存池中的所有祖先大小之和超过限制值,则拒绝该笔交易。 单位是KB。

可以在每个节点内设置以下费用,以避免接收过多的交易,

minrelaytxfee:最小转发费用,如果交易费用小于这个值,节点将直接忽略交易。 默认值为 0.00001 BTC/KB。 dustrelayfee:用于判断一笔交易是否为dust tx,如果是则忽略该笔交易。 默认值为 0.00001BTC/KB。 incrementalrelayfee:用于更改内存池最低交易费用的变量。 当内存池中的交易数量超过阈值时,交易费阈值会增加,增加的程度由incrementalrelayfee决定。 默认值为0.00001BTC/KB。节点交易处理流程

因此,一个全节点的交易处理流程如下: (1) 首先判断交易费用之和是否大于minrelayfee; (2)然后判断是否是dustrelayfee,如果是则转发给其他节点,自己忽略这笔交易; (3) 最后判断费用是否满足当前费用条件。 当前费用将根据交易数量动态变化。 当交易数量过多时,它会增加,当交易数量减少时,它也会减少。

源码中这个地方就是判断命令行中是否设置了-incrementalrelayfee,如果设置了,则设置变量incrementalRelayFee。

设置脚本校验线程数 // -par=0表示自动检测,但nScriptCheckThreads==0表示不并发 nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);if (nScriptCheckThreads