U-Boot 基础概念与学习分享

  • Board: rockchip-px30, armv8, Cortex-A35
  • U-Boot: rockchip-linux/u-boot, branch next-dev
  • Tools: VScode, Exuberant CTags

1. 前言

学习 u-boot 启动流程有些时日了, 虽然看了大量的文章以及在此期间仔细阅读源码, 但是仍感觉很多知识点掌握不深刻容易遗忘,不如在写博文博文的时候重溯整个流程, 也分享自己学习 u-boot 的学习路线方便后来者入门。

2. 学习路线

一般来说芯片公司会提供相关的手册介绍各个组件, 这对了解特定型号的开发板是很有效的, 但不适合初学者进行系统的学习, 建立全局概念应当是第一位的。 学习 u-boot 应当对以下几个方面有所了解:

  • ARM Assembly, 推荐 Kyle BaldwinARM Assembly By Example, 我自己在 github 上也实现了相关的 lab。
  • Device Tree, The DeviceTree Specification 结合具体的 dts/dtsi 文件阅读学习。
  • Linker Script, GNU Binutils 官方的ld文档合具体的lds文件进行阅读学习, 结合 程序员的自我修养--链接装载与库 这本书建立相关概念。
  • Kconfig, Kconfig Language 同样用在了 u-boot 中需要对其有一定的了解。

如果对 DM(Driver Model) 有兴趣, 可以阅读 Linux Device Driver, Third Edition,但具备以上几块知识已经足够理解 u-boot 启动流程。 Das U-Boot 提供的文档以及 u-boot 源码中提供的 README 这类文档不需要仔细读完, 这些仅供学习参考, 但 elinux_talks 这部分资源也值得查阅。

当然还是那句话, 全局概念非常重要, 先啃源码建立相关概念, 带着问题探究细节会事半功倍。

3. U-Boot 框架与启动阶段

3.1 U-Boot 架构分析

u-boot 的开发者在开发文档中描述目录的层次结构, 但缺少更为宏观的概括。 以 rockchip-px30 为例, 其在 u-boot 中的文件可被划归为以下几类。以CPU,ARCH,Board 三级对文件进行划分可以帮助我们在配置新板时有更清晰的规划。 Quentin Schulz 在 2017 年的嵌入式 linux 欧洲会议上的演讲 Porting U-Boot and Linux on new ARM boards: a step-by-step guide 则具体介绍了详细的实施步骤。

CPU (armv8), ARCH (arm), Board(px30)

  • CPU 层级依赖文件

    • arch/arm/cpu/armv8/*c;*S;*lds
    • arch/arm/include/asm/armv8/*h
  • ARCH(arm) 层级依赖文件
    • arch/arm/lib/*c;*S;*lds
    • arch/arm/include/asm/*h;*S
  • Board 层级依赖文件
    • board/rockchip/evb_px30/*c
    • arch/arm/mach-rockchip/px30/*c
  • Board 层级配置文件
    • arch/arm/include/asm/arch-rockchip/*h;*S
    • include/rockchip/*h
    • include/px30_common.h;evb_px30.h
  • Board 层级非依赖文件
    • common(cmd, flash, env, usb, ...), disk(partition)
    • drivers, fs, net, lib



U-Boot Hierarchy, HangX-Ma

u-boot 的初始化过程就是 CPU \(\rightarrow\) ARCH \(\rightarrow\) Board 的过程, 但并不严格划归, ARCH部分的通用代码会调用 Board 相关的接口。 在 wowo 的文章中提到曾经存在于 ARCH 和 Board 之间的 machine 层级由于最新的ARM64架构引入了 device tree 的缘故, 已经将 machine 概念删除了, 在当前 u-boot 中看到的 mach-xxx 的目录或文件就属于 machine 层级, 虽然 u-boot 还未更新相关的架构概念, 但在开发层面 u-boot 和 linux 内核几乎同时适用了 device tree, 这意味着 u-boot 也很可能在之后的更新中删除类似的 mach-xxx 文件。

3.1.1 举例——从 Kconfig 自底向上

Kconfig 中自底向上梳理整个编译框架, 假设我们使用的目标板是 rockchip-px30 系列的 evb-px30, 那么 board/rockchip/evb_px30 文件夹中定义了目标板的一些依赖代码, 在 include/configs/evb_px30.h 会有该目标板的配置信息, 类似的配置信息和编译是息息相关的需要格外留意, 后续不过提点。

从顶层的 board/rockchip/evb_px30/Kconfig 查看, 可以找到 TARGET_EVB_PX30 整个关键量以及定义的 BORADVENDOR 等编译相关的变量。

# board\rockchip\evb_px30\Kconfig
if TARGET_EVB_PX30 config SYS_BOARD
default "evb_px30" config SYS_VENDOR
default "rockchip" config SYS_CONFIG_NAME
default "evb_px30" config BOARD_SPECIFIC_OPTIONS # dummy
def_bool y endif

顺着前述所提及的关键量, 在 arch/arm/mach-rockchip/px30/Kconfig 中能找到引用信息(尤其是 source 了前述的 Kconfig 文件), 由于当前使用的就是 evb-px30 板, EVB_PX30bool 变量是 true。 可以看到该 Kconfig 文件在框架中属于亟待更新的 machine 层级, 所以在该部分可以看到 SYS_SOC 这个配置变量。 在该 Kconfig 文件中还覆盖定义了 SYS_MALLOC_F_LENSPL_SERIAL_SUPPORT

# arch/arm/mach-rockchip/px30/Kconfig
if ROCKCHIP_PX30 config TARGET_EVB_PX30
bool "EVB_PX30"
select BOARD_LATE_INIT config SYS_SOC
default "rockchip" config SYS_MALLOC_F_LEN
default 0x400 config SPL_SERIAL_SUPPORT
default y source "board/rockchip/evb_px30/Kconfig" endif

在更上一级目录则看到更为通用的 Kconfig 文件会配置 ROCKCHIP_PX30 这个定义量。 可以看到在该目录下配置了 px30 系列使用默认配置。 我们再向上一级的查找 ARCH_ROCKCHIP 变量以其找到顶层的 xxx_defconfig 配置文件。

# arch\arm\mach-rockchip\Kconfig
if ARCH_ROCKCHIP config ROCKCHIP_PX30
bool "Support Rockchip PX30"
select ARM64 if !ARM64_BOOT_AARCH32
select GICV2
select ARM_SMCCC
select SUPPORT_SPL
select SUPPORT_TPL
select SPL if !ARM64_BOOT_AARCH32
select TPL if !ARM64_BOOT_AARCH32
select TPL_TINY_FRAMEWORK if TPL imply SPL_SEPARATE_BSS
imply SPL_SERIAL_SUPPORT
imply TPL_SERIAL_SUPPORT
help
The Rockchip PX30 is a ARM-based SoC with a quad-core Cortex-A35
including NEON and GPU, Mali-400 graphics, several DDR3 options
and video codec support. Peripherals include Gigabit Ethernet,
USB2 host and OTG, SDIO, I2S, UART, SPI, I2C and PWMs. if ROCKCHIP_PX30 config TPL_LDSCRIPT
default "arch/arm/mach-rockchip/u-boot-tpl-v8.lds" config TPL_TEXT_BASE
default 0xff0e1000 config TPL_MAX_SIZE
default 10240 config ROCKCHIP_RK3326
bool "Support Rockchip RK3326 "
help
RK3326 can use most code from PX30, but at some situations we have
to distinguish between RK3326 and PX30, so this macro gives help.
It is usually selected in rk3326 board defconfig.
endif
...

在更上一级, 我们先找到了 arch\arm\Kconfig, ARCH 层级的默认配置。

# arch\arm\Kconfig
...
config ARCH_ROCKCHIP
bool "Support Rockchip SoCs"
select OF_CONTROL
select BLK
select DM
select SPL_DM if SPL
select SYS_MALLOC_F
select SYS_THUMB_BUILD if !ARM64
select SPL_SYS_MALLOC_SIMPLE if SPL
imply DM_GPIO
select DM_SERIAL
select DM_SPI
select DM_SPI_FLASH
select DM_USB if USB
select CMD_ROCKUSB if USB_GADGET_DOWNLOAD
select ENABLE_ARM_SOC_BOOT0_HOOK
select SYS_NS16550
select SPI
select DEBUG_UART_BOARD_INIT
select PANIC_HANG
imply DM_MMC
imply DM_I2C
imply DM_PWM
imply DM_REGULATOR
imply CMD_FASTBOOT
imply FASTBOOT
imply FAT_WRITE
imply USB_FUNCTION_FASTBOOT
imply USB_FUNCTION_ROCKUSB
imply SPL_SYSRESET
imply TPL_SYSRESET
imply ADC
imply SARADC_ROCKCHIP
...

最终我们能在 configs/evb-px30_defconfig(Target) 中找到用户自定义的基本宏信息, 另外一些信息则在前述提及的配置文件中。 例如 include/configs/px30_common.hinclude/configs/evb_px30.h, 以及 include/configs/rockchip-common.h。 我们自底向上, 特定的板级文件开始溯源, 找到了最终顶层的配置文件。 根据顶层的配置文件以及每个层级的配置文件可以梳理出编译特定板所需的功能。 另外, 在底层的 TARGET 的配置中可以看到诸如 SYS_xxx 的一系列配置, 这些配置会在更上层的 arch\Kconfig 中定义。 所以综上可以总结出如下配置关系图。

flowchart
subgraph Target
evb-px30_defconfig
end
subgraph arch
subgraph arm
subgraph mach-rockchip
subgraph px30
Board/evb-px30
end
end
end
end
style Target fill:#A44,stroke:#333,stroke-width:4px
style arch fill:#A44,stroke:#333,stroke-width:4px
style arm fill:#A43,stroke:#333,stroke-width:4px
style mach-rockchip fill:#BBA,stroke:#333,stroke-width:4px
style px30 fill:#444,stroke:#333,stroke-width:4px
Target -.-> arch

3.2 Boot Loader Stage

BLx(Boot Loader Stage) 指代 Boot Loader 的各个阶段, 具体的划分根据 u-boot 初始化时所在存储设备略有不同, 一般将 u-boot 启动划分为 4 个阶段, BL0, BL1, BL2, BL3。值得注意的是这与 ARM TrustZone 的划分非属同源, 在 ARM TrustZone 的划分中, u-boot 属于 BL33 Non-secure 部分。

  • BL0, SOC 生产厂家固化在 iROM(Internal ROM) 中的启动代码, 主要负责加载 BL1 的程序, 该部分被称作 Initial Program Loader (IPL) 或者 Primary Program Loader (PPL)。
  • BL1, 该部分被称为 SPL(Secondary Program Loader), 若 SPL 部分仍超过了 flash 存储限制, 首先会通过 TPL(Trinary Program Loader) 进行更简洁的初始化如 DDR 部分的初始化,以保证代码体积极小, 之后再从指定位置加载 SPL 继续执行初始化。
  • BL2, 该阶段 u-boot 运行程序重定位之前的部分, 主要负责一系列初始化操作以及构建 C 语言的运行环境, 最为关键的是将 u-boot 重定位至 DRAM/SDRAM 中继续执行 BL3 阶段的程序。
  • BL3, 在该阶段实质上加载了u-boot, 当然通过 ATF(Architecture Trusted Firmware) 加载也是可以的。 该阶段在负责初始化 SOC 的外设, 准备内核启动参数以及加载运行内核等操作。



Boot loader sequences, HouchengLin

根据以上描述, 以图例形式表述 u-boot 的启动流程应当如下所示。

flowchart LR
p1(BootROM)
p2(TPL, optional)
p3(SPL)
p4(ATF, optional)
p5(U-Boot)
p6(Kernel)
p1 --> p2 --> p3 --> p4 --> p5 --> p6
p1 -.-> p3
p3 -.-> p5
p1 ==> p5

4. 浅析 TPL

嵌入式的代码铁定有个名为 start.S 的入口汇编代码, 但在进行源码分析之前, 我比较喜欢阅读链接脚本以此获悉 u-boot 的构成以及分析启动过程中的一些工作。 在 arch/arm/cpu/armv8 目录下有两个 lds 文件, armv8 的 BootROM \(\rightarrow\) u-boot 的引导使用 u-boot.lds 进行链接, 而在 u-boot 之前存在 SPL/TPL 阶段则会使用 u-boot-spl.ldsarch/arm/mach-rockchip/u-boot-tpl-v8.lds 进行链接。

在上述流程中提及 TPL 的存在, 这也是让我比较困惑的, TPL 如何 与 SPL 进行配合实现对 bootloader 的引导启动, 这一块内容值得深入探究。 不妨先从 TPL 的链接脚本入手, 厘清 TPL 阶段的相关逻辑。

这两篇文章关于 u-boot-spl.lds 有着不同详略的解析, 可以用以了解 u-boot 相关的链接脚本的 section 的基本功能以及了解链接脚本的基本概念, 这些内容已有前人做了充分的解析不再赘述。

4.1 TPL Configurations

根据前述配置, evb-px30 在启动时会经由 TPL 以及 SPL 引导 u-boot。 在 arch/arm/mach-rockchip/Kconfig 中可以看到与 TPL 相关的一些定义:TPLTPL_TINY_FRAMEWORKTPL_TINY_FRAMEWORKTPL_LDSCRIPTTPL_TEXT_BASETPL_MAX_SIZESUPPORT_TPL, 这些宏定义会影响后续编译的过程。

其中, CONFIG_TPL_BUILD 这个宏定义非常重要。 网上很多博客提及相关内容仅说明在定义 CONFIG_TPL 之后 CONFIG_TPL_BUILD 会自动定义, 但没有详细说明具体位置。 在 scripts/Makefile.autoconf 文件的 85-87 行可以看到几行规则, 实际上向 tpl/u-boot.cfg 传递了 CONFIG_SPL_BUILDCONFIG_TPL_BUILD 这两个量。 在其他任何 config.mkKconfigKbuild 这样的文件中都不会找到这两个量的定义。

# scripts/Makefile.autoconf
tpl/u-boot.cfg: include/config.h FORCE
$(Q)mkdir -p $(dir $@)
$(call cmd,u_boot_cfg,-DCONFIG_SPL_BUILD -DCONFIG_TPL_BUILD)

另外在顶层的 Makefile 文件我们可以找到这样一则规则, 是 script/Makefile.autoconf 的上一级引用。

# Makefile
u-boot.cfg spl/u-boot.cfg tpl/u-boot.cfg: include/config.h FORCE
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf $(@)

而当我们查看 script/Makefile.autoconf 所描述的功能时, 可以看到前述的 CONFIG_SPL_BUILDCONFIG_TPL_BUILD 以及其他生成的宏定义最终会被转移到 Kconfig 中以完成全局性的定义。

# This helper makefile is used for creating
# - symbolic links (arch/$ARCH/include/asm/arch
# - include/autoconf.mk, {spl,tpl}/include/autoconf.mk
# - include/config.h
#
# When our migration to Kconfig is done
# (= When we move all CONFIGs from header files to Kconfig)
# this makefile can be deleted.

4.2 TPL Linker Script

BootROM 完成基本的初始化后首先会在 iRAM 中载入 TPL 段的运行代码。

OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start)
SECTIONS
{
. = 0x00000000; .text : {
. = ALIGN(8);
*(.__image_copy_start)
CPUDIR/start.o (.text*)
*(.text*)
} .rodata : {
. = ALIGN(8);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
} .data : {
. = ALIGN(8);
*(.data*)
} .u_boot_list : {
. = ALIGN(8);
KEEP(*(SORT(.u_boot_list*)));
} .image_copy_end : {
. = ALIGN(8);
*(.__image_copy_end)
} .end : {
. = ALIGN(8);
*(.__end)
} _image_binary_end = .; .bss_start (NOLOAD) : {
. = ALIGN(8);
KEEP(*(.__bss_start));
} .bss (NOLOAD) : {
*(.bss*)
. = ALIGN(8);
} .bss_end (NOLOAD) : {
KEEP(*(.__bss_end));
} /DISCARD/ : { *(.dynsym) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
} #if defined(CONFIG_TPL_MAX_SIZE)
ASSERT(__image_copy_end - __image_copy_start < (CONFIG_TPL_MAX_SIZE), \
"TPL image too big");
#endif #if defined(CONFIG_TPL_BSS_MAX_SIZE)
ASSERT(__bss_end - __bss_start < (CONFIG_TPL_BSS_MAX_SIZE), \
"TPL image BSS too big");
#endif #if defined(CONFIG_TPL_MAX_FOOTPRINT)
ASSERT(__bss_end - _start < (CONFIG_TPL_MAX_FOOTPRINT), \
"TPL image plus BSS too big");
#endif

ENTRY(_start) 实际上声明了程序的入口地址, 对 TPL 而言这是显而易见的, 因为 TPL 需要在该阶段获得程序的控制权完成一系列基本的初始化进程。 与其他 ld 文件不同的是, TPL 的链接脚本对 TPL 程序本身的大小有严格的控制。 在 machine 级的 arch/arm/mach-rockchip/Kconfig 中我们定义了 TPL_MAX_SIZE, 这使得我们可以检查 TPL image 的大小以满足 iRAM 的空间限制要求。一般来说,__image_copy_start__image_copy_end 这两个变量常用来辅助 u-boot 的重定位, 但在此处被赋予了新的功能。 另外可以看到 bss 段都被声明了 NOLOAD 属性, 这意味着 bss 段在 image 中并不占用任何空间, 但相关的地址信息会被保留用以在 u-boot加载时的一些数据初始化操作。 因而可以归纳得到 TPL 加载时实际的内存分布情况。




TPL Loading Memory, HangX-Ma

另外可以从 ld 文件中看到, 入口程序是 CPUDIR/start.o, CPUDIR 可以依据层级划分从各个较为顶层的 Makefile 文件中找到具体定义。 但根据架构分析中的概念, 不难得出此处的 CPUDIRarch/arm/armv8start.S 最终会定位到 _main 程序入口继续执行流程。(关于 start.S 的详细流程可以参考 ARMv8架构u-boot启动流程详细分析(二), 内核新视界) 由于我们的编译是 AArch64 架构, 那么 C Runtime Environment 的建立也应当是 crt0_64.S, 可以在这个文件中看到, board_init_f_alloc_reserveboard_init_f_init_reserveboard_init_f_boot_flags 几个函数通过在栈顶预留内存来达到给 GD(Global Data) 开辟内存空间, 在 AArch64 架构中 GD 指针地址会被保留在 x18 寄存器中供全局使用, 之后跳转到 board_init_f。 这是一个分水岭, TPL, SPL 以及 u-boot 都会执行这个函数。

一般来说可以将 u-boot 的启动过程划分为两个阶段, 也就是前述的 BL2 和 BL3 的区分。 Pre-relocation(common/board_f.c), 此处的 f 表示程序执行所在的存储介质是 flash, 以及 After-relocation(common/board_r.c), 此处的 r 表示程序执行所在的存储介质是 RAM

我们知道 TPL 只完成一些很基本的初始化流程, 对于 TPL 而言实际上不存在重定位的需求, 所以关键就在 board_init_f 这个函数。

  • SPL: arch\arm\mach-rockchip\spl.c
  • TPL: arch\arm\mach-rockchip\tpl.c
  • U-Boot: common\board_f.c

在编译链接时, 编译组件就会对这几个文件进行区分, 以保证绑定正确的可执行文件。 在 arch\arm\mach-rockchip\Makefile 中就巧妙的在编译 TPL 文件时取消了 SPL 相关文件的生成, 而在编译 SPL 文件时则不受 TPL 的相关定义影响。

根据之前的宏定义梳理的在 TPL 阶段的 board_init_f 所做的工作如下, 时钟初始化, CPU 部分初始化, UART 串口初始化, SDRAM 初始化。 这些工作都完成之后会通过 arch\arm\mach-rockchip\Kconfig 默认定义的 TPL_ROCKCHIP_BACK_TO_BROM 宏引导的 back_to_bootrom 返回 BootROM 阶段再进行下一阶段的 SPL。

board_init_f
rockchip_stimer_init
arch_cpu_init
debug_uart_init
timer_init
sdram_init
back_to_bootrom
flowchart LR
p1(BootROM)
p2(TPL)
p3(SPL)
p4(U-Boot)
p5(Kernel)

p1 -.->|stage 1 'init'| p2
p2 -.->|stage 2 'back to rom'| p1
p1 -->|stage 3| p3
p3 --> p4
p4 --> p5

至于 SPL 的具体流程可以参考 TPL 的流程进行推导相关的资料也非常详细, 在参考部分的 U-Boot 部分已经列举了筛选过的较好的资料可供选读。

5. 总结

文章对 u-boot 学习路线进行了简单介绍, 并从 u-boot 构建框架着手解构 u-boot, 以 Kconfig 为索引文件自底向上分析框架。 除此之外还介绍了 Boot Loader 的几个基本流程, 对其中的 TPL 过程进行了剖析。后续会在此篇博文的基础上进行增改扩充基础概念部分, 而其他需要仔细剖析的部分则另建博文进行阐述。

6. 参考

6.1 U-Boot

6.2 ARM 参考手册

6.3 AArch64 架构

U-Boot-基础概念与学习分享的更多相关文章

  1. kettle基础概念的学习

    参考书籍:Pentaho Kettle Solutions中文版.由于最近不断的使用kettle,随着不断深入使用,遇到的问题越来越多,发现脑子那点货根本不够用,所以根据阅读把一些概念记录一下,方便自 ...

  2. Vue基础概念,学习环境等

    前提: 你已有 HTML.CSS 和 JavaScript 中级前端知识. 概念: Vue.js(读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式框架.与其他重量级框架不同的是,Vu ...

  3. Json学习一(基础概念知识学习)

    1.Json简单介绍 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它使得人们非常easy的进行阅读和编写. 同一时候也方便了机器进行解析和生成.它是基 ...

  4. 【强化学习RL】必须知道的基础概念和MDP

    本系列强化学习内容来源自对David Silver课程的学习 课程链接http://www0.cs.ucl.ac.uk/staff/D.Silver/web/Teaching.html 之前接触过RL ...

  5. Elasticserach学习笔记-01基础概念

    本文系本人根据官方文档的翻译,能力有限.水平一般,如果对想学习Elasticsearch的朋友有帮助,将是本人的莫大荣幸. 原文出处:https://www.elastic.co/guide/en/e ...

  6. 分布式强化学习基础概念(Distributional RL )

    分布式强化学习基础概念(Distributional RL) from: https://mtomassoli.github.io/2017/12/08/distributional_rl/ 1. Q ...

  7. 【转】Pro Android学习笔记(二):开发环境:基础概念、连接真实设备、生命周期

    在Android学习笔记(二):安装环境中已经有相应的内容.看看何为新.这是在source网站上的Android架构图,和标准图没有区别,只是这张图颜色好看多了,录之.本笔记主要讲述Android开发 ...

  8. C#学习基础概念二十五问

    C#学习基础概念二十五问 1.静态变量和非静态变量的区别?2.const 和 static readonly 区别?3.extern 是什么意思?4.abstract 是什么意思?5.internal ...

  9. 『深度应用』NLP机器翻译深度学习实战课程·零(基础概念)

    0.前言 深度学习用的有一年多了,最近开始NLP自然处理方面的研发.刚好趁着这个机会写一系列NLP机器翻译深度学习实战课程. 本系列课程将从原理讲解与数据处理深入到如何动手实践与应用部署,将包括以下内 ...

  10. Kubernetes 学习笔记(一):基础概念

    个人笔记,仅本人查阅使用,不保证正确. 零.微服务 微服务架构专注于应用解耦合,通过将应用彻底地组件化和服务化,每个微服务只包含一个非常小的功能,比如权限管理.日志收集等等.由这一组微服务组合起来,提 ...

随机推荐

  1. 【每日一题】【比较中右,内部比较中右,注意边界带>=】2021年11月2日-搜索旋转排序数组-211102/220211

    [某下标处进行了旋转]整数数组 nums 按升序排列,数组中的值 互不相同 . 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋 ...

  2. dubbo2升级到dubbo3实践

    dubbo当前版本 2.7.3 期望升级到 3.0.11. 升级过程 maven依赖变更 <dependency> <groupId>org.apache.dubbo</ ...

  3. 区块链,中心去,何曾着眼看君王?用Go语言实现区块链技术,通过Golang秒懂区块链

    区块链技术并不是什么高级概念,它并不比量子力学.泡利不相容原则.哥德巴赫猜想更难以理解,但却也不是什么类似"时间就是金钱"这种妇孺皆知的浅显道理.区块链其实是一套统筹组织记录的方法 ...

  4. JavaScript:七大基础数据类型:大整数bigint

    因为数值number有表示范围,所以当我们需要精确表示更大的数字时,我们需要用到大整数bigint: 事实上,大整数可以精确表示任意长度的整数: 我们可以通过在整数的末尾添加字母n,来声明它是一个大整 ...

  5. [编程基础] Python装饰器入门总结

    Python装饰器教程展示了如何在Python中使用装饰器基本功能. 文章目录 1 使用教程 1.1 Python装饰器简单示例 1.2 带@符号的Python装饰器 1.3 用参数修饰函数 1.4 ...

  6. Java基于内存的消息队列实现

    Java基于内存的消息队列实现 有需要源码的同学可以去Gitee上下载: https://gitee.com/hyxl-520/memory_message_queue 先看测试情况 需求背景 需求来 ...

  7. 已完成 10000 多次提交,Solon Java Framework v1.12.1 发布

    一个更现代感的 Java 应用开发框架:更快.更小.更自由.没有 Spring,没有 Servlet,没有 JavaEE:独立的轻量生态.主框架仅 0.1 MB. @Controller public ...

  8. 连号区间数【第四届蓝桥杯省赛C++B组,第四届蓝桥杯省赛JAVAB组】

    连号区间数 小明这些天一直在思考这样一个奇怪而有趣的问题: 在 \(1∼N\) 的某个排列中有多少个连号区间呢? 这里所说的连号区间的定义是: 如果区间 \([L,R]\) 里的所有元素(即此排列的第 ...

  9. 蚂蚁感冒【第五届蓝桥杯省赛C++A/B组】

    蚂蚁感冒 长 \(100\) 厘米的细长直杆子上有 \(n\) 只蚂蚁. 它们的头有的朝左,有的朝右. 每只蚂蚁都只能沿着杆子向前爬,速度是 1 厘米/秒. 当两只蚂蚁碰面时,它们会同时掉头往相反的方 ...

  10. [cocos2d-x]registerScriptHandler和registerScriptTapHandler区别

    一 .调用registerScriptHandler 的对象不同相应的响应函数和调用方式也不相同 1. 对象为layer时调用方式为: local function onNodeEvent(event ...