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

【github】docker realtime

Linux和Docker实时指南,适用于Ubuntu实时内核和PREEMPT_RT  ReadMe.md

作者:Tobit Flatscher(2021 - 2024)

概述

本指南解释了如何在Linux操作系统内开发/部署运行实时代码的Docker容器。因此,它会带你了解:

  • 实时系统的基础知识以及不同实时Linux方法的概述
  • 实时系统的设置,特别是激活Ubuntu实时内核或替代安装PREEMPT_RT,提供简单的脚本用于自动重新编译内核
  • 实时系统的可能优化(Real-time Optimizations),以最小化延迟并通过cyclictest基准测试实时性能
  • 让Nvidia驱动程序与PREEMPT_RT一起工作
  • 实时容器在带有PREEMPT_RT补丁的主机系统上的所需设置

本指南不适用于Windows和Macintosh操作系统,因为它依赖于带有PREEMPT_RT补丁的主机系统,而Windows和Macintosh上的Docker实际上是在中间运行轻量级虚拟机  runs with a light-weight virtual machine in the middle。

有关使用Docker和Docker-Compose进行开发的介绍,如何在Visual Studio Code中设置它,以及如何将其用于图形用户界面和ROS工作区的机器人应用,请查看我的另一个仓库docker-for-robotics。

用例

这可以用于多种不同的应用,特别是:

  • 从Linux系统控制实时机器人硬件,例如通过EtherCAT(使用SOEM或IgH等EtherCAT主站)或为其他机器人组件(如Franka Emika Panda协作机器人臂)设置容器
  • 通过将桌面  turning your desktop 或单板计算机变成SoftPLC(例如使用CodeSYS Control或LinuxCNC)进行数控
  • 实时音频开发中的编程,例如使用Juce

但也可能适用于开发任何其他形式的实时代码的软件开发人员。

1. Linux用于实时应用

将普通Linux系统转变为实时操作系统有多种方法。如在doc/RealTimeLinux.md中概述的,PREEMPT_RT可能是最具有未来保障性的可能性,因为它即将被纳入Linux主线。自Ubuntu 22.04起,它也可以通过Ubuntu Pro供Ubuntu用户开箱即用。

由于Docker共享主机内核,只要主机内核是实时的,允许容器运行实时代码就只是启动它时使用正确权限的问题。使用Ubuntu实时内核/PREEMPT_RT设置实时Docker相当直接。你所需要的只是:

  • 带有实时内核的Ubuntu操作系统或替代的带有PREEMPT_RT补丁的Linux主机操作系统
  • 一个任意的Docker容器,以正确的选项启动,以便它可以从容器内部设置实时优先级以及减少网络延迟的选项

1.1 安装实时内核

应用PREEMPT_RT补丁有几种方法。自2023年2月和Ubuntu 22.04起,通过Ubuntu Pro可以使用几个终端命令安装Ubuntu实时内核。对于个人使用,你可以免费注册多达五台机器,而商业客户则需要注册订阅。我推荐以这种方式安装补丁,因为这是最简单和最可靠的方式。doc/PreemptRt.md指南会带你了解注册和安装程序。

此外,我还概述了在doc/PreemptRt.md中通过从源代码编译或从现有的Debian包安装的安装程序。也可以使用提供的脚本src/install_debian_preemptrt和src/compile_kernel_preemptrt(见下图)执行相同的程序。

1.2 设置实时权限

在对系统打补丁并重启后,启动到新安装的内核中,你应该已经可以启动一个实时Docker了。如果你不打算在Docker内部使用root作为用户,那么你还需要在主机计算机上为一个属于具有实时权限的组的用户赋予一个名字。如何做到这一点可以在doc/PreemptRt.md中找到。

1.3 启动Docker

在成功安装PREEMPT_RT后,执行带有以下选项的Docker就足够了:

然后,Docker内部的任何进程都可以设置实时优先级 rtprio(例如,通过在C/C++代码内部调用::pthread_setschedparam或使用命令行中的chrt)。一般来说,建议不要将rtprio设置为99(有关更多信息,请参阅拉取请求#3)。

2. 示例

这个GitHub仓库带有一个简单的示例,可以用来尝试。在Docker容器内部运行cyclictest以评估系统的实时性能。你可以将其结果与在本地系统上运行的结果进行比较。从下面的图表中可以看出,两者之间几乎没有区别:

对于启动 cyclictest,打开Docker,输入

$ docker-compose -f docker/docker-compose.yml up

然后浏览 benchmark/ 文件夹并运行命令

$ ./mklatencyplot.bash

这应该会通过测量线程的预期唤醒时间与实际唤醒时间之间的差异来创建一个延迟直方图(生成一个类似上面的plot.png图像)。这会测量由硬件、固件和操作系统引起的任何形式的延迟。有关此测试的更多信息,请参阅OSADL。

实时 Linux  /doc/RealTimeLinux.md

作者:Tobit Flatscher(2021 年 8 月 - 2023 年 3 月)

1. 引言

在标准操作系统中,任务调度在一定程度上是非确定性的,这意味着无法给出一个可数学证明的执行时间上限。这种特性对于大多数应用程序来说是可接受的,因为它可以提高吞吐量。然而,对于实时系统,我们需要能够提供这样的上限,即任务的执行时间不会超过某个确定的值。实时系统的目标不是尽可能快地执行任务,而是以一致且确定性的方式执行任务。重要的是最坏情况下的延迟,而不是平均延迟

使(Linux)操作系统具备实时能力有多种方法,这些方法将在下一节中讨论。关于实时操作系统的介绍可以参考这里和这里。此外,还有一篇 NASA 的会议论文,讨论了他们在使用 Linux 作为实时操作系统时遇到的挑战。如果对 Linux 内核和调度器的工作原理感兴趣,可以阅读这篇由比作者更了解该主题的人撰写的指南。该主题还涉及多核架构上的负载均衡。

2. 实时 Linux:双内核与单内核方法

在讨论实时内核时,可以区分单内核方法(如 PREEMPT_RT)和双内核方法(如 Xenomai)。你可以将支持实时的 Docker 与这些方法结合使用来创建实时系统,但方法有所不同。显然,这并不依赖于 Docker 本身,而是依赖于底层宿主系统,这意味着你仍需正确配置宿主系统,可能需要重新编译其内核

2.1 双内核实时 Linux

双内核方法比单内核方法早出现几年。在这种情况下,一个独立的实时微内核与传统的 Linux 内核并行运行,在硬件和 Linux 内核之间添加了一个处理实时需求的层。实时代码的优先级高于用户空间,只有在没有实时代码执行时,用户空间才被允许运行。

常见的双内核方法有两种:

RTAI(实时应用接口)

由米兰理工大学开发。需要在内核空间编程,而不是用户空间,因此不能使用标准 C 库,而是必须使用特殊库,这些库不提供标准库的全部功能。与用户空间的交互通过特殊接口处理,这使得编程变得更加困难。对于新硬件,还需要开发新的微内核驱动程序,导致代码总是稍微落后于主流。对于商业代码,许可也可能是一个问题,因为内核模块通常在开源的 GNU 公共许可证(GPL)下授权。

Xenomai 实时操作系统

尝试改善内核和用户空间之间的分离。程序员在用户空间工作,然后添加抽象层(称为“皮肤”),这些抽象层模拟不同的 API(例如实现 Posix 线程的一个子集),在编译时需要链接这些抽象层。Xenomai 进程像任何正常的 Linux 应用程序一样启动。在初始初始化阶段之后,它们可以声明自己为实时进程。之后,将失去对 Linux 服务和驱动程序的访问权限,必须使用特定于 Xenomai 的设备驱动程序。因此,应用程序需要分为实时和非实时部分,并且需要有可用的实时设备驱动程序,或者可能需要开发。

2.2 单内核实时 Linux

尽管双内核方法具有出色的实时性能,但其主要缺点是固有的复杂性。正如德国嵌入式开发公司 Linutronix 的 Jan Altenberg 所说,他是 PREEMPT_RT 的主要贡献者之一:

“问题是,有人需要维护微内核,并在新硬件上支持它。这是一个巨大的努力,而且开发社区并不大。此外,因为 Linux 并不是直接在硬件上运行,你需要一个硬件抽象层(HAL)。有了两样东西需要维护,你通常会落后于主线 Linux 的开发。”

这种缺点促使了不同的开发尝试,通过修改 Linux 内核本身中的调度来修补现有的 Linux 内核,即所谓的单内核系统。内核本身被调整为具备实时能力。

默认情况下,Linux 内核可以编译为不同级别的可抢占性(例如,参见 Reghenzani 等人的论文 - “实时 Linux 内核:PREEMPT_RT 调查”):

  1. PREEMPT_NONE:没有强制抢占的方式。
  2. PREEMPT_VOLUNTARY:在某些位置可以进行抢占,以减少延迟。
  3. PREEMPT:内核的任何部分(除了自旋锁和其他关键部分)都可以发生抢占。

这些可以与控制组(简称 cgroups)的功能结合,通过在内核编译期间设置 `CONFIG_RT_GROUP_SCHED=y`,为某个(用户定义的)组的进程保留一定的 CPU 时间。这似乎与高延迟峰值有关,可以通过 `cyclictest` 工具观察控制组的情况。

`PREEMPT_RT` 是从 `PREEMPT` 开发出来的一组补丁,旨在使内核在关键部分(`PREEMPT_RT_FULL`)也能完全抢占。有关详细解释,请参考这篇 [Ubuntu 深入指南](https://wiki.ubuntu.com/RealTime)。为此,例如自旋锁大多被互斥锁取代。这意味着无需进行内核空间编程,可以使用标准的 C 和 Posix 线程库。2021 年中,Linux 主要开发者 Linus Torvalds 将 220 个未完成补丁中的 70 个合并到 Linux 主线中。不久的将来,`PREEMPT_RT` 应该可以默认提供给 Linux 社区,而无需对系统进行补丁,确保补丁的维护。有关更详细的概述,请查看这个 [演讲](https://www.youtube.com/watch?v=xyz) 以及 Ubuntu 入门系列(参见 [此网络研讨会](https://www.youtube.com/watch?v=abc) 和 [这篇博客文章](https://blog.example.com))。一个潜在的问题是,内核驱动程序的开发不一定考虑了实时约束。

`PREEMPT_RT` 在机器人领域被广泛使用。

SpaceX 在其机载计算机上运行它。

机器人制造商如 Franka Emika、Universal Robots 和丰田将其用于机器人的控制计算机。特别是像 MIT Mini Cheetah 这样的四足机器人通常使用它。

国家仪器(NI)在其一些控制器上使用它。

LinuxCNC(最初由 NIST 开发)使用它来实现数控机床的数值控制。

它通常与 nVidia Jetson 平台一起使用。

SCHED_OTHER:完全公平调度器,不具备实时能力,通常是默认的调度策略。

SCHED_DEADLINE:最早截止时间优先调度。

SCHED_FIFO:先进先出调度,不进行时间片轮转。这通常用于实时应用程序。

SCHED_RR:SCHED_FIFO 的增强版,带有时间片轮转。

实时优化指南  doc/RealTimeOptimizations.md

作者:Tobit Flatscher(2022 - 2024)


1. 引言

本文将介绍在搭建实时系统时需要考虑的关键因素,以及一些可以显著提升实时性能的优化方法。此外,还会介绍用于评估系统实时性能的基准测试技术。本指南主要基于 Red Hat 的优化指南,重点关注其中的特定方面。更多详细信息可以参考 Ubuntu 实时内核调整指南。


2. 选择硬件

大多数为节能优化的计算机(例如笔记本电脑)的延迟通常比台式机高出一个或两个数量级。因此,通常不建议使用笔记本电脑进行实时测试(无论是否使用 Docker)。一些单板计算机(如树莓派)表现不错,但仍无法与台式机竞争。开源自动化开发实验室(OSADL)会在其网站上进行长期测试,你可以查看这些测试结果,以便将你的系统性能与其他系统进行比较。

3. 内核参数

通过在重新编译内核时更改内核参数,可以提高实时性能。在 OSADL 长期测试农场网站上,您可以查看和下载特定系统的内核配置(例如,这里)。减少延迟的重要设置包括禁用所有无关的调试功能。您可以使用 $ cat /boot/config-$(uname -r) 显示当前配置的参数。

操作系统使用系统时钟时间生成中断请求,以调度可能导致具有相同优先级的任务进行时间片轮转调度的事件。可以通过调整时钟滴答的分辨率来提高定时事件的准确性。

对于 Linux,目前支持的最高时钟频率是 1000Hz,可以通过内核参数设置:

```plaintext
CONFIG_HZ_1000=y # 需要注释掉其他选项,如 CONFIG_HZ=250
CONFIG_HZ=1000
```

为了提高性能,可以在空闲时停止周期性滴答,并在只有一个可运行任务的 CPU 上省略调度滴答,具体描述见这里和这里:

```plaintext
CONFIG_NO_HZ_IDLE=y
CONFIG_NO_HZ_FULL=y
```

后者通过调度时钟中断的持续时间来改善最坏情况的延迟。必须显式标记应该应用的 CPU,例如 `nohz_full=6-8`,但无法将所有 CPU 标记为自适应滴答 CPU!通过使用内核参数 `CONFIG_NO_HZ_FULL_ALL=y` 可以为除引导 CPU 外的所有 CPU 激活此模式。

对于无锁编程,Linux 有一个读-复制更新 (RCU) 同步机制,避免了当多个线程同时读取和更新通过指针链接并属于共享数据结构的元素时出现不一致。这种机制有时会将回调排队到将来在 CPU 上执行。可以通过编译内核时使用以下参数来排除某些 CPU 执行 RCU 回调:

```plaintext
CONFIG_RCU_NOCB_CPU=y
```

然后指定应该应用的 CPU 列表,例如 `rcu_nocbs=6-8`。


4. CPU 频率缩放和睡眠状态

在操作过程中,操作系统可能会调整 CPU 频率以提高性能或节省能源。负责这一过程的是:

  • - 调频调控器 scaling governor:根据负载等计算 CPU 频率的算法。
  • - 调频驱动程序  scaling driver:与 CPU 交互以确保达到所需频率。

更多详细信息可以参考 Arch Linux。

虽然这些设置可以帮助节省能源,但通常会增加延迟,因此在实时系统中应禁用它们

在 Intel CPU(以及类似的 AMD 处理器)上,驱动程序提供了两种减少功耗的可能性(参见此处和此处以及 Linux 内核文档):

  • - P 状态:处理器可以在较低电压和/或频率级别下运行,以减少功耗。
  • - C 状态:空闲状态,子系统被关闭。

这两种状态都是编号的,0 对应于最大性能的操作状态,较高的级别对应于省电(可能增加延迟)的模式。

根据我的经验,C 状态对延迟的影响最大,而将 P 状态更改为性能模式的影响很小。如果有影响的话,通常可以在 BIOS 中更改这些设置,但 Linux 操作系统通常会忽略相应的 BIOS 设置。因此,重要的是根据下一节的内容来配置操作系统

此外,还有其他动态频率调节功能,如Turbo-Boost,允许在需要时临时提高频率。其可行程度取决于活动核心的数量、电流和功耗以及 CPU 温度。不同处理器的数值可以在WikiChip 上找到。此功能可以在操作系统和 BIOS 中停用,并且通常在需要全天候满负荷运行的服务器上停用。为了获得最佳的实时性能,您应该关闭超线程 hyperthreading 

4.1 C 状态

为了降低延迟,禁用处理器的空闲状态是至关重要的(参见下文的实际基准测试部分)。可以通过以下几种方式实现:

  • 可以使用内核参数,如 intel_idle.max_cstate=0 禁用 Intel 驱动程序,或 processor.max_cstate=0 idle=poll 使处理器始终保持在 C0 状态。这些方法是永久性的,只能在重启后更改。

  • 可以通过 /dev/cpu_dma_latency 文件动态控制。打开该文件并写入最大允许延迟。使用以下命令可以确定不同状态的延迟值:

    cd /sys/devices/system/cpu/cpu0/cpuidle
    for state in state{0..4}; do echo c-$state `cat $state/name` `cat $state/latency`; done
    

    如果设置为 0,则处理器将保持在 C0 状态。重要的是在关闭该文件时保持打开状态,否则处理器将再次进入空闲状态。/dev/cpu_dma_latency 文件的值以十六进制表示,可以通过以下命令读取:

    HEX_LATENCY=$(sudo xxd -p /dev/cpu_dma_latency | tac -rs .. | echo "$(tr -d '\n')") && echo $((0x${HEX_LATENCY}))
    
  • 还可以通过写入 /sys/devices/system/cpu/cpu${i}/cpuidle/${cstate}/disable 为每个核心独立动态设置 C 状态,其中每个 CPU 核心的 ${i} 在 $(nproc) 中,每个 ${cstate} 在 ls /sys/devices/system/cpu/cpu0/cpuidle | grep state 中。

我个人更喜欢第一种方法,因为在处理实时相关代码时,我不想每次都调用脚本。相反,我将启动一个专用内核,并激活相应的内核参数。我将通过 Grub-Customizer 生成另一个启动项(见下图),称为 Ubuntu 22.04 real-time,在此我基本克隆了 X.XX.xx-rtXX 配置,并添加了 processor.max_cstate=0 intel_idle.max_cstate=0 idle=poll(见截图右侧倒数第二行)。

After boot you can use $ cat /proc/cmdline to double check the settings.

Additionally i7z can be very useful for monitoring the C-states. After installing it with

$ sudo apt-get install i7z

continue to run it with

$ sudo i7z

It will show you the percentage of time that each individual core is spending in a particular C-state:


4.2 Cyclictest

Cyclitest 是评估由硬件、固件和操作系统整体引起的实时系统延迟的重要测试。它反复测量线程计划唤醒时间与实际唤醒时间之间的差异(更多详情见此处)。对于实时应用程序来说,最坏情况下的延迟比平均延迟更为重要,因为任何违反截止时间的情况都可能是致命的。理想情况下,我们希望将其与压力测试(如 stress-ng -c $(nproc))结合使用,并运行较长时间(数小时、数天甚至数周)。

关闭 C 状态的效果在两个系统上进行了评估:Lenovo ThinkStation P500 台式机(配备 Intel Xeon E5-2680 v3 @ 2.50GHz 十二核 CPU 和 32GB ECC-DDR4-RAM)和 Lenovo ThinkPad T14 Gen1 笔记本(配备 Intel Core i5-10210U 四核 CPU 和 16GB DDR4-RAM)。在这两种情况下,使用以下参数执行 Cyclitest:

cyclictest --latency=750000 -D5m -m -Sp90 -i200 -h400 -q

确保使用 -p 标志设置适当的优先级,例如 -p90(以及可能的 --policy,例如 --policy=fifo),否则将假定较低的默认优先级,不能反映系统的实际性能。参数 latency 在这里是必要的,因为默认情况下,Cyclitest 会写入 /dev/cpu_dma_latency,有效地禁用了 C 状态。这样可以对系统的潜在能力有一个现实的概述,但不能反映其他不使用相同优化程序的行为。

测试持续时间为五分钟,对于真正的实时系统来说,这相当短。通常,系统延迟会在数天或数周内进行调查(通常还会对系统进行压力测试),因为某些驱动程序相关的问题可能只会在长时间使用后显现出来。不过,以下测试应该足以说明延迟的差异。

这两个系统都使用 Ubuntu 22.04 5.15.86-rt56 内核自编译版本。BIOS 中禁用了 Intel Turbo-boost。所有 Docker 测试都直接通过 Docker-Compose 运行(没有通过 Visual Studio Code 间接运行)。为了获得最佳性能,T14 笔记本在整个测试期间都插电。


4.3 Hwlatdetect

Hwlatdetect 工具可以检测系统管理中断(SMI)。它通过占用所有处理器资源,检测 CPU 时间戳计数器中的间隔来判断 SMI 的发生。运行方法如下:

$ sudo hwlatdetect --duration=60s


5. 隔离 CPU

通常,为特定的实时任务保留一个 CPU 核心,如 Grub 和 Linux 中所述(参见这里和这里). 如果在 BIOS 中激活超线程(不推荐),则相应的虚拟核心也需要隔离。第二个虚拟核心的索引遵循物理核心。

你可以通过添加以下选项来隔离 CPU(数字对应于我们想要隔离的 CPU 列表,从 0 开始):

GRUB_CMDLINE_LINUX_DEFAULT="isolcpus=0-3,8-11"

将其添加到 /etc/default/grub,然后通过 sudo update-grub 更新 grub,并重启。或者,你可以通过 Grub 自定义器将 isolcpus=0-1 选项添加到内核参数中。然后,可以通过运行 cat /sys/devices/system/cpu/isolated 和 cat /proc/cmdline 检查隔离是否正常工作。验证的一个好方法是运行 stress-ng -c $(nproc),并使用 htop 监控没有压力测试进程生成在隔离核心上。如果隔离正常,所有核心应该完全使用,而隔离的应该为空。

此外,还可以隔离相应的硬件中断(IRQ)(在专用 CPU 上),禁用 irqbalance 守护程序,并将进程绑定到特定 CPU,如这里所述。这对于包含网络和 EtherCAT 通信的应用程序尤为重要。

为此,使用以下命令禁用 irqbalance:

systemctl disable irqbalance
systemctl stop irqbalance

然后用以下命令检查中断:

cat /proc/interrupts

记录相关行开头的 IRQ 编号,然后可以将相应 IRQ 的中断放置到你选择的核心上:

echo <cpu_core> > /proc/irq/<irq_id>/smp_affinity

最后,我们还可以通过添加以下内容激活完整的 dynticks 内部结构,并禁止 RCU 在我们的隔离核心上运行(如这里所述):

nohz_full=1-3,5,7 rcu_nocbs=1-3,5,7

where the number corresponds to the CPUs we would like to isolate.

Putting this together with the previous Grub configuration options this leaves us with:

GRUB_CMDLINE_LINUX_DEFAULT=“isolcpus=1-3,5,7 nohz_full=1-3,5,7 rcu_nocbs=1-3,5,7”

Nvidia 驱动程序与实时内核 doc/NvidiaDriver.md

作者:Tobit Flatscher(2023 年 7 月)

0. 引言

为了让 Nvidia 驱动程序能够与 PREEMPT_RT 内核配合使用,在安装驱动程序时必须设置环境变量 IGNORE_PREEMPT_RT_PRESENCE。

1. 安装

在开始安装之前,请在终端中输入以下命令:

export IGNORE_PREEMPT_RT_PRESENCE=1

然后(在设置了环境变量的同一个终端中),可以通过 Debian 软件包安装 Nvidia 驱动程序:

sudo -E apt-get install nvidia-driver-XXX

其中 XXX 是驱动程序版本,例如 535。

或者,可以按照官方文档的说明安装 .run 文件,或者从源代码编译最新的 Nvidia 开源 GPU 内核模块。要查看哪些驱动程序适用于您的显卡(以及哪些是推荐的),可以检查“软件与更新”/“附加驱动程序”菜单。

确保在使用 sudo 时,通过 -E 选项或直接在命令前设置环境变量,以确保环境变量生效。例如:

sudo IGNORE_PREEMPT_RT_PRESENCE=1 apt-get install some-package

关于如何在 Docker 中使用 Nvidia 驱动程序的更多信息,请参考相关指南。

2. 故障排除

如果在更新后无法启动到特定内核,请检查哪些内核已成功安装了 Nvidia 内核模块。可以使用以下命令:

dkms status

例如,输出可能如下:

nvidia/535.54.03, 5.15.86-rt56, x86_64: installed nvidia/535.54.03, 5.19.0-45-generic, x86_64: installed

然后,检查 /boot 目录下的内核文件:

find /boot/vmlinuz*

输出可能如下:

/boot/vmlinuz /boot/vmlinuz-5.15.0-1040-realtime /boot/vmlinuz-5.15.107-rt62 /boot/vmlinuz-5.15.86-rt56 /boot/vmlinuz-5.19.0-41-generic /boot/vmlinuz-5.19.0-45-generic /boot/vmlinuz.old

从上面的输出可以看出,虽然安装了多个内核,但 Nvidia 内核模块仅成功安装在部分内核上。例如,5.15.0-1040-realtime、5.15.107-rt62 和 5.19.0-41-generic 内核未成功安装 Nvidia 内核模块。

可以手动安装这些内核模块。例如,安装 5.19.0-41-generic 内核的 Nvidia 模块:

sudo dkms install nvidia/535.54.03 -k 5.19.0-41-generic

对于实时内核,需要先设置环境变量:

export IGNORE_PREEMPT_RT_PRESENCE=1 sudo -E dkms install nvidia/535.54.03 -k 5.15.0-1040-realtime

http://www.kler.cn/a/543081.html

相关文章:

  • 每日一题——括号生成
  • 131,【2】 攻防世界 catcat-new
  • 七、I2C通信读取LM75B温度
  • 利用邮件合并将Excel的信息转为Word(单个测试用例转Word)
  • Oracle常用导元数据方法
  • 分层耦合 - IOC详解
  • 探索RDMA技术:从基础到实践
  • 【Qt】定期清理程序
  • AI写代码工具赋能前端工程师,加速职业晋升
  • 二叉树详解
  • 对前端的技术进行分层
  • 关于FC设备Map 系统的一些需求思考
  • OpenBayes 教程上新 | 告别服务器繁忙,DeepSeek 一键部署教程上线!
  • 解锁电商数据宝藏:淘宝商品详情API实战指南
  • 微信小程序的制作
  • 学习docker!!!
  • Qt手撸控件不显示问题
  • kafka动态监听主题
  • Conda 虚拟环境与 venv、virtualenv、pipenv 的对比
  • 基于 DeepSeek 的创新点及其在学术研究与论文发表中的应用
  • uniapp国际化不立即生效(带解决方案)
  • ffmpeg学习:ubuntu下编译Android版ffmpeg-kit
  • 元宵节快乐
  • 力扣刷题(数组篇)
  • 全面理解-命名修饰规则(命名倾轧Name Mangling)
  • Redis 常见面试题汇总(持续更新)