关于设备树,之前就经过详细的系统培训,但是本着会用就行的原则,对各个知识点都没有进行系统的总结。都是用到哪里学哪里,时间长了,基本也忘记了。所以对于后期知识各个知识点进行总结,本章主要讨论一下内容,能看懂和修改对应模块的dts文件。
- dts的引入
- dts的基本语法和基本格式介绍
- dts的组成,各个模块使用方法
1. dts为何物
在传统Linux内核中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,比如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data,这些板级细节代码对内核来讲只不过是垃圾代码。而采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。导致ARM的merge工作量较大,其次ARM很多的代码都是垃圾,代码里面有若干愚蠢的table,而多个人在维护这个table,从而导致了冲突。最后linux发出
Gaah.Guys, this whole ARM thing is a f*cking pain in the ass.
之后经过一些讨论,对ARM平台的相关code做出如下相关规范调整,这个也正是引入DTS的原因。
1、ARM的核心代码仍然保存在arch/arm目录下
2、ARM SoC core architecture code保存在arch/arm目录下
3、ARM SOC的周边外设模块的驱动保存在drivers目录下
4、ARM SOC的特定代码在arch/arm/mach-xxx目录下
5、ARM SOC board specific的代码被移除,由DeviceTree机制来负责传递硬件拓扑和硬件资源信息。
本质上,Device Tree改变了原来用code方式将HW配置信息嵌入到内核代码的方法,改用bootloader传递一个DB的形式。对于嵌入式系统,在系统启动阶段,bootloader会加载内核并将控制权转交给内核,此外,还需要把上述的三个参数信息传递给kernel,以便kernel可以有较大的灵活性。在linux kernel中,Device Tree的设计目标就是如此。
在devie tree中,可描述的信息包括:
1、CPU的数量和类别
2、内存基地址和大小
3、总线和桥
4、外设连接
5、中断控制器和中断的使用情况
6、GPIO控制器和GPIO使用情况
7、clock控制器和clock使用情况
它基本就是一棵电路板上的CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核来识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给内核,内核会将这些资源绑定给展开的相应设备
2. DTS基本知识
2.1 dts
硬件的相应信息都会写在.dts为后缀的文件中,每一款硬件可以单独写一份xxxx.dts,一般在Linux源码中存在大量的dts文件,对于arm架构可以在arch/arm/boot/dts找到相应的dts,一个dts文件对应一个ARM的machie。
2.2 dtsi
值得一提的是,对于一些相同的dts配置可以抽象到dtsi文件中,然后类似于C语言的方式可以include到dts文件中,对于同一个节点的设置情况,dts中的配置会覆盖dtsi中的配置。
2.3 dtc
dtc是编译dts的工具,可以在Ubuntu系统上通过指令apt-get install device-tree-compiler安装dtc工具,不过在内核源码scripts/dtc路径下已经包含了dtc工具;
2.4 dtb
dtb(Device Tree Blob),dts经过dtc编译之后会得到dtb文件,dtb通过Bootloader引导程序加载到内核。所以Bootloader需要支持设备树才行;Kernel也需要加入设备树的支持;
3. DTS结构
/dts-v1/;
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
// hex is implied in byte arrays. no '0x' prefix is required
a-byte-data-property = [01 23 34 56];
child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
child-node1 {
};
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
device tree的基本单元是node。这些node被组织成树状结构,除了root node,每个node都只有一个parent。一个device tree文件中只能有一个root node。每个node中包含了若干的property/value来描述该node的一些特性。每个node用节点名字(node name)标识,节点名字的格式是node-name@unit-address。如果该node没有reg属性(后面会描述这个property),那么该节点名字中必须不能包括@和unit-address。unit-address的具体格式是和设备挂在那个bus上相关。例如对于cpu,其unit-address就是从0开始编址,以此加一。而具体的设备,例如以太网控制器,其unit-address就是寄存器地址。root node的node name是确定的,必须是“/”。 也就是说设备树源文件的结构为
- 1个root节点”/”;
- root节点下面含一系列子节点,“node1” and “node2”
- 节点node1和下又含有一系列子节点,“child-node1” and “child-node2”
- 各个节点都有一系列属性
- 这些属性可能为空,如an-empty-property
- 可能为字符串,如a-string-property
- 可能为字符串树组,如a-string-list-property
- 可能为Cells(由u32整数组成),如second-child-property
4 DTS语法介绍
了解了基本的device tree的结构后,我们总要把这些结构体现在device tree source code上来。在linux kernel中,扩展名是dts的文件就是描述硬件信息的device tree source file,在dts文件中,一个node被定义成:
[label:] node-name[@unit-address] {
[properties definitions]
[child nodes]
}
- 1
- 2
- 3
- 4
“[]”表示option,因此可以定义一个只有node name的空节点,label方便在dts文件中引用
基本数据类型:
- text string(以null结束),以双引号括起来,如:
- string-property = “a string”
- cells 是32位无符号整形数,以尖括号括起来,如
- cell-property = <0xbeef 123 0xabcd1234>
- binary data 以方括号括起来,如:
- binary-property = [0x01 0x23 0x45 0x67];
- 不同类型数据可以在同一个属性中存在,以逗号分格,如:
- mixed-property = “a string”, [0x01 0x23 0x45 0x67], <0x12345678>;
- 多个字符串组成的列表也使用逗号分格,如:
- string-list = “red fish”,“blue fish”;
5 dts的组成
5.1 标准属性
5.1.1 compatible
每一个dts文件都是由一个root的根节点组成,内核通过根节点“/”的兼容性即可判断它启动的是什么设备,其代码结构如下
/ {
model = "Spreadtrum SC9830A-5 V1.0.0 Smartphone Board";
compatible = "sprd,sp9830a-5h10-ga1", "sprd,sc9830";
chosen {
bootargs = "earlycon=sprd_serial,0x70100000,115200n8 loglevel=8 console=ttyS1,115200n8 init=/init root=/dev/ram0 rw androidboot.hardware=sc9830";
linux,initrd-start = <0x85500000>;
linux,initrd-end = <0x855a3212>;
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- model属性值是,它指定制造商的设备型号。推荐的格式是:“manufacturer,model”,其中manufacturer是一个字符串描述
制造商的名称,而型号指定型号。- compatible属性值是,指定了系统的名称,是一个字符串列表,它包含了一个“<制造商>,<型号>”形式的字符串。重要的是要指定一个确切的设备,并且包括制造商的名字,以避免命名空间冲突。
- chosen 节点不代表一个真正的设备,但功能与在固件和操作系统间传递数据的地点一样,如根参数,取代以前bootloader的启动参数,控制台的输入输出参数等
5.1.2 #address-cells和#size-cells
- #address-cells = <1>: 基地址、片选号等绝对起始地址所占字长,单位uint32
- #size-cells = <1>: 长度所占字长,单位uint32
soc {
#address-cells = <1>;
#size-cells = <1>;
serial {
compatible = "ns16550";
reg = <0x4600 0x100>;
clock-frequency = <0>;
interrupts = <0xA 0x8>;
interrupt-parent = <&ipic>;
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5.1.3 CPU addressing
在讨论寻址时,CPU节点代表了最简单的情况。 每个CPU都分配有一个唯一的ID,并且没有与CPU ID相关联的大小。
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在cpus节点,#address-cells被设置成了1,#size-cells被设置成了0。这是说子reg值是单独的uint32,它用无大小字段表示地址。在此情况下,这两个cpu分配到的地址为0和1。Cpu节点的#size-cells是0因为每个cpu只分配到了一个单独的地址。
你仍然需要注意reg值班需要与节点名的值相匹配。按照惯例,如果一个节点有一个reg属性,那么这个节点名称必须包括unit-address,这是reg属性的第一个address值。
5.1.4 Memory Mapped Devices
与在cpu节点中单独的address值不同,内存映射设备被分配了一系列将要响应的地址,因此不仅需要包含内存的基地址而且还需要映射地址的长度,因此需要使用#size-cells用来表示在每个子reg元组中长度字段的大小。在以下示例中,每个address值为1 cell(32 bits),每个长度值也是1 cell,这在32 bit系统是比较典型的。64 bit设备也许会为#address-cells和#size-cells使用数值2,在device tree中获取64 bit addressing。
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
...
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
};
serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
};
interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
};
...
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
5.1.5 Non Memory Mapped Devices
处理器总线的其它设备为非内存映射设备。他们有地址范围,但不能被CPU直接寻址。母设备的驱动程序将代替CPU进行间接访问。以i2c设备为例,每个设备都分配了一个地址,但没有长度或范围与之相匹配。这与CPU地址分配很相似。
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
5.1.6 Ranges (Address Translation)
我们已经讨论过如何向设备分配地址,但此时这些地址只是本地设备节点,还没有说明如何从那些地址里映射到cpu可以使用的地址。根节点经常描述地址空间的CPU视图。根节点的子节点已经使用了CPU的address domain,所以不需要任何明确的映射。例如,serial@101f0000设备被直接分配了地址0x101f0000。
根节点的非直接子节点是无法使用CPU的address domain的。为了在deivce tree获取内存映射地址必须指定如何从一个域名将地址转换到另一个。Ranges属性就用于此目的。以下是添加了ranges属性的device tree示例。
/dts-v1/;
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
...
external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
Ranges是一个地址转换列表。每个输入ranges表格的是包含子地址的元组,母地址和子地址空间的范围大小。每个字段的大小都由获取的子地址的#address-cells值,母地址的#address-cell值和子地址的#size-cells值而定。以外部总线为例,子地址是2 cells,母地址是1 cell,大小也为1 cell。转换三个ranges:
- Offset 0 from chip select 0 is mapped to address range 0x10100000…0x1010ffff
- Offset 0 from chip select 1 is mapped to address range 0x10160000…0x1016ffff
- Offset 0 from chip select 2 is mapped to address range 0x30000000…0x30ffffff
例如上面的总线是有片选的,就需要描述片选及片选的偏移量,在说明地址时,还需要说明地址映射范围
5.1.7 status
device tree中的status标识了设备的状态,使用status可以去禁止设备或者启用设备,看下设备树规范中的status可选值
value | Description |
---|---|
“okay” | 表示设备正在运行 |
“disabled” | 表示该设备目前尚未运行,但将来可能会运行 |
“fail” | 表示设备无法运行。 在设备中检测到严重错误,确实如此没有修理就不可能投入运营 |
“fail-sss” | 表示设备无法运行。 在设备中检测到严重错误,它是没有修理就不可能投入运营。 值的sss部分特定于设备并指示检测到的错误情况 |
5.2 中断映射
与遵循树的自然结构而进行的地址转换不同,机器上的任何设备都可以发起和终止中断信号。另外地址的编址也不同于中断信号,前者是设备树的自然表示,而后者者表现为独立于设备树结构的节点之间的链接。 下图显示了设备的自然结构以及每个节点在逻辑中断树中的位置。
上图包括以下部分:
- open-pic中断控制器是中断树的根
- 中断树根有三个子设备,它们将中断直接路由到open-pic
- device1
- PCI host controller
- GPIO Controller
- 存在三个中断域; 一个以开放式pic节点为根,一个在PCI主桥节点,一个在GPIO Controller节点上
- 有两个nexus节点; 一个位于PCI主桥,一个位于GPIO控制器。
属性 | 属性值 | 描述 |
---|---|---|
#interrupts | prop-encoded-array | 一个设备节点属性,该属性主要描述了中断的HW interrupt ID以及类型 |
#interrupt-parent | phandle | 该属性主要描述了该设备的interrupt request line连接到哪一个interrupt controller,那些没有 interrupt-parent 的节点则从它们的父节点中继承该属性 |
#interrupts-extended | phandle prop-encoded-array | 列出了设备生成的中断,当设备连接到多个中断控制器 |
#interrupt-cells | u32 | 这是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符(类似于 #address-cells 和 #size-cells),则子节点的interrupts一个cell三个32bits整型值: <中断域 中断 触发方式> |
#interrupt-controller | empty | 一个空属性用来声明这个node接收中断信号 |
下面显示了具有PCI总线控制器和采样中断的设备片段
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
open-pic {
clock-frequency = <0>;
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <2>;
};
pci {
#interrupt-cells = <1>;
#size-cells = <2>;
#address-cells = <3>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <
/ * IDSEL 0x11 - PCI slot 1* /
0x8800 0 0 1 &open-pic 2 1 / * INTA* /
0x8800 0 0 2 &open-pic 3 1 / * INTB* /
0x8800 0 0 3 &open-pic 4 1 / * INTC* /
0x8800 0 0 4 &open-pic 1 1 / * INTD* /
/ * IDSEL 0x12 - PCI slot 2* /
0x9000 0 0 1 &open-pic 3 1 / * INTA* /
0x9000 0 0 2 &open-pic 4 1 / * INTB* /
0x9000 0 0 3 &open-pic 1 1 / * INTC* /
0x9000 0 0 4 &open-pic 2 1 / * INTD* /
>;
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
5.3 特殊节点
aliases节点为了解决节点路径名过长的问题,引入了节点别名的概念,可以引用到一个全路径的节点。如/external-bus/ethernet@0,0,但当用户想知道具体内容的时候显得太累赘,“哪个设备是eth0?”
aliases {
ethernet0 = ð0;
serial0 = &serial0;
};
- 1
- 2
- 3
当为设备分配一个标识符的时候,操作系统更倾向于使用aliases
本文说明
本文是【手把手教程】职业学校技能大赛“区块链技术应用”赛项-样题-区块链部署&运维&测试部分(任务1-2,1-3)这篇文章的补充。解决了离线环境下脚本
build_chain.sh
无法从github下载fisco二进制文件的问题。
考虑到比赛环境不允许连接外网,而脚本build_chain.sh中有从github下载fisco的二进制文件的命令,我们可以做如下调整:
- 调整build_chain.sh中下载代码的部分,跳过下载二进制文件
- 但这样我们需要提供下载好的二进制文件(前提是比赛环境中至少有一份已经准备好的
fisco-bcos
二进制文件。可以用find命令找一找)
在比赛时,可以按照后文介绍的方法,实现上述1,2两点调整。首先介绍修改方法(基于样题的虚拟机环境),之后附上这样修改的逻辑,便于比赛环境中灵活应用。
修改方法(基于样题环境)
把原文档中运行bash build_chain.sh ...
那一步,替换为下面这些
sudo su
cd /root/tools
rm -rf nodes # 如果存在nodes,先删掉
# 自己创建好目录,并准备好fisco-bcos(直接用虚拟机上现成的)
mkdir nodes
mkdir nodes/192.168.157.128
mkdir nodes/192.168.157.129
cp ./webase-deploy/nodes/127.0.0.1/fisco-bcos nodes/192.168.157.129
cp ./webase-deploy/nodes/127.0.0.1/fisco-bcos nodes/192.168.157.128
cp ./webase-deploy/nodes/127.0.0.1/fisco-bcos nodes
# 修改build_chain.sh,跳过下载代码的部分
vim build_chain.sh
# 修改如下位置:
1654 #dir_must_not_exists "${output_dir}" # 因为我们手动创建了nodes目录,这里如果不注释掉这行,运行脚本会报错[WARN] /root/tools/brtest/nodes DIR exists, please clean old DIR!
# 下面这部分注释掉,并添加一行(1679):
1678 # download fisco-bcos and check it
1679 bin_path=${output_dir}/${bcos_bin_name} # 添加这行是因为这行是download_bin函数里的内容,它修改了全局变量bin_path,所以要加在这里,否则可能影响后面的代码运行
1680 #if [ -z ${docker_mode} ];then
1681 # if [[ -z ${bin_path} ]];then
1682 # download_bin
1683 # else
1684 # check_bin
1685 # fi
1686 #fi
# 查看fisco-bcos的版本号,并运行脚本,指定版本号
./nodes/fisco-bcos -v
FISCO-BCOS Version : 2.7.2
Build Time : 20210201 10:03:03
Build Type : Linux/clang/Release
Git Branch : HEAD
Git Commit Hash : 4c8a5bbe44c19db8a002017ff9dbb16d3d28e9da
# 运行脚本,注意指定版本号(从上面看到是2.7.2)
bash build_chain.sh -f ipconf -p 30330,20230,8545 -v 2.7.2
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
这时就完成了原本的bash build_chain.sh ...
这一步,可以按原教程继续了。
产生修改方案的逻辑过程
这部分作为补充,介绍前述修改方案的产生过程(分析过程)
查看build_chain.sh中的下载代码的部分
打开build_chain.sh
,查找下载代码的部分
sudo su
cd /root/tools/
vim build_chain.sh
# (下面部分是在vim中进行的)
# (可选)显示行号,方便参考本文进行定位
:set nu
# 查找脚本中下载代码的命令位置
# 这里我们根据脚本运行的报错中的[INFO]信息来找。
/Downloading fisco-bcos binary from
# 回车。这时可以看到,在1551行的位置找到相应的[INFO]信息
1540 download_bin()
1541 {
1542 if [ "${x86_64_arch}" != "true" ];then exit_with_clean "We only offer x86_64 precompiled fisco-bcos binary, your OS architecture is not x86_64. Please compile from source." ; fi
1543 bin_path=${output_dir}/${bcos_bin_name}
1544 package_name="fisco-bcos.tar.gz"
1545 if [ -n "${macOS}" ];then
1546 package_name="fisco-bcos-macOS.tar.gz"
1547 fi
1548
1549 Download_Link="https://github.com/FISCO-BCOS/FISCO-BCOS/releases/download/v${compatibility_version}/${package_name}"
1550 local cdn_download_link="${cdn_link_header}/FISCO-BCOS/releases/v${compatibility_version}/${package_name}"
1551 LOG_INFO "Downloading fisco-bcos binary from ${Download_Link} ..."
1552 if [ $(curl -IL -o /dev/null -s -w %{http_code} "${cdn_download_link}") == 200 ];then
1553 curl -#LO "${Download_Link}" --speed-time 20 --speed-limit 102400 -m "${download_timeout}" || {
1554 LOG_INFO "Download speed is too low, try ${cdn_download_link}"
1555 curl -#LO "${cdn_download_link}"
1556 }
1557 else
1558 curl -#LO "${Download_Link}"
1559 fi
1560 if [[ "$(ls -al . | grep tar.gz | awk '{print $5}')" -lt "1048576" ]];then
1561 exit_with_clean "Download fisco-bcos failed, please try again. Or download and extract it manually from ${Download_Link} and use -e option."
1562 fi
1563 tar -zxf ${package_name} && mv fisco-bcos ${bin_path} && rm ${package_name}
1564 chmod a+x ${bin_path}
1565 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
我们来理解这段代码。它定义了一个函数download_bin()
,是从github上下载${package_name}
这个文件(1558行 curl -#LO命令),然后解压(1563行 tar-zxf),解压后得到的文件名为fisco-bcos
。把fisco-bcos
移动到${bin_pach}
目录下(1563行 mv),并删除了刚下载的${package_name}
文件(1563行 rm)
那么,这个函数download_bin()
是在哪里调用的呢?我们在vim中查找:
/download_bin
# 回车。然后点击n寻找下一个,直到看到这个:
1677 # download fisco-bcos and check it
1678 if [ -z ${docker_mode} ];then
1679 if [[ -z ${bin_path} ]];then
1680 download_bin
1681 else
1682 check_bin
1683 fi
1684 fi
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
可见,该函数在1680行被调用。逻辑是,如果本地没有相关文件${bin_path}
,就下载(1680行,调用download_bin
函数),否则检查已有文件(1682行,check_bin
函数)。那么我们可能可以把这段(1678-1684)注释掉以跳过下载过程,但同时必须手动准备好原本由download_bin
下载的fisco-bcos
文件
查找机器上已有的fisco-bcos文件
如果机器上已有ficso-bcos文件,那么就可以直接搬过来用,从而能够跳过上述的download_bin
部分。
sudo su
cd /root/tools
find . -name fisco-bcos
./nodes/192.168.157.129/fisco-bcos
./nodes/192.168.157.128/fisco-bcos
./webase-deploy/nodes/127.0.0.1/fisco-bcos
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这里找到三个fisco-bcos
,其中前两个是我之前搭建区块链是生成的,webase-deploy里的是虚拟机中自带的。那么,应该可以直接把webase-deploy里的fisco-bcos拿来用,从而跳过build_chain.sh里的下载部分。
不过这里还有一个问题:虚拟机中自带的fisco-bcos
和脚本下载的fisco-bcos
版本不一致,这是通过diff
命令看出的
$ diff ./nodes/192.168.157.129/fisco-bcos ./webase-deploy/nodes/127.0.0.1/fisco-bcos
二进制文件 ./nodes/192.168.157.129/fisco-bcos 和 ./webase-deploy/nodes/127.0.0.1/fisco-bcos 不同
- 1
- 2
因此,我们还需要修改build_chain.sh
中关于fisco-bcos
版本号的设置问题,防止之后因为版本号不匹配造成问题。
处理fisco-bcos版本号问题
这里有两个问题:
- 如何知道
fisco-bcos
二进制文件的版本号 - 需要定位
build_chain.sh
中规定fisco-bcos
版本号的位置
vim build_chain.sh
- 1
考虑前面我们在1682行看到,如果二进制文件已经存在,会通过check_bin函数来检查。我们来看看check_bin函数是怎么做的:
1569 check_bin()
1570 {
1571 echo "Checking fisco-bcos binary..."
1572 bin_version=$()
1573 if ! ${bin_path} -v | grep -q 'FISCO-BCOS';then
1574 exit_with_clean "${bin_path} is wrong. Please correct it and try again."
1575 fi
1576 bin_version=$(${bin_path} -v | grep -i version | grep -oe "[2-9]*\.[0-9]*\.[0-9]*")
1577 if version_gt "${compatibility_version}" "${bin_version}";then
1578 exit_with_clean "${bin_version} is less than ${compatibility_version}. Please correct it and try again."
1579 fi
1580 echo "Binary check passed."
1581 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
可见,1576-1579行在检查版本号。于是从1576行知道fisco-bcos -v
可以查看二进制文件fisco-bcos
的版本号信息。另外,1578行在比较版本号和${compatibility_version}
之间的关系,而download_bin函数中指定下载版本(1549行,见前文)也是${compatibility_version}
,因此我们知道只要指定该字段为我们想要的版本号,即可解决fisco-bcos版本号问题。
那么我们追踪${compatibility_version}
这个变量:
1672 # use default version as compatibility_version
1673 if [ -z "${compatibility_version}" ];then
1674 compatibility_version="${default_version}"
1675 fi
# 1674行可见,如果没有指定版本号,则使用default_version来设置compatibility_version
50 compatibility_version=""
51 default_version="2.8.0"
# 51行可见,default_version是2.8.0
57 help() {
58 cat << EOF
59 Usage:
60 -l <IP list> [Required] "ip1:nodeNum1,ip2:nodeNum2" e.g:"192.168.0.1:2,192.168.0.2:3"
61 -f <IP list file> [Optional] split by line, every line should be "ip:nodeNum agencyName groupList p2p_port,channel_port,jsonrpc_port". eg "127.0.0.1:4 age ncy1 1,2 30300,20200,8545"
62 -v <FISCO-BCOS binary version> Default is the latest v${default_version}
# usage中又见可以通过-v来指定版本号
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
至此,我们知道可以通过fisco-bcos -v
查看我们需要的版本号信息,然后直接在运行build_chain.sh
时用-v
指定版本号。或者,也可以直接修改build_chain.sh
中的default_version。我们不妨采用第一种方案。
方案形成和实验
我新建一个目录用于实验。把build_chain.sh
和ipconf
复制近来,然后尝试我们的方案:
- 修改build_chain.sh
- 复制虚拟机上自带的fisco-bcos过来
- 离线运行脚本(把主机网线拔了hhh),注意指定版本号
sudo su
cd /root/tools
mkdir brtest
cp build_chain.sh brtest/
cp ipconf brtest/
cd brtest
mkdir nodes
mkdir nodes/192.168.157.128
mkdir nodes/192.168.157.129
cp ../webase-deploy/nodes/127.0.0.1/fisco-bcos nodes/192.168.157.129
cp ../webase-deploy/nodes/127.0.0.1/fisco-bcos nodes/192.168.157.128
cp ../webase-deploy/nodes/127.0.0.1/fisco-bcos nodes # 尽管最后的结果中nodes目录下是没有fisco-bcos的,但是在脚本运行过程中这里会有。如果不在这里复制一份fisco-bcos,运行脚本会报错:cp: 无法获取'/root/tools/brtest/nodes/fisco-bcos' 的文件状态(stat): 没有那个文件或目录
vim build_chain.sh
# 修改如下位置:
1654 #dir_must_not_exists "${output_dir}" # 因为我们手动创建了nodes目录,这里如果不注释掉这行,运行脚本会报错:[WARN] /root/tools/brtest/nodes DIR exists, please clean old DIR!
# 下面这部分注释掉,并添加一行:
1678 # download fisco-bcos and check it
1679 bin_path=${output_dir}/${bcos_bin_name} # 添加这行是因为这行是download_bin函数里的内容,它修改了全局变量bin_path,所以要加在这里,否则可能影响后面的代码运行
1680 #if [ -z ${docker_mode} ];then
1681 # if [[ -z ${bin_path} ]];then
1682 # download_bin
1683 # else
1684 # check_bin
1685 # fi
1686 #fi
# 网线拔掉然后继续(
# 首先查看我们的fisco-bcos的版本号
./nodes/fisco-bcos -v
FISCO-BCOS Version : 2.7.2
Build Time : 20210201 10:03:03
Build Type : Linux/clang/Release
Git Branch : HEAD
Git Commit Hash : 4c8a5bbe44c19db8a002017ff9dbb16d3d28e9da
# 运行脚本,注意指定版本号(从上面看到是2.7.2)
bash build_chain.sh -f ipconf -p 30330,20230,8545 -v 2.7.2
==============================================================
Generating CA key...
==============================================================
Generating keys and certificates ...
Processing IP=192.168.157.129 Total=2 Agency=agencyA Groups=1
Processing IP=192.168.157.129 Total=2 Agency=agencyB Groups=2
Processing IP=192.168.157.128 Total=3 Agency=agencyC Groups=1,2
==============================================================
Generating configuration files ...
Processing IP=192.168.157.129 Total=2 Agency=agencyA Groups=1
Processing IP=192.168.157.129 Total=2 Agency=agencyB Groups=2
Processing IP=192.168.157.128 Total=3 Agency=agencyC Groups=1,2
==============================================================
Group:1 has 5 nodes
Group:2 has 5 nodes
==============================================================
[INFO] IP List File : ipconf
[INFO] Start Port : 30330 20230 8545
[INFO] Server IP : 192.168.157.129:2 192.168.157.129:2 192.168.157.128:3
[INFO] Output Dir : /root/tools/brtest/nodes
[INFO] CA Path : /root/tools/brtest/nodes/cert/
==============================================================
[INFO] Execute the download_console.sh script in directory named by IP to get FISCO-BCOS console.
e.g. bash /root/tools/brtest/nodes/192.168.157.129/download_console.sh -f
==============================================================
[INFO] All completed. Files in /root/tools/brtest/nodes
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
这样看来,应该是成功了。
评论记录:
回复评论: