我使用的是Ubuntu 16.04 系统所以直接按照https://github.com/bitcoin/bitcoin/blob/master/doc/build-unix.md 编译就可以成功,编译完成之后,生成了一下几个可执行文件:
bench_bitcoin
:根据https://github.com/bitcoin/bitcoin/issues/829 解释,作用是编译系统更新,也就是检查系统使用的一些加密算法是否有新的更新。bitcoin-cli
:是Bitcoind的一个功能完备的RPC客户端,包括查询区块,交易信息等等,具体将在相应章节介绍。bitcoind
:是比特币运行的核心程序俗称bitcoin core,也是我们分析的重点。bitcoin-qt
:比特币钱包。bitcoin-tx
:比特币交易处理模块,支持交易的查询和创建。test_bitcoin
:运行各个模块的测试代码。test_bitcoin-qt
:运行钱包的模块测试代码。我们首先从最核心的bitcoind开始分析,然后再看其他的,因为其他部分的代码使用的很多类、很多函数都是bitcoind中使用过的,所以分析完bitcoind,其他部分也就轻而易举。
另外提及一下代码的查看软件,我用的是Sublime Text,能快速的找到函数的定义和实现的位置,并且还支持在项目内查找,一个好的编辑器对于代码的分析也是很有帮助的。
对于c++代码,整个程序都是从main函数开始执行的,所以我们首先寻找bitcoind的main函数。而一般编译出来的可执行程序都是有对应文件名的cpp文件,所以我们找到了src/bitcoind.cpp,代码拉到最后就找到了我们的main函数。
// src/bitcoind.cpp line 188
int main(int argc, char* argv[])
{
SetupEnvironment();
// Connect bitcoind signal handlers
noui_connect();
return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}
找到SetupEnvironment的实现位置,位于src/util.cpp中,sublime中直接右键Goto Definition即可。
// src/util.cpp line 834
void SetupEnvironment()
{
#ifdef HAVE_MALLOPT_ARENA_MAX
if (sizeof(void*) == 4) { //判断是否为32位系统
mallopt(M_ARENA_MAX, 1);
}
#endif
#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
try {
std::locale(""); // Raises a runtime error if current locale is invalid
} catch (const std::runtime_error&) {
setenv("LC_ALL", "C", 1);
}
#endif
std::locale loc = fs::path::imbue(std::locale::classic());
fs::path::imbue(loc);
}
函数首先通过sizeof(void*) == 4
来判断当前系统是否是32位,如果是64位的话那么sizeof(void*)
值就为8。mallopt
函数是用来控制malloc
内存分配时的行为的(具体请参考http://man7.org/linux/man-pages/man3/mallopt.3.html),而M_ARENA_MAX
参数是值最多能创建的arena数,一个arena是指malloc
在分内内存时的一个内存池,而这个arena是线程安全的,也就是说多线程访问时是互斥访问的,既然是互斥访问的,那么很明显,当arena数量越多时,线程的竞争就越小,但是需要的内存也就越多(因为arena就相当于一次性申请大量内存,然后在malloc时慢慢分配出去)。通过代码中的注释,我们发现glibc
库会为每个核创建2个arena,而这会对32为系统造成虚拟地址空间不足的问题,所以这里设为1.
下面是locale()
是设置系统区域,这将决定程序所使用的当前语言编码、日期格式、数字格式及其它与区域有关的设置。最后两行是文件路径的本地化设置,主要设计宽字符(Wide char)和多字节(Multi bytes)之间的转换问题。
// src/noui.cpp line 52
void noui_connect()
{
// Connect bitcoind signal handlers
uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);
uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion);
uiInterface.InitMessage.connect(noui_InitMessage);
}
这里涉及到我们之前讲到的Boost信号/插槽机制(http://blog.csdn.net/pure_lady/article/details/77675915)。首先看变量的定义,
// src/ui_interface.h
// line 74-81
/** Show message box. */
boost::signals2::signalconst std::string& message,
const std::string& caption,
unsigned int style),
boost::signals2::last_value> ThreadSafeMessageBox;
/** If possible, ask the user a question.
* If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */
boost::signals2::signalconst std::string& message,
const std::string& noninteractive_message,
const std::string& caption, unsigned int style), boost::signals2::last_value > ThreadSafeQuestion;
/** Progress message during initialization. */
boost::signals2::signal<void (const std::string &message)> InitMessage;
extern CClientUIInterface uiInterface; // line 123
这里在CClientUIInterface
类中定义了一些信号,其中三个分别是ThreadSafeMessageBox
,ThreadSafeQuestion
和InitMessage
。再看前面的noui_connect
中的变量,我们发现通过connect
连接的插槽函数定义和信号中的定义完全一致,所以当信号触发的时候,这些连接的函数都会被调用。