比特币源码研读之二

前一篇文章中已经完成了main函数运行过程的梳理,并且也绘制了其运行流程图,为了更清晰地记录每个过程的详细执行内容,我将从本文开始对每个过程进行说明。

在做代码分析之前,我将先把本文主要涉及的代码文件列举出来,大家可以准备好相应文件,更好地理解源码:

本文主要涉及的源码文件包括:

src/bitcond.cpp、src/util.h、src/util.cpp、src/noui.h、src/noui.cpp、ui_interface.h、ui_interface.cpp

一、设置运行环境SetupEnviroment

《比特币源码研读之二》

main函数执行的第一步是通过SetupEnviroment函数实现比特币后台进程运行环境的设置,SetupEnviroment实现位于src/util.cpp中。在研读SetupEnviroment函数之前,我们有必要先说明下其所在的实现文件util.cpp的主要功能,因为该实现文件将在后续的研读中经常出现。

util.cpp其位于src目录中。util源码文件主要功能是什么呢?我们可以从其头文件util.h的最开始处找到相应的注释说明:

/**

* Server/client environment: argumenthandling, config file parsing,

* logging, thread wrappers

*/

其大意为该源文件实现了服务器/客户端运行环境的设置,包括参数处理、配置文件解析、日志打印以及线程封装等的初始化与属性设置。也可以将util看做是对比特币核心源码的通用功能的统一封装实现文件。

再次回到SetupEnviroment函数,该函数由3部分组成:

《比特币源码研读之二》

(1)内存分配区设置

此处内存分配区设置的目的是为了防止32位操作系统中虚拟地址空间过渡使用,即程序中的控制内存分配。通过sizeof(void*)==4判断当前系统是否为32位,如果是,则通过mallopt设置只有1个内存分配区,即表示系统按CPU进行自动设置。

(2)本地化设置

C/C++程序中,locale(即系统区域设置,即国家或地区设置)将决定程序所使用的当前语言编码、日期格式、数字格式及其它与区域有关的设置,locale设置的正确与否将影响到程序中字符串处理(wchar_t如何输出、strftime()的格式等)。因此,对于每一个程序,都应该慎重处理locale设置。

具体可参见:http://blog.csdn.net/haiross/article/details/45074355

(3)本地化文件路径设置

第三部分代码主要通过boost::filesystem::path::imbue实现文件系统的本地化设置。

二、信号处理noui_connect

《比特币源码研读之二》

noui_connect函数位于noui.cpp中,从文件名noui(no ui,即无UI界面的消息连接)我们可以看出该文件实现是无操作界面的,noui_connect就是无界面情况下的信息连接!

通过分析,发现noui_connect通过信号/槽的通信方式实现子线程与主线程的消息处理功能。其处理的消息包括三类,分别是:消息弹出框信息提示消息、对用户问题询问的交互提示消息以及程序初始化过程的消息。

voidnoui_connect()

{

// Connect bitcoind signal handlers

uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);

uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion);

uiInterface.InitMessage.connect(noui_InitMessage);

}

上述代码中的uiInterfaceui_为全局变量,该变量在ui_interface.h的最后面进行了声明:

extern CClientUIInterface uiInterface;

在ui_interface.cpp文件的上方实现了其定义:

CClientUIInterface uiInterface;

我们可以看到noui_connect()实现的3行代码中,uiInterface分别调用了3个变量:

ThreadSafeMessageBox、ThreadSafeQuestion以及InitMessage

每个变量都通过connect方法调用了noui.cpp中定义的3个静态函数:

noui_ThreadSafeMessageBox、noui_ThreadSafeQuestion以及noui_InitMessage

我们来分析下上述代码的实现原理。首先来看看uiInterface调用的3个变量的类型与定义。

这3个变量位于ui_interface.h中:

/**Show message box. */

boost::signals2::signal > ThreadSafeMessageBox;

/**If possible, ask the user a question. If not, falls back toThreadSafeMessageBox(noninteractive_message, caption, style) and returns false.*/

boost::signals2::signal > ThreadSafeQuestion;

/**Progress message during initialization. */

boost::signals2::signal InitMessage;

我们可以看到这3个变量的类型均为boost::signals2::signal信号类型,同时,在signal中通过<>方式包含了程序接收到该信号时的处理方法,也就是信号接收槽,整个机制也叫信号/机制。Boost中的信号/槽机制在设计模式中对应的是观察者模式,其具体解释如下:

signals2基于Boost的另一个库signals,实现了线程安全的观察者模式。在signals2库中,观察者模式被称为信号/插槽(signals and slots),他是一种函数回调机制,一个信号关联了多个插槽,当信号发出时,所有关联它的插槽都会被调用。

许多成熟的软件系统都用到了这种信号/插槽机制(另一个常用的名称是事件处理机制:event/event handler),它可以很好地解耦一组互相协作的类,有的语言设置直接内建了对它的支持(如c#),signals2以库的形式为c++增加了这个重要的功能。

signal最重要的操作函数是插槽管理connect()函数,它吧插槽连接到信号上,相当于为信号(事件)增加了一个处理的handler。

插槽可以是任意的可调用对象,包括函数指针、函数对象、以及它们的bind表达式和function对象,signal内部使用function作为容器来保存这些可调用对象。连接时可以指定组号也可以不指定组号,当信号发生时将依据组号的排序准则依次调用插槽函数。

如果连接成功connect()将返回一个connection,表示了信号与插槽之间的连接关系,它是一个轻量级的对象,可以处理两者间的连接,如断开、重连接、或者测试连接状态。

成员函数disconnect()可以断开插槽与信号的连接,它有两种形式:传递组号将断开该组的所有插槽,传递一个插槽对象将仅断开该插槽。函数disconnect_all_slots()可以一次性断开信号的所有插槽连接。

我们再来看这3个信号/槽对象的定义,以ThreadSafeMessageBox为例:

boost::signals2::signal

(const std::string& message, const std::string& caption, unsigned int

style), boost::signals2::last_value > ThreadSafeMessageBox;

从定义中我们可以看到signal<>中包含了1个函数定义和1个类型定义:

函数定义:

bool (const std::string&message, const std::string& caption, unsigned int style)

返回类型定义:

boost::signals2::last_value

那么这个函数定义与类型定义的作用是什么呢?其实这个函数定义就是信号对应的槽,类型就是这个槽函数的返回值类型。也就是说,程序在实现该接收槽时,其参数数量、类型、返回值必须要与上述定义一致,才能正确处理对应信号!

有了这个解释,我们再来看我们本节要说明的noui_connect()函数,我们以第一行代码为例来进行代码的解读:

uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);

在第一行代码中,我们通过ThreadSafeMessageBox.connect方法确定了槽函数noui_ThreadSafeMessageBox,而这个槽函数的实现就在noui.cpp中,其函数定义如下:

static bool noui_ThreadSafeMessageBox(conststd::string& message, const std::string& caption, unsigned int style)

通过对比我们可以发现noui_ThreadSafeMessageBox其定义完全符合ThreadSafeMessageBox定义所要求的槽函数定义,在该函数中完成了对应信号的实现。通过其代码我们会发现其主要通过日志打印和文件记录方式实现相应信号的处理,以作为程序运行过程的记录。

作者:区块链研习社比特币源码研读班 菜菜子

以下是广告:

我们区块链研习社已创建“区块链研习社币圈交流”小密圈”,在小密圈中,我们将带领大家一起学习区块链的原理与投资,还将提供区块链基本原理解答、交易所注册与交易操作、ICO交易与操作、投资分析、风险分析等内容。

目前入圈价格初始定价50元,50人调整一次价格,每次调整幅度为50元!

《比特币源码研读之二》

    原文作者:菜菜子_forest
    原文地址: https://www.jianshu.com/p/4fc762796f83
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞