当前位置: 首页 > news >正文

Linux内核学习(一)——Vmware虚拟机安装Ubuntu20.4系统及QEMU模拟ARM64 Linux

Linux内核学习(一)——Vmware虚拟机安装Ubuntu20.4系统及QEMU模拟ARM64 Linux

小编有话说:
在实验进行前,建议大家提前去学习快照功能,具体原因如下:
1.因为我们在虚拟机上面完成实验的时候,经常会因为马虎或者资料寻找错误导致整个实验报废,如果不使用快照,很有可能会导致之前那的努力白费;
2.如果想深入学习命令行,可以启动同样的快照,在不同的命令行操作下编译,更能深刻理解其中的含义。
学习博客:https://zhuanlan.zhihu.com/p/196701930 , https://www.cnblogs.com/liuyuelinfighting/p/15484974.html
在这里插入图片描述

目录标题

  • Linux内核学习(一)——Vmware虚拟机安装Ubuntu20.4系统及QEMU模拟ARM64 Linux
  • 一.实验目的
  • 二.实验
    • (一)实验1.1**Ubuntu Linux 20.04系统的安装**
      • 实验目的
      • 实验步骤
    • (二)实验 1-2:给 Ubuntu Linux 系统更换心脏(内核升级)
      • 实验目的
      • 实验步骤
      • 1. 安装依赖包
          • 启用 deb-src 仓库
          • 更新软件包列表
          • 验证操作
      • 2. 下载最新内核源码
      • 3. 解压内核源码
      • 4. 配置内核选项
      • 5. 编译内核
        • 安装缺失的依赖库
        • 清理编译环境并重新编译
      • 6. 安装内核模块
      • 7. 安装内核
      • 8. 更新引导加载程序(GRUB)
      • 9. 重启并验证
    • (三)实验 1-3:使用 QEMU 虚拟机运行 ARM64 Linux 系统并编译内核模块
      • 实验目的
      • 实验步骤
      • 1.安装 aarch64 交叉编译工具:
      • 2.安装QEMU
      • 3.启动QEMU
        • 创建共享文件目录
        • 安装预编译内核
        • 确定内核位置
        • 运行QEMU模拟器
        • 退出QEMU模拟器
      • 4.编译一个简单的内核模块并在QEMU上运行
    • (四)实验1-4:创建基于Ubuntu的根文件系统
      • 实验目的
      • 实验步骤
      • 1.BusyBox构建根文件系统
      • 2.制定编译工具链
      • 3.配置编译BusyBox
      • 4.创建需要的目录
      • 5.复制动态库文件到 `lib` 目录
    • (五)实验 1-6:编译和运行一个 ARM64 Linux 最小系统
      • 实验目的
      • 实验步骤
      • 1. 编译内核源码
      • 2. 启动 QEMU
      • 3. 编译一个简单的内核模块并在 QEMU 上运行
  • 三.总结

一.实验目的

  1. 掌握 VMware 虚拟机软件的基本使用方法。
  2. 学习在虚拟机中安装 Ubuntu 20.04 Desktop 系统。
  3. 熟悉 Linux 系统的初始配置及快照管理。

二.实验

(一)实验1.1Ubuntu Linux 20.04系统的安装

实验目的

Ubuntu Linux 20.04系统的安装

实验步骤

  1. 创建虚拟机

    • 打开 VMware,选择 创建新的虚拟机典型配置 → 选择 ISO 文件路径。
    • 设置用户名和密码(例如:user/123456)。
    • 指定虚拟机名称和存储位置(如 D:\VM\Ubuntu20.04)。
    • 分配硬件资源:4 核 CPU、4GB 内存、50GB 硬盘。
  2. 安装 Ubuntu 系统

    • 启动虚拟机,进入 Ubuntu 安装界面:

    • 安装完成后重启系统。
      学习链接:https://blog.csdn.net/Z_Date/article/details/137855618?utm_source=miniapp_weixin

(二)实验 1-2:给 Ubuntu Linux 系统更换心脏(内核升级)

实验目的

  1. 通过本实验学会如何给Linux系统更换最新版本的Linux内核。
  2. 如何编译和安装Linux内核

实验步骤

以下是给Ubuntu Linux系统更换最新内核的具体操作步骤:


1. 安装依赖包

sudo apt update
sudo apt install -y libncurses5-dev libssl-dev build-essential openssl flex bison dwarves
# 或者使用以下命令安装所有编译依赖
sudo apt build-dep -y linux-image-generic

在这里插入图片描述
如果遇到这种情况,完整操作如下:
在这里插入图片描述

解决方案
出现 E: 必须在 sources.list 中指定代码源 (deb-src) URI 错误时,说明系统缺少源代码仓库的配置。以下是分步解决方法:


启用 deb-src 仓库
  1. 备份当前的源列表文件(可选):

    sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
    
  2. 编辑 sources.list 文件

    sudo nano /etc/apt/sources.list  # 使用 nano 编辑器,也可替换为 vim 或 gedit
    
  3. 添加或取消注释 deb-src 行

    • 找到已有的 deb 行(例如):
      deb http://archive.ubuntu.com/ubuntu focal main restricted
      
    • 在下方添加对应的 deb-src 行:
      deb-src http://archive.ubuntu.com/ubuntu focal main restricted
      
    • 如果已有 deb-src 行但被注释(以 # 开头),删除 # 取消注释。
  4. 保存并退出编辑器

    • nano:按 Ctrl + O 保存,Ctrl + X 退出。
    • vim:按 :wq 保存并退出。

更新软件包列表
sudo apt update

验证操作

重新执行之前出错的命令( sudo apt build-dep linux-image-generic),检查是否仍然报错。


2. 下载最新内核源码

  • 访问内核官网或清华镜像站查找最新稳定版(例如5.10.15)。
  • 在此之前一定自己要创建文件夹!!!!(否则后面会因为找不到文件使命令失效)
  • 完整操作如下:
cd ~/kernel          # 进入自定义目录(需提前创建:mkdir ~/kernel)
wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.10.15.tar.xz

3. 解压内核源码

tar -xvf linux-5.10.15.tar.xz
cd linux-5.10.15

4. 配置内核选项

  • 方法1(推荐,基于当前系统配置):
  • 建议操作之前先查看自己下载的内核版本
    在这里插入图片描述
    在这里插入图片描述
cp /boot/config-$(uname -r) .config
make olddefconfig
  • 方法2(默认设置):
make defconfig  

如果出错,那基本原因就是前面指定代码源那一步出错了,建议删除之前下载的镜像,重新从建立文件夹操作(在没弄清楚命令行具体含义和作用之前,不建议直接在现有基础上改,会越改越乱!)
在这里插入图片描述

5. 编译内核

  • 使用多线程编译(根据CPU核心数调整-j8,例如8线程):
make -j$(nproc)  # 或者手动指定:make -j8
  • 此过程可能耗时较长(30分钟至数小时),确保系统电源稳定。
    如果遇到这,按下列提示操作:
    在这里插入图片描述

解决方法

安装缺失的依赖库

执行以下命令安装 libelf 开发包:

sudo apt-get install libelf-dev elfutils

(如果系统是CentOS/RHEL,则使用:sudo yum install elfutils-libelf-devel


清理编译环境并重新编译

安装依赖后,重新执行完整编译流程:

make clean                          # 清理旧编译文件
make -j$(nproc)                     # 重新编译内核,确保无报错

6. 安装内核模块

sudo make modules_install

7. 安装内核

sudo make install

8. 更新引导加载程序(GRUB)

sudo update-initramfs -c -k 5.10.15  # 替换为实际内核版本号
sudo update-grub

在这里插入图片描述


9. 重启并验证

sudo reboot
  • 重启后执行以下命令检查内核版本:
uname -r

输出应为新安装的内核版本(如5.10.15)。


在这里插入图片描述

注意事项
这里有一些DeepSeek给出的常见错误,可以借鉴:(建议大家看它给出的解决步骤之前,先看它的思考分析过程,因为有时候我们表述不清,AI生成的内容会有出错)
在这里插入图片描述


通过以上步骤即可完成Ubuntu Linux内核的编译和更换。

(三)实验 1-3:使用 QEMU 虚拟机运行 ARM64 Linux 系统并编译内核模块


实验目的

  1. 学习如何编译 ARM64 版本的 Linux 内核镜像。
  2. 掌握在 QEMU 虚拟机上运行 ARM64 Linux 系统的流程。
  3. 实践编译并加载最简单的内核模块 hello_world,验证模块功能。

实验步骤

1.安装 aarch64 交叉编译工具:

sudo apt-get update
sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install libncurses5-dev build-essential git bison flex libssl-dev

2.安装QEMU

在终端中输入 sudo apt install qemu-system-arm安装

sudo apt install qemu-system-arm

安装完成后,输入 qemu-system-aarch64 --version 来查看 qemu版本

qemu-system-aarch64 --version

在这里插入图片描述

3.启动QEMU

创建共享文件目录
mkdir kmodules
安装预编译内核
sudo apt install linux-image-generic
确定内核位置
ls -l /boot

在这里插入图片描述
在我的 /boot 目录下,内核文件的名称是 vmlinuz-5.4.0-208-generic。那么我可以尝试使用这个文件来启动 QEMU。
注意:
若没有权限,可以调整:

sudo chmod +r /boot/vmlinuz-5.4.0-208-generic

若无挂载路径,可以创建该目录:

mkdir -p /home/xm/kmodules
运行QEMU模拟器

在内核源码目录下执行下面命令(一次性输入)

qemu-system-aarch64 -machine virt -cpu cortex-a57 -machine type=virt -smp 4 -kernel /boot/vmlinuz-5.4.0-208-generic -append "console=ttyAMA0 loglevel=8 root=/dev/vda rw" -nographic -d int,cpu_reset -drive file=/home/xm/disk.img,format=qcow2,if=virtio -fsdev local,id=km_dev,path=/home/xm/kmodules,security_model=none -device virtio-9p-device,fsdev=km_dev,mount_tag=kmount

如果出现错误,说明没有创建虚拟磁盘文件:

qemu-img create -f qcow2 /home/xm/disk.img 10G
退出QEMU模拟器
poweroff

4.编译一个简单的内核模块并在QEMU上运行

在根目录下创建一个文件夹module_test,并编写一个简单的hello.c代码

// 包含内核模块编程所需的头文件
#include <linux/init.h>  // 包含模块初始化和退出函数的宏
#include <linux/module.h>  // 包含模块相关的宏和函数
#include <linux/kernel.h>  // 包含内核打印函数 printk 的头文件// 模块初始化函数
// 当模块被加载到内核时,此函数会被调用
static int __init test_init(void)
{// 在内核日志中打印 "hello world!"printk("hello world!\n");// 返回 0 表示初始化成功return 0;
}    // 模块退出函数
// 当模块从内核中卸载时,此函数会被调用
static void __exit test_exit(void)
{// 在内核日志中打印 "hello exit!"printk("hello exit!\n");
}// 注册模块的初始化函数
// 当模块被加载时,test_init 函数会被调用
module_init(test_init);// 注册模块的退出函数
// 当模块被卸载时,test_exit 函数会被调用
module_exit(test_exit);// 声明模块的许可证
// GPL 是 GNU 通用公共许可证,表示这是一个开源模块
MODULE_LICENSE("GPL");

再编写Makefile文件

# 设置目标架构为 ARM64
export ARCH=arm64# 设置交叉编译工具链前缀为 aarch64-linux-gnu-
export CROSS_COMPILE=aarch64-linux-gnu-# 定义内核源码目录
KERNEL_DIR ?= /home/xm/linux-6.13.5# 定义要编译的内核模块目标文件
# obj-m 表示将 hello.c 编译为内核模块 hello.ko
obj-m := hello.o# 默认目标:编译内核模块
modules:# 调用内核源码目录的 Makefile,编译当前目录下的模块$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules# 清理目标:删除编译生成的文件
clean:# 调用内核源码目录的 Makefile,清理当前目录下的生成文件$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean# 安装目标:将编译好的内核模块复制到指定目录
install:# 将当前目录下所有的 .ko 文件复制到内核源码目录的 kmodules 子目录中cp *.ko $(KERNEL_DIR)/kmodules

编译module,拷贝到共享目录

make modules
make install 

启动QEMU

qemu-system-aarch64 
-machine virt 
-cpu cortex-a57 
-machine type=virt  
-m 1024 
-smp 4 
-kernel arch/arm64/boot/Image 
--append "rdinit=/linuxrc root=/dev/vda rw console=ttyAMA0 loglevel=8"  
-nographic  
--fsdev local,id=kmod_dev,path=$PWD/kmodules,security_model=none  
-device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mountls /mnt

(四)实验1-4:创建基于Ubuntu的根文件系统

实验目的

创建关于Ubuntu linux的根文件系统

实验步骤

1.BusyBox构建根文件系统

1)下载BusyBox源码
BusyBox是一个集成了大量的Linux命令(如ls、mv、ifconfig 等命令)和工具的软件。借助BusyBox,进行配置和编译,就可以方便的构建一个嵌入Linux平台所需要的根文件系统。

可在BusyBox官网 https://busybox.net/ 下载源码。

将压缩包拖拽到Ubuntu虚拟机中的桌面或文件夹中,并解压

tar -vxjf busybox-1.37.0.tar.bz2

2.制定编译工具链

cd busybox-1.37.0
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
//检测是否配置成功
echo $ ARCH
echo $CROSS_COMPILE

3.配置编译BusyBox

修改源码以支持中文显示(可选):
打开文件 /libbb/printable_string.c,注释掉 printable_string 函数中 if (c >= 0x7f) break; 的代码行。
打开文件 /libbb/unicode.c,修改 unicode_conv_to_printable2 函数中的相关代码。

在这里插入图片描述
在这里插入图片描述
安装交叉编译工具链
你需要安装适用于 ARM64 架构的交叉编译工具链。在 Ubuntu 或 Debian 系统上,可以运行以下命令:

sudo apt update
sudo apt install gcc-aarch64-linux-gnu

配置busybox:有以下几种配置选项,一般使用默认配置即可


make defconfig  	#使用默认配置
make menuconfig		#打开图形化配置界面

如果出现这个错误,把终端窗口放大就行。
在这里插入图片描述

** 设置Settings -> Build static binary (no shared libs)
设置Settings -> Support Unicode,使能busybox的unicode编码以支持中文**

编译busybox:配置好busybox以后就可以编译了,输入如下命令


sudo apt install make
make
make install CONFIG_PREFIX=/home/xm/linux/rootfs
#CONFIG_PREFIX指定编译结果的存放目录

如果出现这样的错误,操作如下:(我们下载的很多压缩包都是有bug或者和我们的操作版本不兼容的,出现这种本身自带的文件编译错误,可以直接找到位置更改)
在这里插入图片描述

在这里插入图片描述

编译完成以后,busybox的所有工具和文件就会被安装到rootfs目录中,如下图;rootfs目录下有bin、sbin和usr三个目录,以及linuxrc文件。
在这里插入图片描述

在这里插入图片描述

4.创建需要的目录

cd  ~/linux/rootfs
mkdir dev etc lib sys proc tmp var home root mnt

创建 profile 文件并添加内容

cd /etcsudo nano /etc/profile

将以下内容复制粘贴到 profile 文件中:

#!/bin/sh
export HOSTNAME=user
export USER=root
export HOME=/home
export PS1="[$USER@$HOSTNAME \W]\# "
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH

保存并退出:

  • 按下 Ctrl + O 保存文件。
  • 按下 Enter 确认文件名。
  • 按下 Ctrl + X 退出 nano
    在这里插入图片描述

创建 inittab 文件并添加内容

cd /etc
sudo nano inittab

将以下内容复制粘贴到 inittab 文件中:

::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r

保存并退出:

  • 按下 Ctrl + O 保存文件。
  • 按下 Enter 确认文件名。
  • 按下 Ctrl + X 退出 nano

创建 fstab 文件并添加内容

cd /etc
sudo nano fstab

将以下内容复制粘贴到 fstab 文件中:

#device  mount-point    type     options   dump   fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
kmod_mount /mnt 9p trans=virtio 0 0

保存并退出:

  • 按下 Ctrl + O 保存文件。
  • 按下 Enter 确认文件名。
  • 按下 Ctrl + X 退出 nano

创建 init.d 目录并在其中创建 rcS 文件

cd /etc
mkdir init.d
cd init.d
sudo nano rcS

将以下内容复制粘贴到 rcS 文件中:

cd init.dmkdir -p /sys
mkdir -p /tmp
mkdir -p /proc
mkdir -p /mnt
/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

保存并退出:

  • 按下 Ctrl + O 保存文件。
  • 按下 Enter 确认文件名。
  • 按下 Ctrl + X 退出 nano

添加权限

sudo chmod 777 rcS

检查 etc 目录结构

sudo apt update
sudo apt install tree
sudo tree /etc

在这里插入图片描述

创建 dev 目录下的必要文件

cd /dev
sudo mknod console c 5 1

检查是否创建成功:

ls -l /dev/console

在这里插入图片描述

5.复制动态库文件到 lib 目录

cd /lib
sudo cp /usr/aarch64-linux-gnu/lib/*.so* -a .

检查当前目录是否包含了 .so 文件:

ls -l *.so*

在这里插入图片描述

(五)实验 1-6:编译和运行一个 ARM64 Linux 最小系统

实验目的

本实验旨在指导你如何编译一个 ARM64 架构的 Linux 内核,并使用 QEMU 虚拟机运行该内核,同时加载一个简单的内核模块进行测试。

实验步骤

可以继承实验1-4的快照

1. 编译内核源码

下载和解压源码

  1. 访问 Linux 内核官网(https://www.kernel.org/),找到你需要的内核版本(如 linux-6.13.6.tar.xz)。
  2. 使用 wget 命令下载源码:
    wget https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.13.6.tar.xz
    
  3. 解压源码:
    tar xvf linux-6.13.6.tar.xz
    

指定编译工具

  1. 设置架构为 arm64,并指定交叉编译工具链前缀为 aarch64-linux-gnu-:
    export ARCH=arm64
    export CROSS_COMPILE=aarch64-linux-gnu-
    

将根文件系统放到源码根目录

  1. 使用 sudo cp 命令将构建好的根文件系统复制到内核源码根目录下:
    sudo cp ~/linux/rootfs rootfs_arm64 -a
    

配置生成.config

  1. 运行 make defconfig 生成默认配置文件:
    make defconfig
    
  2. 运行 make menuconfig 打开图形化配置界面,进行以下配置:
    • 添加 hotplug 支持:
      Device Drivers-> Generic Driver Options-> Support for uevent helper(/sbin/hotplug) path to uevent helper
      
    • 添加 initramfs 支持:
      General setup --->[*]Initial RAM filesystem and RAM disk(initramfs/initrd) support(_install_arm64) Initramfs souce file(s)
      
    • 配置 Virtual address space:
      Kernel Features  --->Page size(4KB)  --->Virtual address space size(48-bit)--->
      

编译

  1. 运行 make all -j8 进行编译,-j8 表示使用 8 个线程进行并行编译:
    make all -j8
    

2. 启动 QEMU

  1. 运行以下命令启动 QEMU 虚拟机:
    qemu-system-aarch64 -machine virt -cpu cortex-a57 -machine type=virt -m 1024 -smp 4 -kernel arch/arm64/boot/Image --append "rdinit=/linuxrc root=/dev/vda rw console=ttyAMA0 loglevel=8" -nographic --fsdev local,id=kmod_dev,path=$PWD/kmodules,security_model=none -device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mount
    

3. 编译一个简单的内核模块并在 QEMU 上运行

创建 module_test 文件夹并编写 hello.cMakefile 文件

  1. 创建 module_test 文件夹:
    mkdir module_test
    cd module_test
    
  2. 编写 hello.c 文件,内容如下:
    // 包含内核模块编程所需的头文件
    #include <linux/init.h>  // 包含模块初始化和退出函数的宏
    #include <linux/module.h>  // 包含模块相关的宏和函数
    #include <linux/kernel.h>  // 包含内核打印函数 printk 的头文件// 模块初始化函数
    // 当模块被加载到内核时,此函数会被调用
    static int __init test_init(void)
    {// 在内核日志中打印 "hello world!"printk("hello world!\n");// 返回 0 表示初始化成功return 0;
    }    // 模块退出函数
    // 当模块从内核中卸载时,此函数会被调用
    static void __exit test_exit(void)
    {// 在内核日志中打印 "hello exit!"printk("hello exit!\n");
    }// 注册模块的初始化函数
    // 当模块被加载时,test_init 函数会被调用
    module_init(test_init);// 注册模块的退出函数
    // 当模块被卸载时,test_exit 函数会被调用
    module_exit(test_exit);// 声明模块的许可证
    // GPL 是 GNU 通用公共许可证,表示这是一个开源模块
    MODULE_LICENSE("GPL");
    
  3. 编写 Makefile 文件,内容如下:
    # 设置目标架构为 ARM64
    export ARCH=arm64# 设置交叉编译工具链前缀为 aarch64-linux-gnu-
    export CROSS_COMPILE=aarch64-linux-gnu-# 定义内核源码目录
    KERNEL_DIR ?= /home/xm/linux-6.13.6# 定义要编译的内核模块目标文件
    # obj-m 表示将 hello.c 编译为内核模块 hello.ko
    obj-m := hello.o# 默认目标:编译内核模块
    modules:# 调用内核源码目录的 Makefile,编译当前目录下的模块$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules# 清理目标:删除编译生成的文件
    clean:# 调用内核源码目录的 Makefile,清理当前目录下的生成文件$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean# 安装目标:将编译好的内核模块复制到指定目录
    install:# 将当前目录下所有的 .ko 文件复制到内核源码目录的 kmodules 子目录中cp *.ko $(KERNEL_DIR)/kmodules
    

编译模块并拷贝到共享目录

  1. 运行 make modules 编译模块:
    make modules
    
  2. 运行 make install 将编译好的模块拷贝到内核源码目录的 kmodules 子目录中:
    make install
    

启动 QEMU 并在 QEMU 中执行模块的插入与卸载

  1. 启动 QEMU 虚拟机(同上一步骤)。
  2. 在 QEMU 中执行以下命令插入模块:
    insmod hello.ko
    
  3. 查看内核日志,确认模块加载成功:
    dmesg | tail
    
  4. 卸载模块:
    rmmod hello.ko
    
  5. 再次查看内核日志,确认模块卸载成功:
    dmesg | tail
    

三.总结

本次实验学习“奔跑吧Linux内核”课件及资料,参照实验指导书中的第1章中的实验1-1到1-6的内容,进行学习理解和模仿实践。在实验过程中经历了很多次失败,感悟很多,推荐大家利用deepseek,kimi等大模型来处理报错,在这个过程之中,除了copy大模型给出的解决方法,也要重点学习一下它的思路(如下图),否则下一次还是可能会经历同样的错误。
在这里插入图片描述


http://www.mrgr.cn/news/93759.html

相关文章:

  • Java线程池深度解析,从源码到面试热点
  • Codecraft-17 and Codeforces Round 391 E. Bash Plays with Functions 积性函数
  • 3.9【Q】csd
  • 线上接口tp99突然升高如何排查?
  • C++算法——差分
  • [通讯协议]485通信
  • 每日一题——乘积最大子数组
  • <建模软件安装教程1>Blender4.2系列
  • 2024华为OD机试真题-找最小数(C++)-E卷B卷-100分
  • 13.C语言指针的易错点
  • html-列表标签和表单标签
  • 【从零开始学习计算机科学】计算机组成原理(四)指令系统
  • Python 实现图片提取文字
  • 发现U9查询设计上的一个逻辑
  • hadoop第3课(hdfs shell常用命令)
  • 数据治理的核心路线
  • 在Vue中 使用 Web Worker
  • ESP8266 WiFi 收发器入门(第 1 部分)
  • 强化学习和最优控制 - 知识图谱
  • Spring AI学习