以太坊源码阅读01
正所谓区块链,怎能不熟悉区块的数据结构呢?区块的结构体被保存在core/types/block.go
文件中,下面是我截取出来的:
type Block struct {header *Headeruncles []*Headertransactions Transactionswithdrawals Withdrawals // cacheshash atomic.Valuesize atomic.Value // These fields are used by package eth to track// inter-peer block relay.ReceivedAt time.TimeReceivedFrom interface{} }
我之前主要使用C++语言,可以看出来上面的代码和C++语言还是有一定的差异的,C++结构体中先声明变量的类型,然后给出变量的名称,而上面的代码则是反过来了,先给出了成员变量的名称,然后给出了类型。下面是对各个字段的解释:
字段 | 描述 |
---|---|
header | 指向Header类型的指针,表示该区块的头部信息 |
uncles | 指向Header类型的数组,表示该区块的叔父区块 |
transactions | 区块所包含的交易列表 |
withdrawals | 区块中的提款列表 |
hash | 区块的哈希值 |
size | 区块的大小 |
ReceivedAt | 接收到该区块的时间 |
ReceivedFrom | 区块是被谁挖出来的 |
现在对于区块的内容已经有所了解了,再来看看区块头的数据结构:
type Header struct {ParentHash common.Hash `json:"parentHash" gencodec:"required"`UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`Coinbase common.Address `json:"miner"`Root common.Hash `json:"stateRoot" gencodec:"required"`TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`Bloom Bloom `json:"logsBloom" gencodec:"required"`Difficulty *big.Int `json:"difficulty" gencodec:"required"`Number *big.Int `json:"number" gencodec:"required"`GasLimit uint64 `json:"gasLimit" gencodec:"required"`GasUsed uint64 `json:"gasUsed" gencodec:"required"`Time uint64 `json:"timestamp" gencodec:"required"`Extra []byte `json:"extraData" gencodec:"required"`MixDigest common.Hash `json:"mixHash"`Nonce BlockNonce `json:"nonce"` // BaseFee was added by EIP-1559 and is ignored in legacy headers.BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` // WithdrawalsHash was added by EIP-4895 and is ignored in legacy headers.WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` // BlobGasUsed was added by EIP-4844 and is ignored in legacy headers.BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"` // ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers.ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"` // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` }
下面是对于字段的解释:
字段 | 描述 |
---|---|
ParentHash | 父区块的哈希值 |
UncleHash | 叔父区块的哈希值 |
Coinbase | 挖矿成功的奖励地址 |
Root | 状态树的根哈希值 |
TxHash | 交易树的根哈希值 |
ReceiptHash | 收据树的根哈希值 |
Bloom | 布隆过滤器 |
Difficulty | 挖矿难度 |
Number | 区块的编号 |
GasLimit | 汽油的最大量限制 |
GasUsed | 汽油的使用量 |
Time | 区块的生成时间 |
Extra | 附加数据 |
MixDigest | 区块的混合哈希值 |
Nonce | 挖矿所需要不断尝试的随机数(4字节) |
BaseFee | 基础手续费 |
WithdrawalsHash | 提款根的哈希值 |
BlobGasUsed | 新的燃气类型的使用量 |
ExcessBlobGas | 辅助识别处理异常情况,当恶意节点试图使用超额的新的燃气类型 |
ParentBeaconRoo | 跟踪对应的父分片,以太坊2.0引入了分片链的概念,每个分片都是相对独立的链,有一个对应的父分片,目的是提高网络的吞吐量和扩展性。 |
上面的字段中,最后五个字段是新提出来的,在传统的区块中会忽略对应的字段。接下来是区块体:
type Body struct {Transactions []*TransactionUncles []*HeaderWithdrawals []*Withdrawal `rlp:"optional"` }
字段 | 描述 |
---|---|
Transactions | 保存当前区块的所有交易列表 |
Uncles | 保存当前区块的所有叔父区块 |
Withdrawals | 保存当前区块的所有提款列表 |
type headerMarshaling struct {Difficulty *hexutil.BigNumber *hexutil.BigGasLimit hexutil.Uint64GasUsed hexutil.Uint64Time hexutil.Uint64Extra hexutil.BytesBaseFee *hexutil.BigHash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSONBlobGasUsed *hexutil.Uint64ExcessBlobGas *hexutil.Uint64 }
如上面结构体所示,以太坊提供了一个将区块头编码的结构体,将区块头中的部分变量换成了十六进制。在以太坊中,还提供了类型覆盖结构体,用于进行编码和解码时指定特定字段的编码方式,以满足不同的需求或协议规范。这种方式允许开发者根据实际情况定制编码和解码的行为,使得数据的序列哈和反序列化更加灵活和可定制。
以太坊中的区块头包含了诸多重要的信息,例如区块号、难度、气体限制、时间戳等等。在不同的协议中,可能需要不同的方式对这些数据进行编码和解码,以满足不同的需求,因此,使用覆盖结构体可以灵活的指定特定字段的编码方式,使得以太坊在不同的环境中具有更强的适应性和可扩展性。
在文件中,还有行代码:
type BlockNonce [8]byte
在工作量证明中,用户打包交易后生成Merck树,然后将其根哈希值保存到区块头中,对整个区块头取哈希的到一个值,这个值需要满足足够小的条件,由于哈希正向容易反向难的特点,矿工很难反向计算得到Nonce值,因此会一个一个数字去尝试,由于不同的矿工打包的交易可能不一样,因此矿工需要找到的Nonce值也可能不一样。上面这行代码说明了Nonce是一个8字节64位的值。通过查询资料得知,由于全网算力的不断增大,64位的难度难以满足要求,因此将Coinbase
字段的前4字节也作为取哈希的内容,这部分的内容可以用户自己设置,通常包含挖矿的奖励地址。
言归正传,先声明变量名,然后声明类型已经很奇怪了,后来发现还有更加怪异的,看下面的函数:
func (n BlockNonce) Uint64() uint64 {return binary.BigEndian.Uint64(n[:]) }
先不用管函数的意义,看看函数的声明是否很奇怪???通过查找资料发现:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {函数体 }
通过上网查询资料发现,接收者类型类似于C++中的this指针,而使用this指针时会修改实际的值,如果是传递一般的参数,则会拷贝一份副本,副本的改变不会导致实参的改变。接收者的声明规则如下:
-
接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
-
接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
-
方法名、参数列表、返回参数:具体格式与函数定义相同。
在Go语言中,nil
是一个预定义的标识符,用于表示指针、切片、映射、通道、函数和接口类型的零值或空值。具体含义取决于它所用于的类型。
-
对于指针类型,
nil
表示一个空指针,即指针未指向任何有效的内存地址。 -
对于切片、映射、通道、函数和接口类型,
nil
表示这些数据结构是空的,即切片、映射、通道为空,函数和接口未指向任何具体的实现或值。
在条件语句中,可以使用 nil
来检查指针、切片、映射、通道、函数和接口是否为零值或空值。
下面是以太坊外部区块的结构体:
type extblock struct {Header *HeaderTxs []*TransactionUncles []*HeaderWithdrawals []*Withdrawal `rlp:"optional"` }
-
Header
: 代表了区块的头部信息,包括区块的元数据,如区块号、时间戳、难度等。 -
Txs
: 是一个包含了所有交易的列表,每个交易都是一个Transaction
结构体的实例。 -
Uncles
: 是一个包含了叔块(Uncle Block)的列表,每个叔块也是一个Header
结构体的实例。 -
Withdrawals
: 是一个包含了所有提现的列表,每个提现也是一个Withdrawal
结构体的实例。
现在对于区块的相关结构体,我们已经有了一定的了解,那么来看看以太坊是如何产生新区块的。
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block { // 创建一个block对象,拷贝一个header对象来初始化block的header字段b := &Block{header: CopyHeader(header)} // 设置交易列表,当交易列表不为空时,根据交易列表计算出哈希值并赋值给TxHash字段并设置block的交易列表if len(txs) == 0 {b.header.TxHash = EmptyTxsHash} else {b.header.TxHash = DeriveSha(Transactions(txs), hasher)b.transactions = make(Transactions, len(txs))copy(b.transactions, txs)}// 设置收据树以及布隆过滤器if len(receipts) == 0 {b.header.ReceiptHash = EmptyReceiptsHash} else {b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)b.header.Bloom = CreateBloom(receipts)}// 设置叔节点if len(uncles) == 0 {b.header.UncleHash = EmptyUncleHash} else {b.header.UncleHash = CalcUncleHash(uncles)b.uncles = make([]*Header, len(uncles))for i := range uncles {b.uncles[i] = CopyHeader(uncles[i])}} return b } // 下面的函数允许区块指定额外的体现信息 func NewBlockWithWithdrawals(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, withdrawals []*Withdrawal, hasher TrieHasher) *Block {b := NewBlock(header, txs, uncles, receipts, hasher) if withdrawals == nil {b.header.WithdrawalsHash = nil} else if len(withdrawals) == 0 {b.header.WithdrawalsHash = &EmptyWithdrawalsHash} else {h := DeriveSha(Withdrawals(withdrawals), hasher)b.header.WithdrawalsHash = &h} return b.WithWithdrawals(withdrawals) } // 实现对区块头的深拷贝 func CopyHeader(h *Header) *Header {cpy := *hif cpy.Difficulty = new(big.Int); h.Difficulty != nil {cpy.Difficulty.Set(h.Difficulty)}if cpy.Number = new(big.Int); h.Number != nil {cpy.Number.Set(h.Number)}if h.BaseFee != nil {cpy.BaseFee = new(big.Int).Set(h.BaseFee)}if len(h.Extra) > 0 {cpy.Extra = make([]byte, len(h.Extra))copy(cpy.Extra, h.Extra)}if h.WithdrawalsHash != nil {cpy.WithdrawalsHash = new(common.Hash)*cpy.WithdrawalsHash = *h.WithdrawalsHash}if h.ExcessBlobGas != nil {cpy.ExcessBlobGas = new(uint64)*cpy.ExcessBlobGas = *h.ExcessBlobGas}if h.BlobGasUsed != nil {cpy.BlobGasUsed = new(uint64)*cpy.BlobGasUsed = *h.BlobGasUsed}if h.ParentBeaconRoot != nil {cpy.ParentBeaconRoot = new(common.Hash)*cpy.ParentBeaconRoot = *h.ParentBeaconRoot}return &cpy }
下面是一些函数的作用:
函数 | 功能 |
---|---|
func EncodeNonce(i uint64) BlockNonce | 将输入的64位无符号整数转换成字节数组,转换成BlockNorce类型(大端序) |
func (n BlockNonce) Uint64() uint64 | 将BlockNonce按照大端序转换成无符号64位整数 |
func (n BlockNonce) MarshalText() ([]byte, error) | 将BlockNonce类型的值编码为带有0x前缀的十六进制字符串 |
func (n *BlockNonce) UnmarshalText(input []byte) error | 从带有0x前缀的十六进制字符串解码并填充BlockNonce类型的值 |
func (h *Header) Hash() common.Hash | 计算区块头的哈希值 |
func (h *Header) Size() common.StorageSize | 区块头所有内容所占内存大小 |
func (h *Header) SanityCheck() error | 判断区块头部字段是否在合理范围内 |
func (h *Header) EmptyBody() bool | 检查头部信息的主体部分是否是空 |
func (h *Header) EmptyReceipts() bool | 检查头部的收据是否为空 |
func (b *Block) DecodeRLP(s *rlp.Stream) error | 从流中解码区块数据 |
func (b *Block) EncodeRLP(w io.Writer) error | 将区块数据序列格式化RLP格式,并写入流中 |
此外还提供了一个更换区块区块头、更换区块体、更换交易列表等函数。
相关文章:
以太坊源码阅读01
正所谓区块链,怎能不熟悉区块的数据结构呢?区块的结构体被保存在core/types/block.go文件中,下面是我截取出来的: type Block struct {header *Headeruncles []*Headertransactions Transactionswithdrawals Withdr…...
Spark-Scala语言实战(15)
在之前的文章中,我们学习了如何在spark中使用键值对中的学习键值对方法中的lookup,cogroup两种方法。想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你宝贵的点赞&#…...
【SpringBoot XSS存储漏洞 拦截器】Java纯后端对于前台输入值的拦截校验实现 一个类加一个注解结束
先看效果: 1.js注入拦截: 2.sql注入拦截 生效只需要两步: 1.创建Filter类,粘贴如下代码: package cn.你的包命.filter; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IO…...
【微信小程序】canvas开发笔记
【微信小程序】canvasToTempFilePath:fail fail canvas is empty 看说明书 最好是先看一下官方文档点此前往 如果是canvas 2d 写canvas: this.canvas,,如果是旧版写canvasId: ***, 解决问题 修改对应的代码,如下所示,然后再试试运行&#x…...
TripoSR: Fast 3D Object Reconstruction from a Single Image 论文阅读
1 Abstract TripoSR的核心是一个基于变换器的架构,专为单图像3D重建设计。它接受单张RGB图像作为输入,并输出图像中物体的3D表示。TripoSR的核心包括:图像编码器、图像到三平面解码器和基于三平面的神经辐射场(NeRF)。…...
u盘为什么一插上电脑就蓝屏,u盘一插电脑就蓝屏
u盘之前还好好的,可以传输文件,使用正常,但是最近使用时却出现问题了。只要将u盘一插入电脑,电脑就显示蓝屏。u盘为什么一插上电脑就蓝屏呢?一般,导致的原因有以下几种。一,主板的SATA或IDE控制器驱动损坏…...
【Redis】redis面试相关积累
Redis到底是多线程还是单线程? Redis 在设计上是单线程的,这意味着 Redis 服务器在任何给定时刻只能执行一个命令。然而,这并不意味着 Redis 无法利用多核 CPU,因为 Redis 使用了一些技术来提高性能和并发性,例如非阻…...
【Linux】进程的状态(运行、阻塞、挂起)详解,揭开孤儿进程和僵尸进程的面纱,一篇文章万字讲透!!!!进程的学习②
目录 1.进程排队 时间片 时间片的分配 结构体内存对齐 偏移量补充 对齐规则 为什么会有对齐 2.操作系统学科层面对进程状态的理解 2.1进程的状态理解 ①我们说所谓的状态就是一个整型变量,是task_struct中的一个整型变量 ②.状态决定了接下来的动作 2.2运行状态 2.…...
前端js基础知识(八股文大全)
一、js的数据类型 值类型(基本类型):数字(Number)、字符串(String)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol,大数值类型(BigInt) 引用数据类型:对象(Object)、数组…...
316_C++_xml文件解析成map,可以放到表格上 + xml、xlsx文件互相解析
xml文件例如: <?xml version"1.0" encoding"UTF-8" standalone"yes"?> <TrTable> <tr id"0" label"TR_PB_CH" text"CH%2"/> <tr id"4" label"TR_PB_CHN"…...
未来汽车硬件安全的需求(2)
目录 4.汽车安全控制器 4.1 TPM2.0 4.2 安全控制器的硬件保护措施 5. EVITA HSM和安全控制器结合 6.小结 4.汽车安全控制器 汽车安全控制器是用于汽车工业安全关键应用的微控制器。 他们的保护水平远远高于EVITA HSM。今天的典型应用是移动通信,V2X、SOTA、…...
html+javascript,用date完成,距离某一天还有多少天
图片展示: html代码 如下: <style>* {margin: 0;padding: 0;}.time-item {width: 500px;height: 45px;margin: 0 auto;}.time-item strong {background: orange;color: #fff;line-height: 100px;font-size: 40px;font-family: Arial;padding: 0 10px;margin-right: 10px…...
跟bug较劲的第n天,undefined === undefined
前情提要 场景复现 看到这张图片,有的同学也许不知道这个冷知识,分享一下,是因为我在开发过程中踩到的坑,花了三小时排查出问题的原因在这,你们说值不值。。。 我分享下我是怎么碰到的这个问题,下面看代码…...
数据结构_基于链表的通讯录
顺序表的源代码需要略作修改,如下 将数据类型改为通讯录的结构体。注释掉打印,查找的函数。 SList.h #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> #include<stdlib.h> #include<assert.h> #include"Contact.h"ty…...
jenkins+gitlab配置
汉化 1、安装Localization: Chinese (Simplified)插件 (此处我已安装) (安装完成后重启jenkins服务即可实现汉化) 新增用户权限配置 1、安装插件 Role-based Authorization Strategy 2、全局安全配置 3、配置角色权限 4、新建…...
【Labview】虚拟仪器技术
一、背景知识 1.1 虚拟仪器的定义、组成和应用 虚拟仪器的特点 虚拟仪器的突出特征为“硬件功能软件化”,虚拟仪器是在计算机上显示仪器面板,将硬件电路完成信号调理和处理功能由计算机程序完成。 虚拟仪器的组成 硬件软件 硬件是基础,负责将…...
IvorySQL 3.2原理解析|与Oracle 12c XML函数兼容性的实现机制
[发行日期:2024年4月11日] IvorySQL 3.2基于PostgreSQL 16.2,引入了多种Oracle XML函数的全面兼容性功能,同时修复了多个问题,更多信息请参考文档网站。 >>>新版本体验链接: https://docs.ivorysql.org/cn…...
SpringBoot + Dobbo + nacos
SpringBoot Dobbo nacos 一、nacos https://nacos.io/zh-cn/docs/quick-start.html 1、下载安装包 https://github.com/alibaba/nacos/releases/下载后在主目录下,创建一个logs的文件夹:用来存日志 2、启动nacos 在bin目录下打开cmd运行启动命令&a…...
学习笔记-微服务基础(黑马程序员)
框架 spring cloudspring cloud alibaba Eureka eureka-server 注册中心 eureka-client 客户端每30s发送心跳服务 服务消费者服务提供者 server 依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-star…...
每日Bug汇总--Day05
Bug汇总—Day05 一、项目运行报错 二、项目运行Bug 1、**问题描述:**前端将从后台查询的数据作为参数进行get请求,参数为空 原因分析: 这种写法可能只支全局的参数调用方法的传参响应 代码实现 if (this.jishiName) {this.$http({url…...
docker、ctr、crictl命令对比
命令dockerctr(containerd)crictl(kubernetes)查看运行的容器docker psctr task ls/ctr container lscrictl ps查看镜像docker imagesctr image lscrictl images查看容器日志docker logs无crictl logs查看容器数据信息docker insp…...
uniapp 编译后分包下静态图片404问题解决方案
如上图官方说明: 在分包下建立一个static文件夹即可: 分包内代码引用图片 <image src"/分包名称/img/图片名称"></image> <image src"/dataView/img/图片名称"></image>...
第十二届蓝桥杯大赛软件赛省赛Java 大学 B 组题解
1、ASC public class Main {public static void main(String[] args) {System.out.println(...
关于openai和chatgpt、gpt-4、PyTorch、TensorFlow 两者和Transformers的关系
近两年,随着人工智能的火爆,不论通过哪个渠道,相信我们都听说过openai、gpt等这类名词,那么它们到底是什么意思,请看下文。 openai:是一家人工智能公司; openai-api:是openai提供的api…...
C 共用体
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。 定义共用体 为了定义共用体,您必须使用 u…...
智能合约:未来数字经济的基石
智能合约是一种自动执行交易的计算机协议,它以代码形式规定了交易双方的权利和义务,具有高度的可靠性和安全性。随着数字经济的发展,智能合约的重要性日益凸显,将成为未来数字经济的基石。 首先,智能合约在金融领域的应…...
第十一届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
第十一届蓝桥杯大赛软件赛省赛C/C 大学 B 组 文章目录 第十一届蓝桥杯大赛软件赛省赛C/C 大学 B 组1、字串排序2、门牌制作3、既约分数4、蛇形填数5、跑步锻炼6、七段码7、成绩统计8、回文日期9、子串分值和10、平面切分 1、字串排序 // 转载博客链接 https://blog.csdn.net/we…...
Lua语法(三)——元表与元方法
参考链接: 系列链接: Lua语法(一) 系列链接: Lua语法(二)——闭包/日期和时间 系列链接: Lua语法(三)——元表与元方法 系列链接: Lua语法(四)——协程 系列链接: Lua语法(五)——垃圾回收 系列链接: Lua语法(六)——面相对象编程 元表与元方法目录 简介正文元表元方法表相关常…...
Lua语法(五)——垃圾回收
参考链接: 系列链接: Lua语法(一) 系列链接: Lua语法(二)——闭包/日期和时间 系列链接: Lua语法(三)——元表与元方法 系列链接: Lua语法(四)——协程 系列链接: Lua语法(五)——垃圾回收 系列链接: Lua语法(六)——面相对象编程 Lua语法 五——垃圾回收 垃圾回收弱引用表__m…...
已解决java.net.NoRouteToHostException: 无法到达主机异常的正确解决方法,亲测有效!!!
已解决java.net.NoRouteToHostException: 无法到达主机异常的正确解决方法,亲测有效!!! 目录 问题分析 报错原因 解决思路 解决方法 检查网络连接 核实目标地址 检查防火墙和路由器规则 验证VPN/代理设置 修正网络配置 …...
网络培训法/seo外链推广工具下载
原标题:一加6T被曝将预装Android P 这一点其它品牌比不了近年来国产手机群雄崛起,不仅在国内获得了很高的呼声,一些品牌在国外也拥有着很高的热度,从以往的三星、苹果手中抢占了很多的份额,如今三星在国内的销量非常惨…...
网站建设常用工具/怎样推广app
场景 通过上一节的学习我们掌握了如何生成并发布接口文档,可是现实总是残酷的。前端团队往往不满足于干巴巴的接口文档,他们需要对接真实的数据,但这样很受限于后端开发进度,于是便有了Mock(模拟)这种东西,也就是我们…...
iis6.1的网站建设及权限设置/互联网下的网络营销
//阿里面试//2010 年,阿里正当红。那年 BAT 的名气还没有现今那么响亮。就业的时候,很多毕业生还是会选择 HP, IBM, EMC, SONY, ORACLE,Microsoft 等一类的外企。这些外企在毕业生眼中代表的是名牌,权威,以及自由&…...
学校网站免费建设/经典广告
在一些批处理或者系统技巧操作教程文章中,我们常常会看到一些形如 %windir% 或者 %systemdrive% 的变量。这些变量都代表着什么含义呢?下面小技巧之家为大家整理了在Windows XP下系统变量方式表达相对应的路径,大家可以看得更加清楚明白了&am…...
网站建设的相关职位/深圳创新创业大赛
我有位实业朋友特别推崇小米,因为小米崛起很快,销售额很大,估值很高、风投竞相涌入,小米既做研发又做制造又做电商也很成功、品牌塑造也很成功(有拥趸粉丝),而且小米做的是很重的(手…...
吕梁网站制作吕梁安全/中文域名注册
从一个运行了RTX系统的程序中跳转到另一个带有RTX系统的程序时,程序卡在RTX初始化中,在跳转前关闭滴答定时器中断,跳转正常 http://www.keil.com/support/docs/3925.htm...