程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

Rockchip RK3399 - 移植ubuntu 20.04.4根文件系统

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板
eMMC16GB
LPDDR34GB
显示屏 :15.6英寸HDMI接口显示屏
u-bootuboot 2023.04
linux6.3
----------------------------------------------------------------------------------------------------------------------------

在上一篇文章中,我们介绍了通过busybox构建根文件系统,但是我们在使用的过程中有个致命的问题,就是缺少各种命令以及各种开发环境,比如pythonjdkmysql啊。比如我们需要使用python开发环境,那么我们就得自己去下载python源码编译,然后移植到开发板中。那我们就开始想,我们是不是像centosubuntu这些文件系统一样,直接通过aptyum命令搞定这些安装呢?

幸运的是,我们搜索网上资料发现,ubuntu针对不同的CPU架构提供相应的ubuntu base根文件系统,目前提供的架构有amd64arm64armhfi386s390xppc64。因此我们可以直接构建基于ubuntu base的根文件系统。

一、ubuntu base

ubuntu base是用于为特定需求创建自定义映像的最小rootfs,是ubuntu可以运行的最小环境。

1.1 下载源码

下载ubuntu-base的方式有很多,我们可以从官方的地址:《ttp://cdimage.ubuntu.com/ubuntu-base/releases》。

也可以其它镜像地址下载,如清华源:《https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cdimage/ubuntu-base/releases/》。

根据需要,下载自己对应的发行版本,这里我们就已20.04为例;

进入到对应的版本目录后,根据我们的CPU类型,选择对应的文件下载,这里,我们使用的是RK339964位的CPU,我们选择arm64的版本;

在宿主机ubuntu命令行运行如下命令,开始下载文件:

root@ubuntu:/work/sambashare/rk3399# wget https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cdimage/ubuntu-base/releases/20.04/release/ubuntu-base-20.04.4-base-arm64.tar.gz
root@ubuntu:/work/sambashare/rk3399# ll ubuntu-base-20.04.4-base-arm64.tar.gz
-rw-r--r-- 1 root root 26228459 Feb 22  2022 ubuntu-base-20.04.4-base-arm64.tar.gz

下载完成后,在宿主机上选择建立一个rootfs/ubuntu目录,将下载的文件ubuntu-base-20.04.4-base-arm64.tar.gz解压到该目录下;

root@ubuntu:/work/sambashare/rk3399# mkdir -p rootfs/ubuntu
root@ubuntu:/work/sambashare/rk3399# sudo tar -xpvf ubuntu-base-20.04.4-base-arm64.tar.gz -C rootfs/ubuntu/
root@ubuntu:/work/sambashare/rk3399# cd rootfs/ubuntu/
root@ubuntu:/work/sambashare/rk3399/rootfs/ubuntu# ls
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

需要注意的是:解压后的文件,需要保留ubuntu-base中的文件权限及所有者,解压时需要root权限或者sudo操作,且使用-p参数保留权限;

1.2 配置

1.2.1 安装qemu

qemu-user-static是一个仿真器,可以选取arm64配置文件仿真开发板运行环境,然后挂载下载的ubuntu-base文件,从而构建ubuntu文件系统。

root@ubuntu:/work/sambashare/rk3399/rootfs/ubuntu# sudo apt-get install qemu-user-static

由于下载的ubuntu-baseaarch64架构的,因此需要拷贝qemu-aarch64-staticubuntu/usr/bin/下;

root@ubuntu:/work/sambashare/rk3399/rootfs/ubuntu# sudo cp /usr/bin/qemu-aarch64-static ./usr/bin/
1.2.2 设置软件源

什么是源?其实吧它就像苹果和案桌的软件应用商店一样,为Linux用户提供软件下载及更新服务的。Linux家族有三个软件源系统:

  • yum源,使用这种软件管理器的主要是:redhatcentos
  • apt源,使用这种软件管理器的主要是:ubuntu
  • pacman源,使用这种软件管理器的主要是:archlinux还有新近崛起的manjaro

ubuntu是外国人开发的系统,默认使用的也是国外的源,但是从国内FQ下载国外的软件(插件)源太慢。其实国内也有很多的镜像源,比如说阿里源,网易源,清华源,中科大源(推荐,不限速),兰州大源等。

需要注意的是,这里要用ARM源,不能复制我们本机的源。

root@ubuntu:/work/sambashare/rk3399/rootfs/ubuntu# sudo vim ./etc/apt/sources.list

这里我们选择华为国内下载源,这里需要把原内容删除,全部替换为:

deb http://mirrors.huaweicloud.com/ubuntu-ports/ focal main multiverse restricted universe
deb http://mirrors.huaweicloud.com/ubuntu-ports/ focal-backports main multiverse restricted universe
deb http://mirrors.huaweicloud.com/ubuntu-ports/ focal-proposed main multiverse restricted universe
deb http://mirrors.huaweicloud.com/ubuntu-ports/ focal-security main multiverse restricted universe
deb http://mirrors.huaweicloud.com/ubuntu-ports/ focal-updates main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ focal main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ focal-backports main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ focal-proposed main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ focal-security main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ focal-updates main multiverse restricted universe

如要用于其他版本,把focal换成其他版本代号即可: 22.04:jammy20.04:focal18.04:bionic16.04:xenial14.04:trusty

需要下载失败,尝试更换下载源。

其他源,比如清华源:

deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-updates main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-backports main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-security main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-security main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-proposed main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-proposed main restricted universe multiverse

163源:

deb http://mirrors.163.com/ubuntu-ports/ focal main restricted universe multiverse
deb http://mirrors.163.com/ubuntu-ports/ focal-security main restricted universe multiverse
deb http://mirrors.163.com/ubuntu-ports/ focal-updates main restricted universe multiverse
deb http://mirrors.163.com/ubuntu-ports/ focal-proposed main restricted universe multiverse
deb http://mirrors.163.com/ubuntu-ports/ focal-backports main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu-ports/ focal main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu-ports/ focal-security main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu-ports/ focal-updates main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu-ports/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu-ports/ focal-backports main restricted universe multiverse
1.2.3 配置DNS

为了可以联网更新软件,我们拷贝本机的dns配置文件到根文件系统;

root@ubuntu:/work/sambashare/rk3399/rootfs/ubuntu# sudo cp /etc/resolv.conf ./etc/resolv.conf

然后在/etc/resolv.conf文件中添加dns

root@ubuntu:/work/sambashare/rk3399/rootfs/ubuntu# sudo vim ./etc/resolv.conf

添加内容如下:

nameserver 8.8.8.8
nameserver 114.114.114.114

修改/etc/systemd/resolved.conf

[Resolve]
DNS=8.8.8.8 114.114.114.114

1.3 挂载/卸载ubuntu-base文件系统

rootfs目录下创建挂载脚本mnt_ubuntu.sh

#!/bin/bash
mnt() {
    echo "MOUNTING"
    sudo mount -t proc /proc ${2}proc
    sudo mount -t sysfs /sys ${2}sys
    sudo mount -o bind /dev ${2}dev
    sudo mount -o bind /dev/pts ${2}dev/pts
    sudo chroot ${2}
}
umnt() {
    echo "UNMOUNTING"
    sudo umount ${2}proc
    sudo umount ${2}sys
    sudo umount ${2}dev/pts
    sudo umount ${2}dev
}
 
if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
    mnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
    umnt $1 $2
else
    echo ""
    echo "Either 1'st, 2'nd or both parameters were missing"
    echo ""
    echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
    echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
    echo ""
    echo "For example: ch-mount -m /media/sdcard/"
    echo ""
    echo 1st parameter : ${1}
    echo 2nd parameter : ${2}
fi

增加脚本执行权限:

root@ubuntu:/work/sambashare/rk3399/rootfs# sudo chmod +x mnt_ubuntu.sh
1.3.1 挂载文件系统

执行如下命令将会挂载ubuntu-base文件系统;

root@ubuntu:/work/sambashare/rk3399/rootfs# ./mnt_ubuntu.sh -m ubuntu/

执行该命令实际上就是执行:

sudo mount -t proc /proc ubuntu/proc
sudo mount -t sysfs /sys ubuntu/sys
sudo mount -o bind /dev ubuntu/dev
sudo mount -o bind /dev/pts ubuntu/dev/pts
sudo chroot ubuntu/

(1) proc文件系统是指一类虚拟文件系统,它不是从磁盘上的分区中读取数据,而是通过内核在内存中动态生成的。

proc文件系统通常用于提供有关系统内核和进程信息的接口,例如系统内存使用情况、硬件设备信息等。在Linux系统中挂载proc文件系统的命令格式如下:

mount -t proc [选项] /proc [挂载点]

其中,-t proc参数表示将要挂载的文件系统类型是proc文件系统;/proc表示要挂载的proc文件系统的挂载点;ubuntu/proc表示挂载点在当前目录下的一个名为ubuntu的子目录中的proc子目录。通过这个命令,可以将proc文件系统挂载到指定的挂载点,以便访问其中包含的有关系统内核和进程信息的接口。

(2)mount -y sysfs命令和mount -t proc同理;

(3) mount -o bind命令将前一个目录挂载到后一个目录上,所有对后一个目录的访问其实都是对前一个目录的访问;

(4) chroot ubuntu/命令把根目录换成指定的目的目录,此时根目录就变成了ubuntu-base所在目录的;用ll可以看到文件系统也有变化,此终端里的根文件系统已经切换到ubuntu目录里的根文件系统,就像进入了一个新系统,所以这也叫作是模拟运行根文件系统;

我们可以在在命令行安装软件,更改系统配置;

root@ubuntu:/# ll
total 56
drwxr-xr-x  17 root root 4096 May 29 15:18 ./
drwxr-xr-x  17 root root 4096 May 29 15:18 ../
lrwxrwxrwx   1 root root    7 Feb 21  2022 bin -> usr/bin/
drwxr-xr-x   2 root root 4096 Apr 15  2020 boot/
drwxr-xr-x  18 root root 4300 May 30 10:54 dev/
drwxr-xr-x  30 root root 4096 May 29 15:30 etc/
drwxr-xr-x   2 root root 4096 Apr 15  2020 home/
lrwxrwxrwx   1 root root    7 Feb 21  2022 lib -> usr/lib/
drwxr-xr-x   2 root root 4096 Feb 21  2022 media/
drwxr-xr-x   2 root root 4096 Feb 21  2022 mnt/
drwxr-xr-x   2 root root 4096 Feb 21  2022 opt/
dr-xr-xr-x 366 root root    0 May 30 11:19 proc/
drwx------   2 root root 4096 May 29 15:38 root/
drwxr-xr-x   4 root root 4096 Feb 21  2022 run/
lrwxrwxrwx   1 root root    8 Feb 21  2022 sbin -> usr/sbin/
drwxr-xr-x   2 root root 4096 Feb 21  2022 srv/
dr-xr-xr-x  13 root root    0 May 30 10:54 sys/
drwxrwxrwt   2 root root 4096 Feb 21  2022 tmp/
drwxr-xr-x  10 root root 4096 Feb 21  2022 usr/
drwxr-xr-x  11 root root 4096 Feb 21  2022 var/

文件系统配置完成,输入exit ,然后运行脚本卸载文件系统;

1.3.2 卸载文件系统

执行如下命令卸载文件系统:

root@ubuntu:/work/sambashare/rk3399/rootfs# ./mnt_ubuntu.sh -u ubuntu/

实际上就是把之前挂载的全部卸载掉。

1.4 安装软件以及桌面环境等

以下安装需要在挂载ubuntu-base文件系统之后进行。

1.4.1 安装必要软件

默认下,ubuntu-base基本没安装什么工具,如果要正常使用,我们可以根据需要安装,以下基本的几个建议都进行安装;

root@ubuntu:/# apt-get update                #更新一下软件包的数据库
root@ubuntu:/# apt-get install net-tools
root@ubuntu:/# apt-get install ethtool
root@ubuntu:/# apt-get install ifupdown
root@ubuntu:/# apt-get install psmisc
root@ubuntu:/# apt-get install nfs-common
root@ubuntu:/# apt-get install htop
root@ubuntu:/# apt-get install vim
root@ubuntu:/# apt-get install rsyslog
root@ubuntu:/# apt-get install iputils-ping
root@ubuntu:/# apt-get install language-pack-en-base
root@ubuntu:/# apt-get install sudo
root@ubuntu:/# apt-get install network-manager systemd
root@ubuntu:/# apt install iputils-ping
root@ubuntu:/# apt install openssh-sftp-server

如果出现如下问题:

root@ubuntu:/# apt-get install net-tools
E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem.
root@ubuntu:/# sudo dpkg --configure -a
1.4.2 安装桌面环境

ubuntu官方考虑到使用者的不同需求,提供各种不同的发行板。虽然发布了几种版本的ubuntu系统,但是他们的核心系统是一模一样的,可以这么说不同发行版的ubuntu的区别在于:桌面环境的不同和预设安装的软件的不同;

(1) ubuntuubuntu是主要的发行版,它使用GNOME这个桌面环境,相依的虚拟套件是ubuntu-desktop

(2) kubuntu:采用以功能强大闻名的KDE当成桌面环境,功能众多以及华丽桌面配置为其卖点,相依的虚拟套件是kubuntu-desktop

(3) xubuntu:采用了比较轻量级的桌面环境Xfce,所以这个版本的诉求是针对比较老旧的电脑。Xfce的设计理念是减少使用的系统资源,并且成为一套完整可用的桌面环境。功能来说,虽没有像ubuntukubuntu功能众多,但也具备基本文书、上网与影音等功能。相依的虚拟套件是xubuntu-desktop

(4) lubuntu:采用了比Xfce更轻量的桌面环境Lxde,是自从10.04之后才新增的版本。这个版本的诉求是针对小笔电、效能较差的电脑或追求快速的使用者。虽然精简,但仍然符合大多数使用者对于桌面的要求。同样具备文书处理、上网和影音,更特别的是,它已经内建了几乎所有播放需要使用到的解码器,可直接播放mp3rmvb等格式的档案,相依的虛擬套件是lubuntu-desktop

(5) edubuntu:是ubuntu的教育分支版。它主要的诉求是教育使用,所以里面有很多精心挑选的教育相关套件,和一些小游戏等。而为了可以让教师们快速安装到电脑教室或安装到老旧的电脑里,它还使用了LTSP技术,可以让老师们在短短的一个小时内建立整个电脑教师环境。使用GNOME桌面环境相依的虚拟套件是edubuntu-desktop;使用KDE桌面环境相依的虚拟套件是edubuntu-desktop-kde

LTSP (Linux Terminal Server Project)的发展目的是提供多个工作站只负责显示,实际软体和计算在伺服器上面,也叫做thin-clients机制。它的好处是工作站可以使用很老旧的电脑,因为工作站只用来当XTerminal,所以一切资源都放在伺服器上。这个方式可以让维修成本和管理成本减少很多,所以特别适合公司和学校使用;

这里我们选择一个虚拟套件安装,这里我最初的虚拟套件是ubuntu-desktop

root@ubuntu:/# apt-get install ubuntu-desktop

需要注意:安装之后桌面环境在开发板运行起来后,CPU占用率相对较高,这下图是我当时使用火狐浏览器打开视频的CPU占用情况,在RK3399上性能不咋样,因此如果不是必要尽量不要安装桌面环境环境,可以考虑采用安装x11vnc远程桌面的解决方案。

后面我又尝试安装了xubuntu,安装过程中有几点需要注意:

  • 语言布局我选的Chinese - Hanyu Pinyin (altgr)
  • 显示管理器(它们提供图形登录并处理用户身份验证)我选择的是lightdm;可以通过cat /etc/X11/default-display-manager命令查看安装的哪一个;

后面介绍的流程都是针对ubuntu的,当然对xubuntulubuntu等基本都是适用的(如果有差异,请自行更正)。

1.4.3 安装ubuntu-restricted-extras

ubuntu-restricted-extras是一个包含各种基本软件,如Flash插件、unrargstreamermp4ubuntu中的chromium浏览器的编解码器等的软件包。

由于这些软件不是开源软件,并且其中一些涉及软件专利,因此ubuntu默认情况下不会安装它们。你必须使用multiverse仓库,它是ubuntu专门为用户提供非开源软件而创建的仓库。

由于ubuntu-restrcited-extras软件包在multiverse仓库中,因此你应验证系统上已启用multiverse仓库:

root@ubuntu:/# sudo add-apt-repository multiverse

然后你可以使用以下命令安装:

root@ubuntu:/# sudo apt install ubuntu-restricted-extras

安装完成后,由于新安装的媒体编解码器,你应该可以播放MP3和其他媒体格式了。

kubuntulubuntuxubuntu都有此软件包,并有各自不同的名称。它们本应使用相同的名字,但不幸的是并不是。

kubuntu上,使用以下命令:

sudo apt install kubuntu-restricted-extras

lubuntu上,使用:

sudo apt install lubuntu-restricted-extras

xubuntu上,你应该使用:

sudo apt install xubuntu-restricted-extras

kubuntulubuntu下安装我没有测试,但是在xubuntu下安装发现找不到安装包,可以考虑安装ubuntu-restricted-extras试试。

注意:如果有看视频的诉求,这个一定要安装,不然是无法观看视频的,浏览器也是无法打开视频。

1.5 系统设置

以下配置需要在挂载ubuntu-base文件系统之后进行。

1.5.1 修改root用户密码

为了方便, 这里我直接将密码设置为123456

root@ubuntu:/# passwd root
New password:
Retype new password:
passwd: password updated successfully

修改sshd配置文件的PermitRootLogin属性:

root@ubuntu:/# vim /etc/ssh/sshd_config

在最后面加上,PermitRootLogin yes

PermitRootLogin yes #是否允许root登录,默认是允许的,但建议设置为no
LoginGraceTime 600m #设置指定时间内没有成功登录,将会断开连接,若无单位则默认时间为秒
1.5.2 新增用户

新增用户,这个根据具体的情况,嵌入式的环境下,我们可以不用新增,直接使用root的用户就行了,如果要新增,执行如下的步骤:

root@ubuntu:/# adduser zhengyang
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LC_TIME = "en_DK.UTF-8",
        LANG = "zh_CN.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Adding user `zhengyang' ...
Adding new group `zhengyang' (1000) ...
Adding new user `zhengyang' (1000) with group `zhengyang' ...
Creating home directory `/home/zhengyang' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for zhengyang
Enter the new value, or press ENTER for the default
        Full Name []:
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:
Is the information correct? [Y/n] y

我了方便,这里新增的zhengyang用户密码也是123456;这里需要输入用户的一些信息,也可以直接回车保留默认值即可。

默认情况下新用户是不能使用sudo命令的,我们需要修改/etc/sudoers文件。

/etc/sudoers文件默认是只读的,因此需要先修改此文件的写权限,使用如下命令:

root@ubuntu:/# chmod u+w /etc/sudoers

然后使用vim打开/etc/sudoers,找到root ALL=(ALL:ALL) ALL这一行,在这一行下面添加:

zhengyang    ALL=(ALL:ALL) ALL

修改完成以后保存退出,重新恢复/etc/sudoers的只读属性,使用如下命令:

root@ubuntu:/# chmod u-w /etc/sudoers
1.5.3 设置主机名称和IP
root@ubuntu:/# echo "rk3399" > /etc/hostname
root@ubuntu:/# echo "127.0.0.1 localhost" >> /etc/hosts
root@ubuntu:/# echo "127.0.0.1 rk3399" >> /etc/hosts

/etc/hostname中存放的是主机名,/etc/hosts存放的是域名与ip的对应关系,域名与主机名没有任何关系,你可以为任何一个IP指定任意一个名字。

1.5.4 配置DHCP

我们配置一下网络DHCP,这样系统启动以后就会自动设置好网络。RK3399默认有1个有线网卡、此外板载2.4G & 5G双频WiFi模组。

网卡eth0

root@ubuntu:/# echo auto eth0 > /etc/network/interfaces.d/eth0
root@ubuntu:/# echo iface eth0 inet dhcp >> /etc/network/interfaces.d/eth0

其中,eth0是接口的名称,inetIPV4协议的设置方式,dhcp表示使用DHCP服务器分配IP地址,如果需要使用静态IP地址,其值应改为static,例如/etc/network/interfaces.d/eth0

auto eth0
iface eth0 inet static
address 192.168.0.105     #设置IP地址为192.168.0.105
netmask 255.255.255.0     #设置掩码为255.255.255.0
gateway 192.168.0.1       #设置默认网关为192.168.0.1

无线网卡wlan

root@ubuntu:/# echo auto wlan0 > /etc/network/interfaces.d/wlan0
root@ubuntu:/# echo allow-hotplug wlan0 >> /etc/network/interfaces.d/wlan0
root@ubuntu:/# echo iface wlan0 inet dhcp >> /etc/network/interfaces.d/wlan0

在实际测试中网口必须接入网线系统才能正常启动,就是在不联网的情况下,每次开机都要等待很久,卡在网络连接上5分钟,这里我们可以修改下面这个文件:

root@ubuntu:/# vim /lib/systemd/system/networking.service

将里面的TimeoutStartSec=5min修改为:

TimeoutStartSec=1sec
1.5.5 修改系统重启默认等待时间

重启开发板的时候,如果有进程没有结束,系统就会等待,默认等待时间很长,导致重启速度慢。

我们可以修改默认等待时间:

root@ubuntu:/# vim /etc/systemd/system.conf

找到这几行:

#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultTRestartSec=100ms

解除注释并将DefaultTimeoutStopSec=90s修改为:

DefaultTimeoutStopSec=1s
1.5.6 设置开机免密登录到图形界面

(1) 我们使用以下命令修改50-ubuntu.conf文件:

root@ubuntu:/# vim /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf

在文件末尾加入以下内容:

greeter-show-manual-login=true
 
all-guest=false

修改完成后保存退出。

(2) 输入以下命令修改gdm-autologin文件内容:

root@ubuntu:/# vim /etc/pam.d/gdm-autologin

注释掉下面这一行,并保存退出。

#auth    required        pam_succeed_if.so user != root quiet_success

(3) 输入以下命令修改gdm-password文件内容:

root@ubuntu:/# vim /etc/pam.d/gdm-password

注释掉下面这一行,并保存退出。

auth    required        pam_succeed_if.so user != root quiet_success

(4) 输入以下命令修改/root/.profile文件:

root@ubuntu:/# vim /root/.profile

将文件最后一行改为以下内容,修改完成如所示:

tty -s && mesg n || true

(5) 输入以下命令修改custom.conf文件:

root@ubuntu:/# vim /etc/gdm3/custom.conf

在文件末尾添加以下内容:

[daemon]
AutomaticLoginEnable=true
AutomaticLogin=root
TimedLoginEnable=true
TimedLogin=root
TimedLoginDelay=10

至此,我们构建的文件系统,就可以开启root用户免密登录可视界面了!

1 .6 安装linux-headers

如果想在开发板上进行编译安装源代码,必须安装linux-headers,具体参考:《Rockchip RK3399 - 移植uboot 2017.09 & linux 4.19(友善之家脚本方式)》。

1.6.1 编译linux-headers

编译内核头文件:

root@ubuntu:/work/sambashare/rk3399/linux-6.3# make -j$(nproc) bindeb-pkg

make bindeb-pkg是一个用于构建debian包的命令,它通常用于编译linux内核并生成对应的debian软件包。

当执行make bindeb-pkg 命令时,它会读取当前目录下的linux内核源代码,并根据配置文件进行内核编译。

编译过程将包括编译内核、生成模块、创建initramfs等步骤。最后,它将生成一组二进制文件和相关的debian控制文件,用于创建 debian包。

linux 6.3执行完成会在内核源码debian目录生成以下文件;

root@ubuntu:/work/sambashare/rk3399/linux-6.3# ll debian/
-rw-r--r--  1 root root    6 1月  30 23:06 arch
-rw-r--r--  1 root root  137 1月  30 23:06 changelog
-rw-r--r--  1 root root 1239 1月  30 23:06 control
-rw-r--r--  1 root root  691 1月  30 23:06 copyright
-rw-r--r--  1 root root  269 1月  30 23:12 files
drwxr-xr-x  5 root root 4096 1月  30 23:11 linux-headers/
drwxr-xr-x  7 root root 4096 1月  30 23:11 linux-image/
drwxr-xr-x  4 root root 4096 1月  30 23:11 linux-image-dbg/
drwxr-xr-x  4 root root 4096 1月  30 23:11 linux-libc-dev/
-rwxr-xr-x  1 root root  486 1月  30 23:06 rules*

同时会在内核上一级目录下生成如下deb包:

root@ubuntu:/work/sambashare/rk3399# ll | grep linux
-rw-r--r--  1 root root  8407716 1月  30 23:11 linux-headers-6.3.0_6.3.0-17_arm64.deb
-rw-r--r--  1 root root 25455204 1月  30 23:11 linux-image-6.3.0_6.3.0-17_arm64.deb
-rw-r--r--  1 root root 69655120 1月  30 23:12 linux-image-6.3.0-dbg_6.3.0-17_arm64.deb
-rw-r--r--  1 root root  1260012 1月  30 23:11 linux-libc-dev_6.3.0-17_arm64.deb
-rw-r--r--  1 root root     7391 1月  30 23:12 linux-upstream_6.3.0-17_arm64.buildinfo
-rw-r--r--  1 root root     2161 1月  30 23:12 linux-upstream_6.3.0-17_arm64.changes

其中:

  • linux-image-<version>.deb:内核镜像文件,用于安装和引导新的内核;
  • linux-headers-<version>.deb:内核头文件,用于开发其他软件或编译内核模块;
  • linux-libc-dev_<version>.deb:用于构建用户空间软件的头文件和静态库;

这些生成的debian包可以在 debian或基于debian的系统上安装和使用。

1.6.2 安装linux-headers

ubuntu宿主机将我们编译的头文件拷贝到根文件系统/opt目录下;

root@ubuntu:/work/sambashare/rk3399/rootfs/ubuntu# cp /work/sambashare/rk3399/linux-headers-6.3.0_6.3.0-17_arm64.deb ./opt

首先需要挂载ubuntu-base文件系统;

root@ubuntu:/work/sambashare/rk3399/rootfs# ./mnt_ubuntu.sh -m ubuntu/

安装linux-headers

root@ubuntu:/# cd /opt
root@ubuntu:/opt# dpkg -i /opt/linux-headers-*.deb
Selecting previously unselected package linux-headers-6.3.0.
(Reading database ... 114184 files and directories currently installed.)
Preparing to unpack .../linux-headers-6.3.0_6.3.0-17_arm64.deb ...
Unpacking linux-headers-6.3.0 (6.3.0-17) ...
Setting up linux-headers-6.3.0 (6.3.0-17) ...

安装完成后,我们可以看一下/usr/src目录下是否创建了linux-headers-$(uname-r)文件;

root@ubuntu:/# ll /usr/src/
drwxr-xr-x 5 root root 4096 Nov 13 00:35 linux-headers-6.3.0
root@ubuntu:/# ll /usr/src/linux-headers-6.3.0/
-rw-r--r--  1 root root  264166 Sep 23 20:58 .config
-rw-r--r--  1 root root   71751 Jun 12  2023 Makefile
-rw-r--r--  1 root root 1064676 Sep 23 20:58 Module.symvers
drwxr-xr-x  3 root root    4096 Nov 13 00:35 arch/
drwxr-xr-x 33 root root    4096 Nov 13 00:35 include/
drwxr-xr-x 17 root root   12288 Nov 13 00:35 scripts/

文件系统配置完成,输入exit ,然后执行如下命令卸载文件系统:

root@ubuntu:/work/sambashare/rk3399/rootfs# ./mnt_ubuntu.sh -u ubuntu/

1.7 安装驱动模块

安装驱动模块,具体参考:《Rockchip RK3399 - 移植uboot 2017.09 & linux 4.19(友善之家脚本方式)》。

1.7.1 编译驱动模块

首先编译内核模块,如果已经编译过忽略即可:

root@ubuntu:/work/sambashare/rk3399/linux-6.3# export KMODULES_OUTDIR=`pwd`/out/output_rk3399_kmodules
root@ubuntu:/work/sambashare/rk3399/linux-6.3# rm -rf ${KMODULES_OUTDIR}
root@ubuntu:/work/sambashare/rk3399/linux-6.3# mkdir -p ${KMODULES_OUTDIR}
root@ubuntu:/work/sambashare/rk3399/linux-6.3# make modules -j$(nproc)
1.7.2 安装驱动模块

然后执行如下脚本install_modules.sh

#!/bin/bash
CROSS_COMPILE=arm-linux-
ARCH=arm64
# 用于构建并输出内核版本号
KERNEL_VER=`make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} kernelrelease`
KMODULES_OUTDIR=`pwd`/out/output_rk3399_kmodules/${KERNEL_VER}
rm -rf ${KMODULES_OUTDIR}
make CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH} INSTALL_MOD_PATH=${KMODULES_OUTDIR} modules_install
if [ $? -ne 0 ]; then
    echo "failed to build kernel modules."
         exit 1
fi
# 如果模块依赖文件modules.dep不存在,则生成内核模块的依赖关系
[ ! -f "${KMODULES_OUTDIR}/lib/modules/${KERNEL_VER}/modules.dep" ] && depmod -b ${KMODULES_OUTDIR} -E Module.symvers -F System.map -w ${KERNEL_VER}
# 去除驱动中的符号信息
(cd ${KMODULES_OUTDIR} && find . -name \*.ko | xargs ${CROSS_COMPILE}strip --strip-unneeded)

# 更新内核模块依赖
(cd ${KMODULES_OUTDIR}/lib/modules/${KERNEL_VER}/ && {
	rm -rf ./build ./source
	echo "depmod ${KMODULES_OUTDIR} ${KERNEL_VER} ..."
	depmod -a -b ${KMODULES_OUTDIR} ${KERNEL_VER}
})

ubuntu宿主机将我们编译的驱动模块拷贝到根文件系统/lib/modules目录下;

root@ubuntu:/work/sambashare/rk3399/linux-6.3# ll ${KMODULES_OUTDIR}
drwxr-xr-x 3 root root 4096 2月   3 11:57 lib/
root@ubuntu:/work/sambashare/rk3399/linux-6.3# ll ${KMODULES_OUTDIR}/lib
drwxr-xr-x 3 root root 4096 2月   3 11:57 modules/
root@ubuntu:/work/sambashare/rk3399/linux-6.3# ll ${KMODULES_OUTDIR}/lib/modules
drwxr-xr-x 3 root root 4096 2月   3 12:02 6.3.0/
root@ubuntu:/work/sambashare/rk3399/linux-6.3# ll ${KMODULES_OUTDIR}/lib/modules/6.3.0
drwxr-xr-x 9 root root   4096 2月   3 12:02 kernel/
-rw-r--r-- 1 root root 383978 2月   3 12:02 modules.alias
-rw-r--r-- 1 root root 376985 2月   3 12:02 modules.alias.bin
-rw-r--r-- 1 root root  45077 2月   3 12:01 modules.builtin
-rw-r--r-- 1 root root  83833 2月   3 12:02 modules.builtin.alias.bin
-rw-r--r-- 1 root root  48994 2月   3 12:02 modules.builtin.bin
-rw-r--r-- 1 root root 252258 2月   3 12:01 modules.builtin.modinfo
-rw-r--r-- 1 root root  73761 2月   3 12:02 modules.dep
-rw-r--r-- 1 root root 118208 2月   3 12:02 modules.dep.bin
-rw-r--r-- 1 root root    128 2月   3 12:02 modules.devname
-rw-r--r-- 1 root root  41203 2月   3 12:01 modules.order
-rw-r--r-- 1 root root    402 2月   3 12:02 modules.softdep
-rw-r--r-- 1 root root 131791 2月   3 12:02 modules.symbols
-rw-r--r-- 1 root root 162601 2月   3 12:02 modules.symbols.bin
root@ubuntu:/work/sambashare/rk3399/linux-6.3# cp -af ${KMODULES_OUTDIR}/lib/modules/* /work/sambashare/rk3399/rootfs/ubuntu/lib/modules
root@ubuntu:/work/sambashare/rk3399/linux-6.3# ll /work/sambashare/rk3399/rootfs/ubuntu/lib/modules
drwxr-xr-x   3 root root 4096 2月   3 12:18 6.3.0/

二、制作ext4根文件系统镜像

2.1 初始化

建立根文件系统挂载点ubuntu_rootfs

root@ubuntu:/work/sambashare/rk3399/rootfs# mkdir ubuntu_rootfs

创建空镜像文件ubuntu_ext4_rootfs.img,大小6144(设置为文件系统大小x2),具体需要的大小可以通过du build -h查看我们根文件系统大小调整;

root@ubuntu:/work/sambashare/rk3399/rootfs# dd if=/dev/zero of=ubuntu_ext4_rootfs.img bs=1M count=6144
root@ubuntu:/work/sambashare/rk3399/rootfs# ll ubuntu_ext4_rootfs.img
-rw-r--r-- 1 root root 6442450944 May 30 20:53 ubuntu_ext4_rootfs.img
root@ubuntu:/work/sambashare/rk3399/rootfs# mkfs.ext4 ubuntu_ext4_rootfs.img

2.2 挂载

将该镜像文件挂载到ubuntu_rootfs

root@ubuntu:/work/sambashare/rk3399/rootfs# mount ubuntu_ext4_rootfs.img ubuntu_rootfs/

然后将ubuntu的文件复制到该空文件夹中:

root@ubuntu:/work/sambashare/rk3399/rootfs# cp ./ubuntu/* ./ubuntu_rootfs/ -af

使用df命令可以查看是否已经挂载:

root@ubuntu:/work/sambashare/rk3399/rootfs# df ubuntu_rootfs
文件系统         1K-块    已用    可用 已用% 挂载点
/dev/loop12    6102624 3804372 1967296   66% /work/sambashare/rk3399/rootfs/ubuntu_rootfs

2.3 卸载

将之前挂载的卸载掉:

root@ubuntu:/work/sambashare/rk3399/rootfs# umount ubuntu_rootfs
root@ubuntu:/work/sambashare/rk3399/rootfs# ll ubuntu_rootfs/
总用量 8
drwxr-xr-x 2 root root 4096 May 30 20:57 ./
drwxr-xr-x 5 root root 4096 May 30 20:58 ../

此时我们已经得到了ext4根文件系统镜像ubuntu_ext4_rootfs.img

root@ubuntu:/work/sambashare/rk3399/rootfs# ll ubuntu_ext4_rootfs.img
-rw-r--r-- 1 root root 6442450944 May 30 21:05 ubuntu_ext4_rootfs.img

删除文件夹ubuntu_rootfs

root@ubuntu:/work/sambashare/rk3399/rootfs# rm -rf ubuntu_rootfs

e2fsck修复及检测镜像文件系统,resize2fs减小镜像文件的大小;

root@ubuntu:/work/sambashare/rk3399/rootfs# e2fsck -p -f ubuntu_ext4_rootfs.img
root@ubuntu:/work/sambashare/rk3399/rootfs# resize2fs -M ubuntu_ext4_rootfs.img
root@ubuntu:/work/sambashare/rk3399/rootfs# ll ubuntu_ext4_rootfs.img
-rw-r--r-- 1 root root 4323823616 May 30 21:08 ubuntu_ext4_rootfs.img
root@ubuntu:/work/sambashare/rk3399/rootfs# du -sh ubuntu_ext4_rootfs.img
3.8G    ubuntu_ext4_rootfs.img

最终得到的ext4根文件系统镜像ubuntu_ext4_rootfs.img大小为3.8G

2.4 总结

我们将之前的步骤,编写成mk_rootfs.sh脚本:

#!/bin/bash
rootfs_name=ubuntu_ext4_rootfs.img   # 根文件系统名称
ubuntu_path=./ubuntu  # ubuntu文件系统路径

rm -rf ubuntu_rootfs
mkdir ubuntu_rootfs

dd if=/dev/zero of=${rootfs_name} bs=1M count=6144
mkfs.ext4 ${rootfs_name}

mount ${rootfs_name} ubuntu_rootfs/
cp ${ubuntu_path}/* ./ubuntu_rootfs/ -af

umount ubuntu_rootfs
rm -rf ubuntu_rootfs

e2fsck -p -f ${rootfs_name}
resize2fs -M ${rootfs_name}

三、烧录根文件系统

在前面文章我们已经多次介绍过使用rkdeveloptool烧录程序了,这里不再重复介绍rkdeveloptool有关内容。

3.1 准备镜像

我们按照之前的流程得到了如下文件idbloader.imgu-boot.itbkernel.itbubuntu_ext4_rootfs.img

关于uboot 2023.04镜像以及linux 6.3 FIT uImage镜像制作参考:《Rockchip RK3399 - 移植uboot 2023.04》。

按照Rockchip官方要求将:

  • idbloader.img烧录到eMMC0x40扇区;
  • u-boot.itb烧录到0x4000扇区;
  • 烧录kernel.itb0x8000扇区;
  • ubuntu_ext4_rootfs.img烧录到0x40000扇区;

我们需要将idbloader.imgu-boot.itbkernel.itbubuntu_ext4_rootfs.img拷贝到rkdeveloptool路径下:

root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# cp ../u-boot-2023.04/idbloader.img ./
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# cp ../u-boot-2023.04/u-boot.itb ./
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# cp ../linux-6.3/kernel.itb ./
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# mv ../rootfs/ubuntu_ext4_rootfs.img ./

3.2 进入MASKROM升级模式

NanoPC-T4开发板如需进入MASKROM升级模式,需要进入如下操作:

  • 将开发板连接上电源,并且连接Type-C数据线到PC
  • 按住BOOT键再长按Power键开机(保持按下BOOT5秒以上),将强制进入MASKROM模式。

一般电脑识别到USB连接,都会发出声音。或者观察虚拟机右下角是否突然多个USB设备:右键点击链接;

3.3 下载ubuntu_ext4_rootfs.img

使用下载引导命令去使目标机器初始化DDR与运行usbplug(初始化DDR的原因是由于升级需要很大的内存,所以需要使用到DDR);

root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool db rk3399_loader_v1.27.126.bin
Downloading bootloader succeeded.

由于BootROM启动会将rk3399_loader_v1.27.126.bin将在到内部SRAM中,然后跳转到ddr.bin代码进行DDR的初始化,ddr.bin执行之后会回跳到BootROM程序,BootROM程序继续加载usbplug.bin,由usbplug.bin完成程序的下载以及烧录到eMMC

使用wl命令烧写镜像到目标机器的eMMC,需要注意的是访问DDR所需的所有其他命令都应在使用db命令之后才能使用;

root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x40 idbloader.img
Write LBA from file (100%)
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x4000 u-boot.itb
Write LBA from file (100%)
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x8000 kernel.itb
Write LBA from file (100%)
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x40000 ubuntu_ext4_rootfs.img
Write LBA from file (100%)
root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool rd
Reset Device OK.

在烧写镜像完成后使用rd命令重启目标机器:

root@ubuntu:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool rd
Reset Device OK.

3.4 启动内核

3.4.1 串口连接

使用准备好的USB转串口适配器和连接线(需另购),连接开发板:

引脚 开发板接口 USB转串口
1 GNC -
2 VCC 5V -
3 UART2DBG_TX RX
4 UART2DBG_RX TX
3.4.2 MobaXterm

这里我使用的串口调试工具是MobaXterm,选择串口端口,设置波特率为1152008位数据位,1位停止位。

3.4.3 开发板上电

将开发板通过HDMI接口连接上显示器,给开发板上电,通过串口打印输出:

U-Boot TPL 2023.04 (May 30 2023 - 21:31:09)
Channel 0: LPDDR3, 800MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB
Channel 1: LPDDR3, 800MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB
256B stride
Trying to boot from BOOTROM
Returning to boot ROM...

U-Boot SPL 2023.04 (May 30 2023 - 21:31:09 +0800)
Trying to boot from MMC2
spl_load_fit_image: Skip load 'atf@5': image size is 0!
cannot find image node 'atf@6': -1
NOTICE:  BL31: v2.8(release):c194aa0
NOTICE:  BL31: Built : 19:26:54, May 11 2023

U-Boot 2023.04 (May 30 2023 - 21:31:23 +0800)

SoC: Rockchip rk3399
Reset cause: POR
Model: Rockchip RK3399 Evaluation Board
DRAM:  4 GiB (effective 3.9 GiB)
Core:  254 devices, 27 uclasses, devicetree: separate
MMC:   mmc@fe320000: 1, mmc@fe330000: 0
Loading Environment from MMC... OK
In:    serial
Out:   serial
Err:   serial
Model: Rockchip RK3399 Evaluation Board
Net:   eth0: ethernet@fe300000
Hit any key to stop autoboot:  0
=>

在倒计时执行完之前,按CTRL+C即可进入uboot命令行。

3.4.4 设置ip以及bootargs

这里我们这是本机和服务器的ip地址:

=> setenv ipaddr 192.168.0.105
=> setenv serverip 192.168.0.200
=> saveenv
Saving Environment to MMC... Writing to MMC(0)... OK

设置启动命令参数:

=> setenv bootargs earlycon=uart8250,mmio32,0xff1a0000 console=tty0 console=ttyS2,115200n8 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init  
=> saveenv
Saving Environment to MMC... Writing to MMC(0)... OK
=>  print bootargs
bootargs=earlycon=uart8250,mmio32,0xff1a0000 console=tty0 console=ttyS2,115200n8 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init

在配置了console=ttyS2,115200n8的情况下,只有配置了console=tty0或者console=tty1内核启动信息才会在显示器上显示,tty0表示当前所使用虚拟终端的一个别名,内核启动的时候,内核日志会发送到console指定的终端设备上。

/dev/tty1~/dev/tty6是虚拟终端在文件系统中的表示(可以通过CTRL+ALT+[F1~F6]切换虚终端),虚拟终端的输入数据来自键盘、同时将tty驱动的输出到显示器,他们共享同一个的键盘显示器套件。

后面测试可以正常启动内核并进入ubuntu桌面系统的话,最好把console=ttyS2,115200n8配置移除掉,因为这个会严重影响CPU的性能。

3.4.5 设置GPT分区表

使用以下命令将分区信息写入GPT分区表:

=> gpt write mmc 0 'uuid_disk=69DAD710-2CE4-4E3C-B16C-21A1D49ABED3;name=loader1,start=32K,size=4000K,uuid=8ef917d1-5bd0-2c4f-9a33-b225b21cf919;name=loader2,start=8MB,size=4MB,uuid=77877125-2374-ad40-8b02-609e37971c59;name=trust,size=4M,uuid=b4b84b8a-f8ae-0443-b5af-3605b195c4c9;name=boot,size=112M,bootable,uuid=35219908-db08-c643-9133-2213191e57ff;name=rootfs,size=-,uuid=b921b045-1df0-41c3-af44-4c6f280d3fae'
Writing GPT: success!
=> part list mmc 0                                                                                                                                                                                                                                      Partition Map for MMC device 0  --   Partition Type: EFI                                                                    
Part    Start LBA       End LBA         Name
        Attributes
        Type GUID
        Partition GUID
  1     0x00000040      0x00001f7f      "loader1"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
                (data)
        guid:   8ef917d1-5bd0-2c4f-9a33-b225b21cf919
  2     0x00004000      0x00005fff      "loader2"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
                (data)
        guid:   77877125-2374-ad40-8b02-609e37971c59
  3     0x00006000      0x00007fff      "trust"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
                (data)
        guid:   b4b84b8a-f8ae-0443-b5af-3605b195c4c9
  4     0x00008000      0x0003ffff      "boot"
        attrs:  0x0000000000000004
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
                (data)
        guid:   35219908-db08-c643-9133-2213191e57ff
  5     0x00040000      0x01d1efde      "rootfs"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
                (data)
        guid:   b921b045-1df0-41c3-af44-4c6f280d3fae
3.4.5 bootm命令启动内核

kernel.itbeMMC 0x8000扇区加载到内存地址0x10000000处,并使用bootm命令启动内核;

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(6190)  评论(20编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2021-05-31 Mini2440开发板及程序烧录、启动方式
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示