QEMU搭建U-Boot+Linux+NFS 环境 学习笔记

使用QEMU搭建U-Boot+Linux+NFS嵌入式开发环境

QEMU简介

  • QEMU是一个模拟器,可以模拟CPU,ARM、X86、MIPS等架构
  • 可以仿真的ARM处理器:ARM926E、ARM1136、Cortex-A8/A9
  • 模拟真实的开发板、外设:串口、LCD、网卡、USB、SD卡…

使用QEMU的事情

  • 研究内核虚拟化
  • 模拟CPU,对于芯片公司,流片之前在QEMU上做验证、仿真、软硬件协同设计,开发BSP和驱动
  • 模拟开发板,在模拟平台上进行系统软件开发、驱动开发
  • 学生、工程师可以利用qemu-system-arm学习嵌入式开发、研究Bootloader 、Linux内核、驱动开发、应用开发等。

使用QEMU学习嵌入式的好处

  • 节省学习成本
  • 跳过开发板、硬件的各种“坑”,缩短学习曲线
  • 重构嵌入式知识体系和技能,跟硬件无关的放到QEMU上学习
  • 跟开发板相关的驱动、BSP针对具体开发板深入突破
  • 适应不同CPU、开发板的技术要求

驱动开发技能

  • 基本的硬件知识
  • Linux内核、系统架构的理解
  • 芯片手册、开发板

搭建嵌入式基本开发环境

基本环境

Vmware+Ubuntu18.04

Vmwaretools

更新软件源

  • 软件源

Ubuntu采用集中式的软件仓库机制,将软件包分类存在软件仓库中,进行管理。软件仓库放置在各种镜像服务器中,对于用户来说,当使用apt-get install安装软件包时,会从这些服务器下载软件包,这些镜像服务器就是 软件源(Reposity).

  • 经常涉及的几个目录

/var/lib/dpkg/available:软件包的描述信息,软件源中所有软件包中的信息,包括安装和未安装的软件包
/var/cache/apt/archives:当apt install安装软件包的临时存放路径
/etc/apt/sources.list:文件里是软件源站点,apt install时,Ubuntu会从这些站点下载软件包到本地并进行安装
/var/lib/apt/lists:使用apt update命令会从/etc/apt/sources.list中下载软件包列表索引,并保存到该目录

  • 工作原理
  • 执行apt update,程序分析/etc/apt/source.list
  • 自动联网寻找list中对应的Packages/Sources/Release列表文件,如果有更新则重新下载之,存入存入/var/lib/apt/lists/目录
  • 然后apt install相应的包,下载到本地并进行安装

安装uboot-tools

  • 编译的内核镜像需要通过工具生成uboot可引导的格式
  • 用来生成适应U-boot引导的镜像文件格式
  • $ apt-get install u-boot-tools

在这里插入图片描述

代码编辑管理工具:git/vim

参考git.
参考git.
参考vim.

交叉编译器:gcc-arm-linux-gnueabi

交叉编译

在一种计算机环境中编译程序,在另外一种环境下运行。
或者说在一个平台上编译生成在另一个平台上运行的可执行代码。

ABI和EABI
  • ABI: 二进制应用程序接口(Application Binary Interface (ABI) for the ARMArchitecture) 在计算机中,应用二进制接口描述了应用程序(或者其他类型)和操作系统之间或其他应用程序的低级接口. 涵盖了数据类型的大小、布局和对齐,调用约定
  • EABI: 嵌入式ABI
    嵌入式应用二进制接口指定了文件格式、数据类型、寄存器使用、堆积
    组织优化和在一个嵌入式软件中的参数的标准约定。
  • Arm-none-gnueabi-linux在可移植性、兼容性上面比arm-linux-gcc要好.
  • 早期u-boot和Linux编译可能使用的都不是一个arm-linux-gcc版本
安装

apt-get install gcc-arm-linux-gnueabi //ubuntu18.04
arm-linux-gnueabi-gcc -v

在这里插入图片描述
在这里插入图片描述

QEMU安装

环境

在这里插入图片描述

自动安装

  • sudo apt-get install qemu

手动编译安装

  • 首先安装QEMU编译依赖的包
    – apt install zlib1g-dev
    – apt install libglib2.0-0 libglib2.0-dev
    – apt install libsdl1.2-dev
    – apt install libpixman-1-dev libfdt-dev
  • 下载QEMU源码:git clone git://git.qemu-project.org/qemu.git
  • 切换到一个稳定版本:git checkout v2.7.0
  • 编译配置:./configure --target-list=arm-softmmu --audio-drv-list=
  • 编译安装:make ; make install

在这里插入图片描述
在这里插入图片描述

QEMU支持的开发板

qemu-system-arm -M help //列出支持的开发板

在这里插入图片描述

  • cubieboard
  • i.mx25
  • mainstone
  • Samsung Exynos4210
  • Vexpress-a9
  • Vexpress-a15

ARM express开发板简介

Vexpress系列开发板

  • 全称versatile express family,ARM公司自己推出的开发板
  • 主要用于SOC厂商设计、验证和测试自己的SOC芯片
  • 采用主板+子板设计,主板提供各种外围接口,子板提供CPU运算

Vexpress系列支持的CPU

  • Cortex-A9:处理器子板 Express A9x4 (V2P-CA9x4)
  • Cortex-A5:处理器子板 Express A5x2 (V2P-CA5x2s)
  • Cortex-R5:
  • Cortex-A15:处理器子板 Express A15x2 (V2P-CA15x2)

在这里插入图片描述

系统框图

在这里插入图片描述

子板系统图

在这里插入图片描述

开发板基本配置

内存映射分布

在这里插入图片描述

最小系统概念

  • 嵌入式最小系统
  • CPU+DDR/SDRAM
  • Flash、SD
  • 串口+LCD

基本配置

  • 内存
  • LCD
  • 串口

编译Linux内核和dtb文件

下载Linux内核

  • www.kernel.org
  • git clone
    git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/

在这里插入图片描述

  • 下载一个长期维护的版本

修改内核根目录的Makefile,若没有则添加

  • ARCH=arm
  • CROSS_COMPILE=arm-linux-gnueabi-
    在这里插入图片描述
    在这里插入图片描述

修改单板配置文件

  • 位于内核根目录/arch/arm/configs中
  • 修改vexpress_defconfig,一般默认
  • 在内核根目录执行make vexpress_defconfig即可完成配置
    在这里插入图片描述

编译内核、模块、dtb文件

  • make vexpress_defconfig
  • make zImage //可采用-jx使用x个线程编译,加快编译速度
  • make modules
  • make dtbs

执行make vexpress_defconfig遇到的问题
LEX scripts/kconfig/lexer.lex.c
/bin/sh: 1: flex: not found
scripts/Makefile.host:9: recipe for target ‘scripts/kconfig/lexer.lex.c’ failed
make[1]: *** [scripts/kconfig/lexer.lex.c] Error 127
Makefile:591: recipe for target ‘vexpress_defconfig’ failed
make: *** [vexpress_defconfig] Error 2
解决方法
sudo apt-get install flex
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

运行内核

qemu-system-arm -M vexpress-a9 -m 512M -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -append “console=ttyAMA0”

在这里插入图片描述

-M vexpress-a9 模拟vexpress-a9单板,你能够使用-M ?參数来获取该qemu版本号支持的全部单板
-m 512M 单板执行物理内存512M
-kernel arch/arm/boot/zImage   告诉qemu单板执行内核镜像路径
-dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb  告诉qemu单板的设备树(必须加入)
-nographic 不使用图形化界面,仅仅使用串口
-append "console=ttyAMA0" 内核启动參数。这里告诉内核vexpress单板执行。串口设备是哪个tty。

关闭qemu

  • ps -a查看进程号
  • kill -9 pid 关闭进程
    在这里插入图片描述

使用busybox制作根文件系统

文件系统

  • 对存储设备上的数据进行组织的机制

为什么要使用文件系统

  • Linux的哲学:一切皆文件
  • 用户与操作系统进行交互的主要工具:文件系统调用
  • 用户和底层存储的接口

根文件系统

  • Linux内核启动后第一个挂载的文件系统
  • 主要由基本的shell命令、各种库、字符设备、配置脚本组成
  • 提供了根目录 /
  • RFS(根文件系统root file system)可以放在:nor/nand flash、SD卡、磁盘、网络空间上

busybox

  • 一个集成100多个Linux常用命令和工具的软件
  • 一个适合制作嵌入式文件系统的软件工具

编译安装

下载源码

sudo apt-get source busybox

在这里插入图片描述

修改Makefile

  • ARCH=arm
  • CROSS_COMPILE=arm-linux-gnueabi-

配置

  • make defconfig
  • make menuconfig
  • 遇到的问题
HOSTCC  scripts/kconfig/lxdialog/checklist.o
<command-line>:0:12: fatal error: curses.h: No such file or directory
compilation terminated.
scripts/Makefile.host:120: recipe for target 'scripts/kconfig/lxdialog/checklist.o' failed
make[2]: *** [scripts/kconfig/lxdialog/checklist.o] Error 1
/home/snowynight/Work/busybox_soft/busybox-1.27.2/scripts/kconfig/Makefile:14: recipe for target 'menuconfig' failed
make[1]: *** [menuconfig] Error 2
Makefile:443: recipe for target 'menuconfig' failed
make: *** [menuconfig] Error 2

在这里插入图片描述

  • 解决方法:sudo apt-get install libncurses5-dev

在这里插入图片描述

  • 配置编译成静态库

在这里插入图片描述
在这里插入图片描述

编译安装

  • make
  • make install
    在这里插入图片描述
  • zImage生成路径:
    arch/arm/boot/zImage
  • modules生成路径:
    drivers/video/backlight/*.ko
  • dtbs生成路径:
    arch/arm/boot/dts/vexpress-v2p-ca9.dtb

制作根文件系统

  • mkdir rootfs
  • mkdir rootfs/lib
  • sudo cp -r busybox_soft/busybox-1.27.2/_install/* rootfs 文件系统目录
  • sudo cp -p /usr/arm-linux-gnueabi/lib/* rootfs/lib/ 系统动态库
  • 创建字符设备,设备文件,文件节点
  • mkdir dev 用于存放文件节点
  • 在dev目录下创建节点
  • mknod –m 666 tty1 c 4 1 字符设备节点,权限666,串口
  • mknod –m 666 tty2 c 4 2 主设备号为4,次设备号为2
  • mknod –m 666 tty3 c 4 3
  • mknod –m 666 tty4 c 4 4
  • mknod –m 666 console c 5 1 工作台节点
  • mknod –m 666 null c 1 3 垃圾回收站

在这里插入图片描述

制作SD卡文件系统镜像

dd命令参考引用.

  • 生成镜像:dd if=/dev/zero of=rootfs.ext3 bs=1M count=32
  • 格式化为exts文件系统:mkfs.ext3 rootfs.ext3
  • 将各种文件拷贝到文件系统镜像中
  • mount -t ext3 rootfs.ext3 /mnt/ -o loop

-o 是mount命令的一bai个参数,Options的首字母,du后面跟着mount选项喽zhi
loop:用来把一个文dao件当成硬1653盘分区mount到目录

  • cp -r rootfs/* /mnt
  • umount /mnt

在这里插入图片描述

  • 遇到的问题
mount: /mnt: wrong fs type, bad option, bad superblock on /dev/loop21, 
missing >codepage or helper program, or other error.

解决方法
mkfs.ext3 rootfs.ext3

在这里插入图片描述

启动内核,挂载rootfs

  • qemu-system-arm -M vexpress-a9 -m 512M -dtb ./vexpress-
    v2p-ca9.dtb -kernel ./zImage -nographic -append
    "root=/dev/mmcblk0 rw console=ttyAMA0" -sd rootfs.ext3

在这里插入图片描述
在这里插入图片描述

使用LCD启动内核

  • 使用2.11版本的qemu无法启动图形化
  • 据说2.7以下版本可以使用图形化启动,2.7以上版本需要用vnc虚拟机进行连接
  • qemu-system-arm -M vexpress-a9 -m 512M -dtb ./vexpress-
    v2p-ca9.dtb -kernel ./zImage -append "root=/dev/mmcblk0 rw
    console=tty0" -sd rootfs.ext3

使用u-boot加载Linux内核

嵌入式启动概述

嵌入式bootloader

  • 功能类似于PC的BIOS、硬件检测是否正常
  • 加载操作系统镜像到RAM
  • 设置不同的启动方式

常见的启动方式

  • NOR/NAND flash启动
  • 从SD卡启动
  • Bootloader从网络加载Linux内核启动

U-boot编译

  • 功能类似于Windows的BIOS
  • 编译u-boot
    – 下载: http://ftp.denx.de/pub/u-boot/
    – 修改Makefile:ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
    – 配置:$ make vexpress_ca9x4_defconfig
    – 编译:$ make –j4
  • 运行u-boot
    – qemu-system-arm -M vexpress-a9
    –kernel u-boot
    – nographic
    – m 512M

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 修改顶层Makefile添加交叉编译工具
    在这里插入图片描述
  • 修改config.mk CPU架构ARCH为arm

在这里插入图片描述
在这里插入图片描述

  • 配置

在这里插入图片描述

  • 编译
    在这里插入图片描述
  • 测试
qemu-system-arm \
	-M vexpress-a9 \
	-m 512M \
	-kernel u-boot \
	-nographic \

在这里插入图片描述

在这里插入图片描述

QEMU网络功能设置

配置QEMU与主机的网络连接

  • 采用桥接(bridge)的网络连接与Host通信
  • 需要主机内核tun/tap模块支持

查看主机内核是否支持tun/tap模块

在这里插入图片描述

配置

  • 主机安装工具包: apt install uml-utilities bridge-utils
  • 创建tun设备文件:/dev/net/tun
  • 修改/etc/network/interfaces文件,重启生效:

# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
auto ens33
auto br0
iface br0 inet dhcp
bridge_ports ens33

  • 配置/etc/qemu-ifup、/etc/qemu-ifdown脚本

查看网口
在这里插入图片描述
修改/etc/network/interfaces文件
ens33字段是ifconfig查到的虚拟网卡的名称
auto lo
iface lo inet loopback
auto ens33
auto br0
iface br0 inet dhcp
bridge_ports ens33在这里插入图片描述
出现br0表示添加成功
在这里插入图片描述

内核配置编译

使用U-boot引导内核镜像

  • 需要将内核编译为uImage格式
  • 需要指定uImage的加载地址
  • 编译时指定:$ make LOADADDR=0x60003000 uImage -j4

若无法生成uImage则参考 ---- 生成uImage的方法
有一个很简单的办法u-boot编译结束时,会在tool文件夹下面生成一个mkimage文件,将这个文件复制到交叉编译器目录下的bin文件夹下面,以后编译时就会生成uImage文件,省的用命令行的方式转了.
在这里插入图片描述
在这里插入图片描述

主机TFTP工具安装

  • 安装主机Host的TFTP
  • 安装tftp工具:$ apt-get install tftp-hpa tftpd-hpa xinetd
  • 修改配置文件: /etc/default/tftpd-hpa
    – TFTP_USERNAME=“tftp”
    – TFTP_DIRECTORY="/home/snowynight/Work/tftpboot" //qemu会从该目录中寻找镜像文件
    – TFTP_ADDRESS=“0.0.0.0:69” //tftp地址
    – TFTP_OPTIONS="-l -c -s"
  • 创建tftp目录: mkdir /home/snowynight/Work/tftpboot
  • chmod 777 tftpboot
  • 重启tftp服务: /etc/init.d/tftpd-hpa restart
  • 拷贝内核镜像(uImage)文件
  • 拷贝dtb(cexpress-v2p-ca9.dtb)文件

在这里插入图片描述

启动测试

qemu-system-arm \
	-M vexpress-a9 \
	-m 512M \
	-kernel u-boot-source/u-boot-2019.07+dfsg/u-boot \
	-nographic \
	-device virtio-net-device,netdev=tap0 -netdev tap,id=tap0,ifname=tap0 \
#	-net nic,vlan=0 -net tap,vlan=0,ifname=tap0 \ //旧版本的qemu
	-sd rootfs.ext3

-net 选项’vlan 过时’解决方法.

  • 实测不需要修改,使用sudo权限执行也可以使用

自动化引导(添加如下定义)

修改include/configs/vexpress_common.h

  • #define CONFIG_BOOTCOMMAND \
    "tftp 0x60003000 uImage;tftp 0x60500000 vexpress-v2p-ca9.dtb; \
    setenv bootargs ‘root=/dev/mmcblk0 console=tty0’; \
    bootm 0x60003000 - 0x60500000; "
  • 配置开发板、主机IP地址
    /Netmask/
    #define CONFIG_IPADDR 192.168.244.128
    #define CONFIG_NETMASK 255.255.255.0
    #define CONFIG_SERVERIP 192.168.244.129

在这里插入图片描述

  • 修改后要重新编译u-boot
  • make -j4

注意事项

服务器地址由创建的虚拟网卡决定
在这里插入图片描述
setenv bootargs ‘root=/dev/mmcblk0 console=tty0’;参数需要和启动脚本中的一致
在这里插入图片描述
在这里插入图片描述

挂载NFS文件系统

NFS

  • 网络文件系统
  • 计算机与计算机之间可以通过网络实现文件共享

主机HOST支持NFS服务

  • 主机HOST开启NFS服务

安装

  • apt install nfs-kernel-server

配置NFS

  • 在/etc/exports文件中添加:
  • /home/snowynight/Work/rootfs *(rw,sync,no_subtree_check,no_root_squash)
    在这里插入图片描述

开启NFS服务

  • /etc/init.d/rpcbind restart
  • /etc/init.d/nfs-kernel-server restart

在这里插入图片描述

修改bootargs启动参数

  • 设置NFS为根文件系统
  • 设置主机NFS文件系统的地址
    在这里插入图片描述
    在这里插入图片描述

内核支持挂载NFS文件系统

  • make menuconfig

File systems —>
[*] Network File Systems —>
<> NFS client support
<
> NFS client support for NFS version 3
[*] NFS client support for the NFSv3 ACL protocol extension
[*] Root file system on NFS

在这里插入图片描述

  • 重新编译make LOADADDR=0X60003000 uImage -j4
  • 拷贝到tftproot目录下

遇到的问题
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(2,0)

ubuntu18挂载NFS4文件系统

#define CONFIG_BOOTCOMMAND \
"tftp 0x60003000 uImage;tftp 0x60500000 vexpress-v2p-ca9.dtb; \
setenv bootargs 'root=/dev/nfs rw \
nfsroot=192.168.3.48:/home/snowynight/Work/rootfs,proto=tcp,nfsvers=4,nolock \
ip=192.168.3.88 console=ttyAMA0'; \
bootm 0x60003000 - 0x60500000; 

注意事项

  • setenv bootargs 参数
    – ,proto=tcp
    – ,nfsvers=4
    – ,nolock
  • 开发板ip地址

make menuconfig配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完善根文件系统

  • mkdir etc
  • cd etc/
  • mkdir init.d
  • cd init.d
  • sudo vim rcS
echo "....................."
echo "inittab init"


echo "....................."
  • sudo chmod 777 rcS

在这里插入图片描述
在这里插入图片描述

步骤

  • 新建etct、mp、sys、var、proc目录
    在这里插入图片描述
  • 在etc目录中添加inittab、init.d/rcS、fstab、profile
  • 修改控制台显示格式
    参考该笔记.

在etc/profile文件添加

PS1='user@snowynight:\w # '
export PS1

在这里插入图片描述

  • 启动Linux,挂载NFS文件系统

文件的启动流程

  • Linux内核启动之后,挂载NFS根文件系统

  • 开启Linux的第一个用户进程:init

  • init进程bootargs->init=…->执行inittab脚本

  • inittab脚本首先会执行init.d/rcS脚本

  • rcS脚本:执行mount –a 读取fstab挂载各种文件系统

  • inittab:接着会启动console
    – 启动shell:/bin/sh
    – 在启动/bin/sh之前先执行profile文件

  • /etc/init.d/rcS

#! /bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=/lib
export PATH LD_LIBRARY_PATH

mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
mdev -s
mkdir -p /var/lock
  • etc/fstab
proc		    /proc		    proc	defaults    		    0	    0
tmpfs           /tmp            tmpfs   defaults                0       0
sysfs           /sys            sysfs   defaults                0       0
tmpfs           /dev            tmpfs   defaults                0       0
var             /dev            tmpfs   defaults                0       0
ramfs           /dev            ramfs   defaults                0       0
  • etc/inittab
::sysinit:/etc/init.d/rcS
#::respawn:-/bin/sh
#tty2::askfirst:-/bin/sh
#::ctrlaltdel:/bin/umount -a -r

console::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
  • etc/profile

PS1='user@snowynight:\w # ' 
export PS1 

运行应用和内核驱动程序

运行应用程序

  • 注意采用交叉编译器进行编译

在这里插入图片描述
在这里插入图片描述

运行内核驱动程序

#include<linux/init.h>
#include<linux/module.h>

MODULE_LICENSE("GPL");

static int hello_init(void)
{
	printk(KERN_ALERT"-----------------!\n");
	printk(KERN_ALERT"hello world!\n");
	printk(KERN_ALERT"hello vexpress!\n");
	printk(KERN_ALERT"-----------------!\n");

	return 0;
}

static void  __exit hello_exit(void)
{
	printk(KERN_ALERT"goodbye, crazy world!\n");
}

module_init(hello_init);
module_exit(hello_exit);
  • Makefile
.PHONY:all clean

obj-m := arm_driver.o

KDIR := /home/snowynight/Work/linux-5.4.72
PWD := $(shell pwd)
all:
		make  CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm -C $(KDIR) M=$(PWD) modules
clean:
		rm -fr *.ko *.o *.mod.o *.mod.c *.symvers *.order .*.ko .tmp_versions


在这里插入图片描述

  • 挂载和卸载

在这里插入图片描述

以下是在 Windows 系统下构建 QEMU Cortex-A7 和 Visual Studio 虚拟开发和调试环境的步骤: 1. 下载 QEMU QEMU 是一个开源的虚拟机软件,可以模拟多种硬件平台,包括 ARM。在 QEMU 官网上下载最新版本的 QEMU:https://www.qemu.org/download/ 2. 下载 Cortex-A7 虚拟机镜像 可以在 Linaro 官网上下载预编译的 Cortex-A7 虚拟机镜像:https://www.linaro.org/downloads/ 3. 安装 Visual Studio 在官网上下载并安装 Visual Studio:https://visualstudio.microsoft.com/downloads/ 4. 安装 VisualGDB 插件 VisualGDB 是一个 Visual Studio 的插件,可以支持 ARM 开发和调试。在官网上下载并安装 VisualGDB:https://visualgdb.com/download/ 5. 配置 VisualGDB 打开 Visual Studio,点击“VisualGDB”菜单,选择“Project Wizard”,选择“Embedded Project”,选择“QEMU ARM Cortex-A7”,然后按照向导完成配置。 6. 启动 QEMU 使用以下命令启动 QEMU: ``` qemu-system-arm -machine virt -cpu cortex-a7 -smp 4 -m 2048 -kernel path/to/kernel -append "root=/dev/vda2 rootfstype=ext4 rw console=ttyAMA0,115200" -drive file=path/to/rootfs,if=none,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -netdev user,id=net0 -device virtio-net-device,netdev=net0 ``` 其中,`path/to/kernel` 是内核镜像的路径,`path/to/rootfs` 是根文件系统的路径。 7. 调试应用程序 在 Visual Studio 中创建一个新的 ARM 项目,然后编写和调试应用程序。在调试配置中选择“VisualGDB QEMU Cortex-A7”,然后启动调试会话即可。 希望这些步骤能够帮助你构建 QEMU Cortex-A7 和 Visual Studio 虚拟开发和调试环境
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值