u-boot 和 rootfs/initramfs 启动 Raspberry Pi 4B-程序员宅基地

技术标签: Linux  u-boot  智能驾驶-BSP  

0. 概述

这篇文章的目的是了解嵌入式Linux的四个组成部分 —— 工具链、引导加载程序、内核、根文件系统 —— 通过使用最少的代码从头开始启动 Raspberry Pi 4 的命令。

1. 硬件要求

  • 用于编译源代码的 Linux 桌面计算机。我正在使用 Ubuntu 20.04。
  • 带有电源适配器的 Raspberry 4 型号 b。
  • SD 卡和读卡器。我使用的是 2GB SD 卡。

2. 准备工作

SD 卡将用于存储引导加载程序和根文件。所以,我们首先在其上创建两个分区 - boot(FAT32格式)和 root(ext4格式)。

2.1 查找TF卡设备名

将 SD 读卡器插入 Linux PC 后,从dmesg或mount找到其设备名称。

$ dmesg | tail
[19304.704047] usbcore: registered new interface driver uas
[19305.719653] scsi 33:0:0:0: Direct-Access     Mass     Storage Device   1.00 PQ: 0 ANSI: 0 CCS
[19305.720283] sd 33:0:0:0: Attached scsi generic sg2 type 0
[19305.725987] sd 33:0:0:0: [sdb] 3842048 512-byte logical blocks: (1.97 GB/1.83 GiB)
[19305.728140] sd 33:0:0:0: [sdb] Write Protect is off
[19305.728142] sd 33:0:0:0: [sdb] Mode Sense: 03 00 00 00
[19305.730188] sd 33:0:0:0: [sdb] No Caching mode page found
[19305.730750] sd 33:0:0:0: [sdb] Assuming drive cache: write through
[19305.757769]  sdb: sdb1 sdb2
[19305.788187] sd 33:0:0:0: [sdb] Attached SCSI removable disk

在本例中,设备名称为 sdb,并且它已经有两个分区sdb1和 sdb2。我将删除它们并重新分区 SD 卡。

2.2 删除现有分区

$ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): d
Partition number (1,2, default 2):

Partition 2 has been deleted.

Command (m for help): d
Selected partition 1
Partition 1 has been deleted.

Command (m for help): p
Disk /dev/sdb: 1.85 GiB, 1967128576 bytes, 3842048 sectors
Disk model: Storage Device
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

2.3 添加两个分区

添加一个 100MB 的boot分区和一个剩余大小的root分区。

$ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p):

Using default response p.
Partition number (1-4, default 1):
First sector (2048-3842047, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-3842047, default 3842047): +100M

Created a new partition 1 of type 'Linux' and of size 100 MiB.
Partition #1 contains a vfat signature.

Do you want to remove the signature? [Y]es/[N]o: Y

The signature will be removed by a write command.

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p):

Using default response p.
Partition number (2-4, default 2):
First sector (206848-3842047, default 206848):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (206848-3842047, default 3842047):

Created a new partition 2 of type 'Linux' and of size 1.8 GiB.
Partition #2 contains a ext4 signature.

Do you want to remove the signature? [Y]es/[N]o: Y

The signature will be removed by a write command.

Command (m for help): t
Partition number (1,2, default 2): 1
Hex code (type L to list all codes): b

Changed type of partition 'Linux' to 'W95 FAT32'.

Command (m for help): p
Disk /dev/sdb: 1.85 GiB, 1967128576 bytes, 3842048 sectors
Disk model: Storage Device
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Device     Boot  Start     End Sectors  Size Id Type
/dev/sdb1         2048  206847  204800  100M  b W95 FAT32
/dev/sdb2       206848 3842047 3635200  1.8G 83 Linux

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

2.4 格式化分区

# FAT32 for boot partition
$ sudo mkfs.vfat -F 32 -n boot /dev/sdb1

# ext4 for root partition
$ sudo mkfs.ext4 -L root /dev/sdb2

2.5 挂载分区

挂载两个分区,以便我们可以写入它们。

$ sudo mount /dev/sdb1 /mnt/boot
$ sudo mount /dev/sdb2 /mnt/root

3. 工具链

首先,我们需要一个工具链来将源代码编译为运行在 树莓派 4。我们构建的工具链包括:

  • 交叉编译器
  • 二进制实用程序,如汇编程序和链接器,以及
  • 一些运行时库

需要交叉编译器,因为我们将编译在 Linux 台式计算机 (X86) 上的 Raspberry Pi 4 (ARM)。

我们可以按照 Linux 中的步骤从头开始构建一个完整的工具链从零开始[2]。但我会走捷径,使用 crosstool-NG。有关 构建工具链的过程,请参阅此处的精彩解释一文

3.1 下载crosstool-NG源码

$ git clone https://github.com/crosstool-ng/crosstool-ng
$ cd crosstool-ng/
# Switch to the latest release
$ git checkout crosstool-ng-1.24.0 -b 1.24.0

3.2 构建和安装crosstool-NG

安装 crosstool-NG 的完整文档可以在这里找到

$ ./bootstrap
$ ./configure --prefix=${PWD}
$ make
$ make install
$ export PATH="${PWD}/bin:${PATH}"

configures是可能会报 not found: libtool,需要额外安装 libtool-bin 解决。

3.3 配置crosstool-NG

在用crosstool-NG于构建工具链之前,我们需要首先对其进行配置。配置器的工作方式与配置 Linux 内核相同。

$ ct-ng menuconfig

还有一些示例配置,我们可以通过ct-ng list-samples命令获取。我们可以使用其中之一,然后使用ct-ng menuconfig。这里 我将不加修改地使用 aarch64-rpi4-linux-gnu。

# Basic information about this config
$ ct-ng show-aarch64-rpi4-linux-gnu
[G...]   aarch64-rpi4-linux-gnu
    Languages       : C,C++
    OS              : linux-4.20.8
    Binutils        : binutils-2.32
    Compiler        : gcc-8.3.0
    C library       : glibc-2.29
    Debug tools     : gdb-8.2.1
    Companion libs  : expat-2.2.6 gettext-0.19.8.1 gmp-6.1.2 isl-0.20 libiconv-1.15 mpc-1.1.0 mpfr-4.0.2 ncurses-6.1 zlib-1.2.11
    Companion tools :

# Use this config
$ ct-ng aarch64-rpi4-linux-gnu

注意:操作系统是 linux-4.20.8,意思是由工具链编译的二进制文件 应该能够在任何内核版本 >= 4.20.8 上运行。

3.4 构建工具链

要构建工具链,只需运行:

$ ct-ng build

注意:在撰写本文时,上述命令在尝试时失败 下载 ISL LIB,因为位置似乎已关闭。一个 可以在此处找到解决方法。isl.gforge.inria.fr

默认情况下,构建的工具链安装在 ~/x-tools/aarch64-rpi4-linux-gnu。

4. 引导加载程序

引导加载程序的工作是将系统设置到基本级别(例如,配置内存控制器以访问DRAM)并加载内核。 通常,启动顺序为:

  1. 存储在芯片上的 ROM 代码运行。它加载辅助程序加载程序 (SPL) 到静态随机存取存储器 (SRAM) 中,不需要存储器 控制器。SPL 可以是完整引导加载程序的精简版本,例如 U-启动。由于SRAM尺寸有限,因此需要它。
  2. SPL 设置内存控制器,以便可以访问 DRAM 并执行 其他一些硬件配置。然后,它将完整的引导加载程序加载到 DRAM。
  3. 然后,完整的引导加载程序加载内核,即扁平化设备树 (FDT) 以及可选的初始 RAM 磁盘 (initramfs) 到 DRAM 中。一旦内核是 加载后,引导加载程序会将控制权移交给它。

4.1 下载u-boot源码

$ git clone git://git.denx.de/u-boot.git
$ cd u-boot
$ git checkout v2021.10 -b v2021.10

4.2 配置u-boot

因为引导加载程序是特定于设备的,所以我们需要在构建它之前对其进行配置。与 crosstool-NG类似,有几个位于configs/下的 sample/default 配置。我们可以在configs/rpi_4_defconfig下 找到一个用于Raspberry Pi 4的。那么我们只需要运行 make rpi_4_defconfig。在此之前,我们还需要设置 CROSS_COMPILE 环境变量。

$ export PATH=${HOME}/x-tools/aarch64-rpi4-linux-gnu/bin/:$PATH
$ export CROSS_COMPILE=aarch64-rpi4-linux-gnu-
$ make rpi_4_defconfig

4.3 构建u-boot

$ make

4.4 安装u-boot

我们只需要将最后一步编译的二进制文件 u-boot.bin 复制到 SD 卡上的 boot 分区中即可。

$ sudo cp u-boot.bin /mnt/boot

注意:Raspberry Pi 有自己专有的引导加载程序,由 ROM代码,并且能够加载内核。但是,既然我想 使用开源,我需要配置树莓派启动 loader 加载,然后让内核加载。u-bootu-bootu-boot

从4B的官方镜像中拷贝出 bootcode.bin, start4.elf 和 fixup4.dat 到 /mnt/boot 中。

再手动写一份 config.txt:

# Let Raspberry Pi 4 bootloader load u-boot
$ cat << EOF > config.txt
enable_uart=1
arm_64bit=1
kernel=u-boot.bin
EOF
$ sudo mv config.txt /mnt/boot/

介绍下rpi4的启动过程,其启动分区采用的 fat32 fs,并采用三级启动方式:

  1. 板载VideoCore GPU启动固化在rpi4中的ROM,该阶段非常简单,主要支持读取TF卡中的fat32 fs的第2级启动程序;
  2. 板载VideoCore GPU加载并执行启动分区(/boot)中的bootcode.bin,该文件的主要功能是解析elf格式文件,再加载并解析同目录下的start4.elf;
  3. 运行start4.elf,读取并解析config.txt的配置文件,再加载并执行真正的u-boot程序。

5. 内核

接下来,我们编译 Linux 内核。

5.1 下载内核源码

虽然原来的 Linux 内核应该可以工作,但使用 Raspberry Pi 的分支 更稳定。另请注意,内核版本必须高于 为工具链配置的内核版本。

$ git clone --depth=1 -b rpi-5.10.y https://github.com/raspberrypi/linux.git
$ cd linux

5.2 配置和构建内核

我们只使用 Raspberry Pi 4 的默认配置。有关 Raspberry Pi 4 型号 b 规格,请参阅此处

$ make ARCH=arm64 CROSS_COMPILE=aarch64-rpi4-linux-gnu- bcm2711_defconfig
$ make -j$(nproc) ARCH=arm64 CROSS_COMPILE=aarch64-rpi4-linux-gnu-

5.3 安装内核和设备树

现在我们将内核映像和设备树二进制文件(*.dtb)复制到SD卡上的boot分区中。

$ sudo cp arch/arm64/boot/Image /mnt/boot
$ sudo cp arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb /mnt/boot/

6. 根文件系统

请参阅文件系统层次结构标准配置,更多 有关 Linux 系统基本目录布局的详细信息。

6.1 创建目录

$ mkdir rootfs
$ cd rootfs
$ mkdir {bin,dev,etc,home,lib64,proc,sbin,sys,tmp,usr,var}
$ mkdir usr/{bin,lib,sbin}
$ mkdir var/log

# Create a symbolink lib pointing to lib64
$ ln -s lib64 lib

$ tree -d
.
├── bin
├── dev
├── etc
├── home
├── lib -> lib64
├── lib64
├── proc
├── sbin
├── sys
├── tmp
├── usr
│   ├── bin
│   ├── lib
│   └── sbin
└── var
    └── log

16 directories

# Change the owner of the directories to be root
# Because current user doesn't exist on target device
$ sudo chown -R root:root *

6.2 构建和安装Busybox

我们将 Busybox 用于基本的 Linux 实用程序,例如 shell。所以,我们需要 将其安装到刚刚创建的rootfs目录中。

# Download the source code
$ wget https://busybox.net/downloads/busybox-1.33.2.tar.bz2
$ tar xf busybox-1.33.2.tar.bz2
$ cd busybox-1.33.2/

# Config
$ CROSS_COMPILE=${HOME}/x-tools/aarch64-rpi4-linux-gnu/bin/aarch64-rpi4-linux-gnu-
$ make CROSS_COMPILE="$CROSS_COMPILE" defconfig
# Change the install directory to be the one just created
$ sed -i 's%^CONFIG_PREFIX=.*$%CONFIG_PREFIX="/home/hechaol/rootfs"%' .config

# Build
$ make CROSS_COMPILE="$CROSS_COMPILE"

# Install
# Use sudo because the directory is now owned by root
$ sudo make CROSS_COMPILE="$CROSS_COMPILE" install

6.3 安装所需的库

接下来,我们安装一些 Busybox 需要的共享库。我们可以找到那些库:

$ readelf -a ~/rootfs/bin/busybox | grep -E "(program interpreter)|(Shared library)"
      [Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libresolv.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

我们需要将这些文件从工具链的sysroot目录复制到rootfs/lib目录。

$ export SYSROOT=$(aarch64-rpi4-linux-gnu-gcc -print-sysroot)
$ sudo cp -L ${SYSROOT}/lib64/{ld-linux-aarch64.so.1,libm.so.6,libresolv.so.2,libc.so.6} ~/rootfs/lib64/

6.4 创建设备节点

Busybox 需要两个设备节点。

$ cd ~/rootfs
$ sudo mknod -m 666 dev/null c 1 3
$ sudo mknod -m 600 dev/console c 5 1

7. 启动开发板

最后,准备好所有组件后,我们就可以启动电路板了。有两个根文件系统的选项。我们可以将其用作 initramfs 它可以在以后挂载一个真正的根文件系统或将其用作永久根文件系统。

7.1 选项 1:使用 initramfs 引导

什么时候是 initramfs 需要?根据 Linux From Scratch [2],只有四个主要 在 LFS 环境中使用 initramfs 的原因:

  • 从网络加载 rootfs。
  • 从 LVM 逻辑卷加载它。
  • 有一个加密的rootfs,其中需要密码。
  • 为了方便将 rootfs 指定为 LABEL 或 UUID。

除了使用 initramfs,我们还可以将根文件系统直接放入 SD 卡上的分区中。在这种情况下,我们需要配置 内核命令行从引导加载程序传递到内核。root

7.1.1 构建 initramfs

initramfs 是一个压缩的存档,它是一个旧的 Unix 存档 格式类似于 和 。cpiotarzip

$ cd ~/rootfs
$ find . | cpio -H newc -ov --owner root:root -F ../initramfs.cpio
$ cd ..
$ gzip initramfs.cpio
$ ~/u-boot/tools/mkimage -A arm64 -O linux -T ramdisk -d initramfs.cpio.gz uRamdisk

# Copy the initramffs to boot partition
$ sudo cp uRamdisk /mnt/boot/
7.1.2 配置u-boot

我们需要配置 u-boot,以便它可以通过正确的内核 命令行和设备树二进制到内核。为简单起见,我将使用 Busybox shell 作为init程序。在现实生活中,如果使用 initramfs,那么 init 程序应负责挂载永久根目录文件系统。

$ cat << EOF > boot_cmd.txt
fatload mmc 0:1 \${kernel_addr_r} Image
fatload mmc 0:1 \${ramdisk_addr_r} uRamdisk
setenv bootargs "console=serial0,115200 console=tty1 rdinit=/bin/sh"
booti \${kernel_addr_r} \${ramdisk_addr_r} \${fdt_addr}
EOF
$ ~/u-boot/tools/mkimage -A arm64 -O linux -T script -C none -d boot_cmd.txt boot.scr

# Copy the compiled boot script to boot partition
$ sudo cp boot.scr /mnt/boot/

引导命令的含义:

  • 将内核映像从分区1( boot partition) 加载到内存中。
  • 将 initramfs 从分区1( boot partition) 加载到内存中。
  • 设置内核命令行。
  • 使用给定的内核、设备树二进制文件和 initramfs 启动。

注意:在最后一行中,最后一个参数 fdt_addr 与其他两个参数不同。起初,我使用 fdt_addr 无法启动开发板。发现这个后我意识到了错误 在树莓上发帖 Raspberry Pi论坛。此外,根据其中一个回复,当前的 U-boot 已经从固件继承 DTB,将其地址放入 {fdt_addr}。所以我们不需要在 U-Boot 中加载 dtb 文件。

7.1.3 启动它!

最后,所有四个组件都准备就绪。我们现在可以尝试启动它。靴子 分区现在包含以下文件:

$ tree /mnt/boot/
/mnt/boot/
├── bcm2711-rpi-4-b.dtb
├── bootcode.bin
├── boot.scr
├── config.txt
├── Image
├── start4.elf
├── uRamdisk
└── u-boot.bin

0 directories, 7 files

现在我们卸载分区并将 SD 卡插入 Raspberry Pi 4。

$ sudo umount /dev/sdb1
$ sudo umount /dev/sdb2

启动 Raspberry Pi 4 后,如果成功,我们应该会得到一个Busybox shell。

7.2 选项 2:直接使用永久 rootfs 引导

或者,我们可以在 root 分区作为根文件系统的情况下直接启动。为此,请按照以下步骤操作。

7.2.1 将rootfs复制到SD卡上的root分区

将 SD 卡插入读卡器,然后将读卡器插入 Linux 桌面。

$ sudo mount /dev/sdb1 /mnt/boot
$ sudo mount /dev/sdb2 /mnt/root
$ cp -r ~/rootfs/* /mnt/root/

7.2.2 更改引导命令

我们不再需要 initramfs。

$ cat << EOF > boot_cmd.txt
fatload mmc 0:1 \${kernel_addr_r} Image
setenv bootargs "console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rw rootwait init=/bin/sh"
booti \${kernel_addr_r} - \${fdt_addr}
EOF
$ ~/u-boot/tools/mkimage -A arm64 -O linux -T script -C none -d boot_cmd.txt boot.scr
$ sudo cp boot.scr /mnt/boot/

# Remove the initramfs as it's not needed
$ sudo rm -f /mnt/boot/uRamdisk

7.2.3 启动它!

现在我们卸载分区并将 SD 卡插入 Raspberry Pi 4。

$ sudo umount /dev/sdb1
$ sudo umount /dev/sdb2

启动 Raspberry Pi 4 后,如果出现以下情况,我们应该会得到一个 shell 成功的。与上面的 -only 情况不同,在这种情况下,无论什么 我们对根文件系统所做的更改将被保留。Busyboxinitramfs。

如下是我的 rootfs 方式下的 U-Boot 串口启动日志:

欢迎使用 minicom 2.8

选项: I18n 
通信端口 /dev/ttyUSB0, 14:09:08

按 CTRL-A Z 说明特殊键 



U-Boot 2024.04-rc4 (Mar 14 2024 - 11:38:29 +0800)

DRAM:  948 MiB (effective 7.9 GiB)
RPI 4 Model B (0xd03115)
Core:  211 devices, 16 uclasses, devicetree: board
MMC:   mmcnr@7e300000: 1, mmc@7e340000: 0
Loading Environment from FAT... Unable to read "uboot.env" from mmc0:1... 
In:    serial,usbkbd
Out:   serial,vidconsole
Err:   serial,vidconsole
Net:   eth0: ethernet@7d580000
PCIe BRCM: link up, 5.0 Gbps x1 (SSC)
starting USB...
Bus xhci_pci: Register 5000420 NbrPorts 5                                       
Starting the controller                                                         
USB XHCI 1.00                                                                   
scanning bus xhci_pci for devices... 3 USB Device(s) found                      
       scanning usb for storage devices... 0 Storage Device(s) found            
Hit any key to stop autoboot:  0                                                
Card did not respond to voltage select! : -110                                  
** Booting bootflow '[email protected]_1' with script                   
25463296 bytes read in 1227 ms (19.8 MiB/s)                                     
Moving Image from 0x80000 to 0x200000, end=1b60000                              
## Flattened Device Tree blob at 2eff2500                                       
   Booting using the fdt blob at 0x2eff2500                                     
Working FDT set to 2eff2500                                                     
   Using Device Tree in place at 000000002eff2500, end 000000002f002fa2         
Working FDT set to 2eff2500                                                     
                                                                                
Starting kernel ... 

资源

[1] 掌握嵌入式 Linux 编程 - 第三版
[2] Linux 从零开始
[3] 如何构建工具链

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yiyu20180729/article/details/137565754

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签