前言在B站看到一个项目仅用几十KB就构建了一个Minecraft服务端，运行更是只需要少得可怜的资源（esp32-c3都能运行），于是记录一下在Alpine Linux上的移植过程。开始因为使用的是 Alpine Linux，它底层使用 musl 而不是通常的 glibc (Ubuntu) 或 uClibc (Buildroot)。这需要一个专门针对 arm-linux-musleabihf 的交叉编译器，否则编译出的程序在板子上运行时会报错（例如 File not found 或段错误）。因此我们需要获取 musl 交叉编译工具链，不过我建议在宿主机手动下载然后传到wsl，不然这速度太慢了。 # 下载工具链 wget https://musl.cc/arm-linux-musleabihf-cross.tgz # 解压 tar -xf arm-linux-musleabihf-cross.tgz echo 'export PATH=$PATH:/home/luckfox/Desktop/arm-linux-musleabihf-cross/bin' >> ~/.bashrc source ~/.bashrc arm-linux-musleabihf-gcc --version搭建编译环境，运行setup_env.sh#!/bin/bash # Setup script for BareIron development environment on WSL/Ubuntu # Installs Java 21 and Node.js set -e echo "[1/3] Updating package lists..." sudo apt update echo "[2/3] Installing dependencies (curl, wget, tar, build-essential)..." sudo apt install -y curl wget tar sudo build-essential echo "[3/3] Installing Java 21..." # Check if OpenJDK 21 is available in default repos if apt-cache search openjdk-21-jdk | grep -q openjdk-21-jdk; then sudo apt install -y openjdk-21-jdk else echo "OpenJDK 21 not found in default repositories. Adding PPA.." sudo add-apt-repository -y ppa:openjdk-r/ppa sudo apt update sudo apt install -y openjdk-21-jdk fi # Verify Java installation java -version echo "Installing Node.js (Latest LTS)..." # Using NodeSource for up-to-date Node.js if ! command -v node &> /dev/null; then curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs else echo "Node.js is already installed." fi # Verify Node installation node -v echo "--------------------------------------------------------" echo "Setup Complete!" echo "--------------------------------------------------------" echo "1. Java 21 and Node.js have been installed." echo "" echo "Note: You need to manually install or configure your cross-compilation toolchain." echo "For Luckfox Pico (Alpine), ensure 'arm-linux-musleabihf-gcc' is in your PATH." echo "" echo "Then you can proceed with:" echo " ./extract_registries.sh" echo " ./build_luckfox.sh --type alpine" 准备注册表数据：运行 ./extract_registries.sh 。（没权限自己添加），手动下载mc 1.21.8 server.jar，放到notchian文件夹，然后继续执行./extract_registries.sh添加交叉编译编译脚本build_luckfox.sh#!/bin/bash # Build script for Luckfox Pico (RV1103) # Target Architecture: ARMv7l (Cortex-A7) set -e # Default settings BUILD_TYPE="buildroot" # Options: buildroot, ubuntu, alpine OUTPUT_NAME="bareiron_luckfox" # Check for registries if [ ! -f "include/registries.h" ]; then echo "Error: 'include/registries.h' is missing." echo "Please run './extract_registries.sh' first (requires Minecraft server jar and Java 21+)." exit 1 fi # Help function print_usage() { echo "Usage: $0 [options]" echo "Options:" echo " --type <type> Target system type: 'buildroot' (default), 'ubuntu', or 'alpine'" echo " --help Show this help message" } # Parse arguments while [[ "$#" -gt 0 ]]; do case $1 in --type) BUILD_TYPE="$2"; shift ;; --help) print_usage; exit 0 ;; *) echo "Unknown parameter: $1"; print_usage; exit 1 ;; esac shift done echo "Building for Luckfox Pico ($BUILD_TYPE)..." # Set compiler based on type if [ "$BUILD_TYPE" == "buildroot" ]; then # Toolchain for Buildroot (uClibc) DEFAULT_CC_PATH="/home/buildroot/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc" CC="${CROSS_COMPILE:-$DEFAULT_CC_PATH}" if ! command -v "$CC" &> /dev/null && [ ! -x "$CC" ]; then echo "Error: Compiler not found at '$CC'" echo "Please set CROSS_COMPILE environment variable to your toolchain path." exit 1 fi elif [ "$BUILD_TYPE" == "ubuntu" ]; then # Toolchain for Ubuntu (glibc) DEFAULT_CC_PATH="arm-none-linux-gnueabihf-gcc" CC="${CROSS_COMPILE:-$DEFAULT_CC_PATH}" if ! command -v "$CC" &> /dev/null; then echo "Error: Compiler '$CC' not found in PATH." echo "Please install 'gcc-arm-linux-gnueabihf' or set CROSS_COMPILE." exit 1 fi elif [ "$BUILD_TYPE" == "alpine" ]; then # Toolchain for Alpine (musl) # User needs to download a musl toolchain, e.g., from musl.cc or bootlin DEFAULT_CC_PATH="arm-linux-musleabihf-gcc" CC="${CROSS_COMPILE:-$DEFAULT_CC_PATH}" # Try to find compiler in common locations if not in PATH if ! command -v "$CC" &> /dev/null; then # Check user-provided path from common locations POTENTIAL_PATH="/home/luckfox/Desktop/arm-linux-musleabihf-cross/bin/$DEFAULT_CC_PATH" if [ -x "$POTENTIAL_PATH" ]; then CC="$POTENTIAL_PATH" fi fi if ! command -v "$CC" &> /dev/null && [ ! -x "$CC" ]; then echo "Error: Compiler '$CC' not found in PATH." echo "For Alpine, you need a musl-based toolchain (e.g., arm-linux-musleabihf-gcc)." echo "Please download one (e.g. from musl.cc) and add it to PATH or set CROSS_COMPILE." exit 1 fi else echo "Error: Invalid build type '$BUILD_TYPE'. Use 'buildroot', 'ubuntu', or 'alpine'." exit 1 fi echo "Using compiler: $CC" # Clean previous build rm -f "$OUTPUT_NAME" # Compile flags # -O2: Optimization # -Iinclude: Include directory # -lpthread: Threading support (needed for glibc/uclibc, usually implicit in musl but harmless) # -lm: Math library # -static: For Alpine/Musl, static linking is often preferred for portability, # but if you have the libraries on the board, dynamic is fine. # We will default to dynamic to match other builds, but you can uncomment -static below if needed. # LDFLAGS="-static" $CC src/*.c -O2 -Iinclude -o "$OUTPUT_NAME" -lpthread -lm if [ -f "$OUTPUT_NAME" ]; then echo "Build successful! Binary: $OUTPUT_NAME" file "$OUTPUT_NAME" else echo "Build failed." exit 1 fi运行前修改项目中include/globals.h，在嵌入式系统（如 ESP32）上， task_yield() 通常用于让出 CPU 时间片给其他任务，但在 Linux/PC 上，如果这个宏定义为空（或者只是一个空操作），程序就会在一个死循环里全速空转，导致单核 CPU 占用率飙升到 100%。Linux/Alpine : 使用 usleep(1000) (休眠 1 毫秒)#ifndef H_GLOBALS #define H_GLOBALS #include <stdint.h> #include <unistd.h> #ifdef ESP_PLATFORM #define WIFI_SSID "your-ssid" #define WIFI_PASS "your-password" void task_yield (); #else #ifdef _WIN32 #define task_yield() Sleep(1) #else #define task_yield() usleep(1000) #endif #endif #define true 1 #define false 0 // TCP port, Minecraft's default is 25565 #define PORT 25565 // How many players to keep in memory, NOT the amount of concurrent players // Even when offline, players who have logged on before take up a slot #define MAX_PLAYERS 16 // How many mobs to allocate memory for #define MAX_MOBS (MAX_PLAYERS) // Manhattan distance at which mobs despawn #define MOB_DESPAWN_DISTANCE 256 // Server game mode: 0 - survival; 1 - creative; 2 - adventure; 3 - spectator #define GAMEMODE 0 // Max render distance, determines how many chunks to send #define VIEW_DISTANCE 2 // Time between server ticks in microseconds (default = 1s) #define TIME_BETWEEN_TICKS 1000000 // Calculated from TIME_BETWEEN_TICKS #define TICKS_PER_SECOND ((float)1000000 / TIME_BETWEEN_TICKS) // Initial world generation seed, will be hashed on startup // Used in generating terrain and biomes #define INITIAL_WORLD_SEED 0xA103DE6C // Initial general RNG seed, will be hashed on startup // Used in random game events like item drops and mob behavior #define INITIAL_RNG_SEED 0xE2B9419 // Size of each bilinearly interpolated area ("minichunk") // For best performance, CHUNK_SIZE should be a power of 2 #define CHUNK_SIZE 8 // Terrain low point - should start a bit below sea level for rivers/lakes #define TERRAIN_BASE_HEIGHT 60 // Cave generation Y level #define CAVE_BASE_DEPTH 24 // Size of every major biome in multiples of CHUNK_SIZE // For best performance, should also be a power of 2 #define BIOME_SIZE (CHUNK_SIZE * 8) // Calculated from BIOME_SIZE #define BIOME_RADIUS (BIOME_SIZE / 2) // How many visited chunk coordinates to "remember" // The server will not re-send chunks that the player has recently been in // Must be at least 1, otherwise chunks will be sent on each position update #define VISITED_HISTORY 4 // How many player-made block changes to allow // Determines the fixed amount of memory allocated to blocks #define MAX_BLOCK_CHANGES 20000 // If defined, writes and reads world data to/from disk (or flash). // This is a synchronous operation, and can cause performance issues if // frequent random disk access is slow. Data is still stored in and // accessed from memory - reading from disk is only done on startup. // When targeting ESP-IDF, LittleFS is used to manage flash reads and // writes. Flash is typically *very* slow and unreliable, which is why // this option is disabled by default when targeting ESP-IDF. #ifndef ESP_PLATFORM #define SYNC_WORLD_TO_DISK #endif // The minimum interval (in microseconds) at which certain data is written // to disk/flash. Bounded on the low end by TIME_BETWEEN_TICKS. By default, // applies only to player data. Block changes are written as soon as they // are made, but in much smaller portions. Set DISK_SYNC_BLOCKS_ON_INTERVAL // to make this apply to block changes as well. #define DISK_SYNC_INTERVAL 15000000 // Whether to sync block changes to disk on an interval, instead of syncing // on each change. On systems with fast random disk access, this shouldn't // be necessary. // #define DISK_SYNC_BLOCKS_ON_INTERVAL // Time in microseconds to spend waiting for data transmission before // timing out. Default is 15s, which leaves 5s to prevent starving other // clients from Keep Alive packets. #define NETWORK_TIMEOUT_TIME 15000000 // Size of the receive buffer for incoming string data #define MAX_RECV_BUF_LEN 256 // If defined, sends the server brand to clients. Doesn't do much, but will // show up in the top-left of the F3/debug menu, in the Minecraft client. // You can change the brand string in the "brand" variable in src/globals.c #define SEND_BRAND // If defined, rebroadcasts ALL incoming movement updates, disconnecting // movement from the server's tickrate. This makes movement much smoother // on very low tickrates, at the cost of potential network instability when // hosting more than just a couple of players. When disabling this on low // tickrates, consider disabling SCALE_MOVEMENT_UPDATES_TO_PLAYER_COUNT too. #define BROADCAST_ALL_MOVEMENT // If defined, scales the frequency at which player movement updates are // broadcast based on the amount of players, reducing overhead for higher // player counts. For very many players, makes movement look jittery. // It is not recommended to use this if BROADCAST_ALL_MOVEMENT is disabled // on low tickrates, as that might drastically decrease the update rate. #define SCALE_MOVEMENT_UPDATES_TO_PLAYER_COUNT // If defined, calculates fluid flow when blocks are updated near fluids // Somewhat computationally expensive and potentially unstable #define DO_FLUID_FLOW // If defined, allows players to craft and use chests. // Chests take up 15 block change slots each, require additional checks, // and use some terrible memory hacks to function. On some platforms, this // could cause bad performance or even crashes during gameplay. #define ALLOW_CHESTS // If defined, enables flight for all players. As a side-effect, allows // players to sprint when starving. // #define ENABLE_PLAYER_FLIGHT // If defined, enables the item pickup animation when mining a block/ // Does not affect how item pickups work! Items from broken blocks still // get placed directly in the inventory, this is just an animation. // Relatively inexpensive, though requires sending a few more packets // every time a block is broken. #define ENABLE_PICKUP_ANIMATION // If defined, players are able to receive damage from nearby cacti. #define ENABLE_CACTUS_DAMAGE // If defined, logs unrecognized packet IDs // #define DEV_LOG_UNKNOWN_PACKETS // If defined, logs cases when packet length doesn't match parsed byte count #define DEV_LOG_LENGTH_DISCREPANCY // If defined, log chunk generation events // #define DEV_LOG_CHUNK_GENERATION // If defined, allows dumping world data by sending 0xBEEF (big-endian), // and uploading world data by sending 0xFEED, followed by the data buffer. // Doesn't implement authentication, hence disabled by default. // #define DEV_ENABLE_BEEF_DUMPS #define STATE_NONE 0 #define STATE_STATUS 1 #define STATE_LOGIN 2 #define STATE_TRANSFER 3 #define STATE_CONFIGURATION 4 #define STATE_PLAY 5 extern ssize_t recv_count; extern uint8_t recv_buffer[MAX_RECV_BUF_LEN]; extern uint32_t world_seed; extern uint32_t rng_seed; extern uint16_t world_time; extern uint32_t server_ticks; extern char motd[]; extern uint8_t motd_len; #ifdef SEND_BRAND extern char brand[]; extern uint8_t brand_len; #endif extern uint16_t client_count; typedef struct { short x; short z; uint8_t y; uint8_t block; } BlockChange; #pragma pack(push, 1) typedef struct { uint8_t uuid[16]; char name[16]; int client_fd; short x; uint8_t y; short z; short visited_x[VISITED_HISTORY]; short visited_z[VISITED_HISTORY]; #ifdef SCALE_MOVEMENT_UPDATES_TO_PLAYER_COUNT uint16_t packets_since_update; #endif int8_t yaw; int8_t pitch; uint8_t grounded_y; uint8_t health; uint8_t hunger; uint16_t saturation; uint8_t hotbar; uint16_t inventory_items[41]; uint16_t craft_items[9]; uint8_t inventory_count[41]; uint8_t craft_count[9]; // Usage depends on player's flags, see below // When no flags are set, acts as cursor item ID uint16_t flagval_16; // Usage depends on player's flags, see below // When no flags are set, acts as cursor item count uint8_t flagval_8; // 0x01 - attack cooldown, uses flagval_8 as the timer // 0x02 - has not spawned yet // 0x04 - sneaking // 0x08 - sprinting // 0x10 - eating, makes flagval_16 act as eating timer // 0x20 - client loading, uses flagval_16 as fallback timer // 0x40 - movement update cooldown // 0x80 - craft_items lock (for storing pointers) uint8_t flags; } PlayerData; typedef struct { uint8_t type; short x; // When the mob is dead (health is 0), the Y coordinate acts // as a timer for deleting and deallocating the mob uint8_t y; short z; // Lower 5 bits: health // Middle 1 bit: sheep sheared, unused for other mobs // Upper 2 bits: panic timer uint8_t data; } MobData; #pragma pack(pop) union EntityDataValue { uint8_t byte; int pose; }; typedef struct { uint8_t index; // 0 - Byte // 21 - Pose int type; union EntityDataValue value; } EntityData; extern BlockChange block_changes[MAX_BLOCK_CHANGES]; extern int block_changes_count; extern PlayerData player_data[MAX_PLAYERS]; extern int player_data_count; extern MobData mob_data[MAX_MOBS]; #endif执行命令chmod +x build_luckfox.sh ./build_luckfox.sh --type alpine传输到板子root目录上，配置后台运行编写openrc/bareiron#!/sbin/openrc-run name="BareIron Minecraft Server" description="Minimalist Minecraft server for embedded systems" # Path to the executable command="/root/bareiron_luckfox" # Background the process command_background=true # PID file location pidfile="/run/${RC_SVCNAME}.pid" # Working directory (where world.bin is stored) directory="/root" # Redirect output to log file (optional, helps debugging) output_log="/var/log/bareiron.log" error_log="/var/log/bareiron.err" depend() { # Start after networking is ready need net # Use DNS if available use dns logger } start_pre() { # Ensure the binary exists and is executable if [ ! -x "$command" ]; then eerror "Executable not found or not executable: $command" return 1 fi einfo "Starting $name..." } 编写install_service.sh#!/bin/sh # Installation script for BareIron OpenRC service on Alpine Linux # Run this on your Luckfox Pico SERVICE_NAME="bareiron" SERVICE_SCRIPT="openrc/bareiron" TARGET_DIR="/etc/init.d" BINARY_NAME="bareiron_luckfox" BINARY_PATH="/root/$BINARY_NAME" # Check if running as root if [ "$(id -u)" -ne 0 ]; then echo "Error: This script must be run as root." exit 1 fi # Check if binary exists on the board if [ ! -f "$BINARY_PATH" ]; then echo "Warning: Binary '$BINARY_PATH' not found." echo "Make sure you have copied '$BINARY_NAME' to /root/" fi echo "Installing service script..." if [ ! -f "$SERVICE_SCRIPT" ]; then echo "Error: Service script '$SERVICE_SCRIPT' not found in current directory." echo "Please upload the 'openrc' folder to the board first." exit 1 fi cp "$SERVICE_SCRIPT" "$TARGET_DIR/$SERVICE_NAME" chmod +x "$TARGET_DIR/$SERVICE_NAME" echo "Service installed to $TARGET_DIR/$SERVICE_NAME" echo "Adding service to default runlevel (autostart on boot)..." rc-update add "$SERVICE_NAME" default echo "---------------------------------------------------" echo "Installation Complete!" echo "---------------------------------------------------" echo "To start the server now:" echo " rc-service $SERVICE_NAME start" echo "" echo "To stop the server:" echo " rc-service $SERVICE_NAME stop" echo "" echo "To check status:" echo " rc-service $SERVICE_NAME status" echo "" echo "To view logs:" echo " tail -f /var/log/bareiron.log"执行命令，然后删除openrc/bareiron和install_service.shchmod +x install_service.sh ./install_service.sh启动： rc-service bareiron start停止： rc-service bareiron stop查看日志： tail -f /var/log/bareiron.log如何游玩下载 : 直接搜索 "HMCL 下载" 或访问其 GitHub/官网。使用方法 :打开 HMCL。点击“账户” -> 添加离线账户 (随便起个名字)。点击“下载” -> 选择 1.21.8 版本（必须与服务器版本一致）。下载完成后，点击“启动游戏”。进入游戏后，选择“多人游戏” -> “添加服务器” -> 输入Luckfox 板子的 IP 地址 -> 连接。配置内网穿透使用樱花frp创建tcp隧道重新配置内外穿透配置域名，具体看设置 SRV 解析多人服务器，填入域名（一般延迟80ms）enjoy yourself！
前言之前买了一个luckfox pico plus，有一说一，当时不知道为什么脑子抽了买个这玩意，60多💴，拿来不知道有啥用，就跟着教程随便摸索了一下，记录一下摸索过程。开始摸索先看了几遍幸狐科技给的wiki文档，有一说一，还挺通俗易懂，luckfox pico plus使用RV1103处理器，基于单核 ARM Cortex-A7 32 位架构，计算性能可达 0.5 TOPS，板载内存64MB DDR2与板载存储128MB spi nand flash，跟着教程也装了ubuntu（卡的要死）、Buildroot，不过用起来感觉不怎么样，看见教程有Alpine Linux 移植，就跟着进行了移植（幸狐这wiki搬运教程也太不上心了，我看到好多错的地方）。移植开始docker运行Alpine Linux容器我用的windows桌面端docker，所以运行前执行docker run --rm --privileged multiarch/qemu-user-static --reset --persistent yes在wsl的ubuntu22.04换行中，执行以下代码，将临时文件挂载到系统，用来存储新的rootfsdd if=/dev/zero of=rootfs.ext4 bs=1M count=100 mkfs.ext4 rootfs.ext4 mkdir -p /home/luckfox/my-rootfs sudo mount rootfs.ext4 /home/luckfox/my-rootfs然后执行启动armv7alpine容器docker run -it --name armv7alpine --net=host -v /tmp/my-rootfs:/my-rootfs arm32v7/alpine容器内操作在容器里，将Alpine Linux的官方源替换成阿里源sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories apk update安装openrc用来管理进程apk add openrc rc-update add devfs boot rc-update add procfs boot rc-update add sysfs boot添加安全终端 ：允许 root 用户从 ttyFIQ0 登录。删除tty1到tty6开始的行，这一步一定要做，否则串口启动会卡死在找ttyX上。执行完最好vi /etc/inittab看看ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100前面有没有#echo ttyFIQ0 > /etc/securetty # 删除 tty1-tty6 的配置 sed -i '/tty[1-6]/d' /etc/inittab # 添加 ttyFIQ0 自动登录配置 sed -i '/# Put a agetty on the serial port/a ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100' /etc/inittab配置静态ipcat <<EOF > /etc/network/interfaces auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.1.100 netmask 255.255.255.0 gateway 192.168.1.1 EOF添加开机启动rc-update add networking default修改dnscat <<EOF > /etc/resolv.conf nameserver 114.114.114.114 nameserver 8.8.8.8 EOF设置板子名字，方便ssh连接echo luckfox > /etc/hostname安装配置ssh，设置登录密码apk add openssh vi /etc/ssh/sshd_config sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config rc-update add sshd default passwd创建本地启动服务脚本cat <<EOF > /etc/local.d/crond.start #!/bin/bash # 网络校时并启动 crond 定时任务 ntpd -d -q && crond # 关闭板载 LED 灯 (如果你不想关灯，可以把这行去掉) echo 0 > /sys/class/leds/work/brightness EOF chmod +x /etc/local.d/crond.start rc-update add local default配置开机启动ntpd实现网络校时cat <<EOF > /etc/ntp.conf server ntp.aliyun.com iburst server ntp1.aliyun.com iburst server ntp2.aliyun.com iburst EOF rc-update add local default apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone && apk del tzdata安装需要用到的软件包，最好手动vi /etc/passwd看看第一行改成 /bin/bash了吗apk add util-linux sqlite btop bash bash-completion sed -i 's/\/bin\/ash/\/bin\/bash/g' /etc/passwd将 rootfs 打包，在宿主机的 /home/luckfox/my-rootfs/ 目录下看到一个 alpine.tar.gz 文件。cd / for d in bin etc lib root sbin usr; do tar c "$d" | tar x -C /my-rootfs; done for dir in dev proc run sys var tmp media mnt opt srv; do mkdir -p /my-rootfs/${dir}; done cd /my-rootfs/ && tar czf alpine.tar.gz *修改官方SDK根目录下的build.sh文件function build_sysdrv() { echo "============Start building sysdrv============" mkdir -p ${RK_PROJECT_OUTPUT_IMAGE} build_uboot build_kernel build_rootfs build_recovery if [ -z $RK_CUSTOM_ROOTFS ]; then rootfs_tarball="$RK_PROJECT_PATH_SYSDRV/rootfs_${RK_LIBC_TPYE}_${RK_CHIP}.tar" else rootfs_tarball="$RK_CUSTOM_ROOTFS" fi rootfs_out_dir="$RK_PROJECT_OUTPUT/rootfs_${RK_LIBC_TPYE}_${RK_CHIP}" if ! [ -d $RK_PROJECT_OUTPUT ]; then mkdir -p $RK_PROJECT_OUTPUT fi if [ -f $rootfs_tarball ]; then if [ -d $rootfs_out_dir ]; then rm -rf $rootfs_out_dir fi if [ -z $RK_CUSTOM_ROOTFS ]; then tar xf $rootfs_tarball -C $RK_PROJECT_OUTPUT else mkdir -p $rootfs_out_dir tar xf $rootfs_tarball -C $rootfs_out_dir fi else msg_error "Not found rootfs tarball: $rootfs_tarball" exit 1 fi msg_info "If you need to add custom files, please upload them to <Luckfox Sdk>/output/out/rootfs_${RK_LIBC_TPYE}_${RK_CHIP}." finish_build } function __PACKAGE_ROOTFS() { local rootfs_tarball _target_dir _install_dir if [ -z $RK_CUSTOM_ROOTFS ]; then rootfs_tarball="$RK_PROJECT_PATH_SYSDRV/rootfs_${RK_LIBC_TPYE}_${RK_CHIP}.tar" else rootfs_tarball="$RK_CUSTOM_ROOTFS" fi if [ -f $rootfs_tarball ]; then if [ -z $RK_CUSTOM_ROOTFS ]; then tar xf $rootfs_tarball -C $RK_PROJECT_OUTPUT else if [ ! -d $RK_PROJECT_PACKAGE_ROOTFS_DIR ]; then mkdir $RK_PROJECT_PACKAGE_ROOTFS_DIR fi tar xf $rootfs_tarball -C $RK_PROJECT_PACKAGE_ROOTFS_DIR fi else msg_error "Not found rootfs tarball: $rootfs_tarball" exit 1 fi if [ "$LF_TARGET_ROOTFS" == "buildroot" ] || [ "$LF_TARGET_ROOTFS" == "busybox" ]; then build_get_sdk_version cat >$RK_PROJECT_PACKAGE_ROOTFS_DIR/bin/sdkinfo <<EOF #!/bin/sh echo Build Time: $(date "+%Y-%m-%d-%T") echo SDK Version: ${GLOBAL_SDK_VERSION} EOF chmod a+x $RK_PROJECT_PACKAGE_ROOTFS_DIR/bin/sdkinfo __COPY_FILES $RK_PROJECT_PATH_APP/root $RK_PROJECT_PACKAGE_ROOTFS_DIR fi __COPY_FILES $RK_PROJECT_PATH_MEDIA/root $RK_PROJECT_PACKAGE_ROOTFS_DIR __COPY_FILES $SDK_ROOT_DIR/external $RK_PROJECT_PACKAGE_ROOTFS_DIR if [ -d "$RK_PROJECT_PACKAGE_ROOTFS_DIR/usr/share/iqfiles" ]; then ( cd $RK_PROJECT_PACKAGE_ROOTFS_DIR/etc ln -sf ../usr/share/iqfiles ./ ) fi if [ -f $RK_PROJECT_FILE_ROOTFS_SCRIPT ]; then chmod a+x $RK_PROJECT_FILE_ROOTFS_SCRIPT cp -f $RK_PROJECT_FILE_ROOTFS_SCRIPT $RK_PROJECT_PACKAGE_ROOTFS_DIR/etc/init.d fi }修改SDK根目录下的.BoardConfig.mk文件,在文件末尾添加以下内容export RK_CUSTOM_ROOTFS=../sysdrv/custom_rootfs/alpine.tar.gz删除output/out/rootfs_uclibc_rv1106目录,确保我们的rootfs生效rm -rf output/out/rootfs_uclibc_rv1106编译镜像，然后烧录cd project ./build.sh lunch ./build.sh手动挂载tf卡ls /dev/mmcblk* mkdir -p /mnt/sdcard mount -t vfat /dev/mmcblk1p1 /mnt/sdcard mount -a df -h添加开机自启vi /etc/fstab/dev/mmcblk1p1 /mnt/sdcard vfat defaults,noatime 0 0配置sshrc-service sshd status ssh-keygen -A mkdir -p /var/empty chown root:root /var/empty chmod 755 /var/empty rc-service sshd start后续单独编译Rootfs，编译后 ：在 output/image/ 目录下会生成一个新的 rootfs.img 。烧录时 ：使用瑞芯微的烧录工具，只勾选 rootfs 分区，选择这个新的 rootfs.img 进行下载。./build.sh rootfs添加内外穿透借助樱花FRP，下载安装配置cd /usr/local/bin # 替换 <下载地址> 为实际链接 wget -O frpc https://nya.globalslb.net/natfrp/client/frpc/0.51.0-sakura-12.3/frpc_linux_armv7 chmod +x frpc ./frpc -v vi /etc/init.d/frpc#!/sbin/openrc-run name="frpc" description="SakuraFrp Client" # 你的 frpc 路径 command="/usr/local/bin/frpc" # 你的启动参数 (把你的 Token 填在这里) # 例如: command_args="-f wdnmdtoken666666:12345" command_args="YOUR_TOKEN_HERE" # 让 frpc 在后台运行 command_background="yes" # PID 文件路径 (用于管理进程) pidfile="/run/frpc.pid" depend() { # 依赖网络服务，要在网络启动后再启动 need net after firewall }chmod +x /etc/init.d/frpc rc-service frpc start rc-update add frpc default添加虚拟内存SWAPdd if=/dev/zero of=/mnt/sdcard/swapfile bs=1M count=256 chmod 600 /mnt/sdcard/swapfile mkswap /mnt/sdcard/swapfile swapon /mnt/sdcard/swapfile free -h vi /etc/fstab /mnt/sdcard/swapfile none swap sw 0 0设置crond定时任务(6小时校正一次)mkdir -p /var/spool/cron/crontabs ln -s /usr/bin/vi /bin/vi crontab -e# do daily/weekly/monthly maintenance # min hour day month weekday command 0 */6 * * * ntpd -d -q