Chemmy's Blog

chengming0916@outlook.com

Longhorn 单机部署指南

Longhorn 是一款基于 Kubernetes 的分布式块存储系统,同时也可以在单节点 Kubernetes 集群中完成部署和使用,适合开发测试、边缘计算等轻量场景。

一、单机部署的核心前提

  1. 环境要求

    1. 单节点 Kubernetes 集群(如 Minikube、k3s、MicroK8s 或手动搭建的单节点集群)

    2. 节点满足 Longhorn 基础条件:

      • 操作系统:Linux(内核 4.14+)

      • 容器运行时:Docker、containerd 等

      • 磁盘:至少 1 块空闲磁盘或目录(用于存储数据)

      • 依赖工具:open-iscsi(必须安装并启动)

  2. 网络要求

    1. 节点能访问互联网(用于拉取 Longhorn 镜像)

    2. Kubernetes 集群网络插件正常运行(如 Calico、Flannel)

二、单机部署的具体步骤(以 k3s 为例)

1. 部署单节点 k3s 集群

1
2
3
4
# 安装 k3s(禁用默认存储,避免冲突)
curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb --disable local-storage
# 配置 kubectl 环境变量
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

2. 安装依赖组件 open-iscsi

1
2
3
4
5
6
7
# Ubuntu/Debian
sudo apt update && sudo apt install -y open-iscsi
sudo systemctl enable --now iscsid

# CentOS/RHEL
sudo yum install -y iscsi-initiator-utils
sudo systemctl enable --now iscsid

3. 部署 Longhorn

推荐使用 Helm 部署(更易管理):

1
2
3
4
5
6
7
8
9
# 安装 Helm 3
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 添加 Longhorn Helm 仓库
helm repo add longhorn https://charts.longhorn.io
helm repo update

# 部署 Longhorn 到 longhorn-system 命名空间
helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace

4. 验证部署状态

1
2
3
4
5
6
# 检查 Pod 是否全部 Running
kubectl -n longhorn-system get pods

# 检查 Longhorn 存储类是否创建
kubectl get sc
# 正常会看到 longhorn 存储类,且标注为 default

三、单机部署的注意事项

  1. 高可用限制

    1. 单机部署下,Longhorn 无法提供数据冗余(默认副本数为 3,需手动调整为 1),节点故障会导致数据丢失,仅适合非生产环境。

    2. 修改副本数:在 Longhorn UI 中设置默认副本数为 1,或在 PVC 注解中指定 numberOfReplicas: 1

  2. 存储路径配置

    1. 如果节点没有空闲磁盘,可以指定目录作为存储后端:

      1
      2
      3
      4
      5
      6
      # 创建本地目录
      sudo mkdir -p /data/longhorn
      sudo chmod 777 /data/longhorn
      # 部署时通过 Helm 参数指定
      helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace \
      --set defaultSettings.defaultDataPath=/data/longhorn
  3. 访问 Longhorn UI

    1. 单机环境下可通过 NodePort 访问 UI:

      1
      2
      kubectl -n longhorn-system get svc longhorn-frontend
      # 访问 http://<节点IP>:<NodePort>

四、单机部署的适用场景

  • 开发/测试环境:快速验证 Longhorn 功能、测试存储与应用的兼容性。

  • 边缘节点:资源受限的边缘设备,仅需单节点提供块存储。

  • 个人学习:熟悉 Kubernetes 存储编排和 Longhorn 运维操作。

使用MSYS2替代原生MinGW-w64搭建Qt 4.8.7 ARM32交叉编译环境,优势是MSYS2的包管理更便捷、依赖兼容性更强,且能完美兼容POSIX命令行环境(适配Qt 4.8.7的Unix风格编译脚本)。以下是基于MSYS2的完整实操指南,核心步骤与原生MinGW一致,但环境搭建环节适配MSYS2生态:

一、前置准备:下载清单(替换原生MinGW为MSYS2)

组件名称 用途 下载地址/获取方式
MSYS2 替代原生MinGW,提供编译环境 MSYS2官网(选Windows x86_64安装包)
ARM32交叉编译工具链 编译ARM32架构程序 推荐Linaro 7.5.0(arm-linux-gnueabihf):Linaro官网
Qt 4.8.7源码 Qt核心源码 Qt Archive
7-Zip 解压压缩包 7-Zip官网

说明:MSYS2内置Python 2.7/Perl/CMake,无需单独安装,通过包管理器一键部署即可。

二、步骤1:安装并配置MSYS2

1.1 安装MSYS2

  • 运行MSYS2安装包,默认安装到C:\msys64禁止修改路径含中文/空格);

  • 安装完成后自动打开MSYS2终端,执行更新命令(首次更新需耐心等待):

1
pacman -Syu
若终端提示“关闭后重启”,则关闭终端,重新打开MSYS2 MSYS终端,再次执行:
1
pacman -Su

1.2 安装MSYS2编译依赖(替代原生MinGW/Perl/Python/CMake)

打开MSYS2 MinGW64终端(关键:必须选MinGW64环境,而非MSYS环境),执行以下命令安装依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 安装MinGW64 GCC(核心编译工具)
pacman -S --needed mingw-w64-x86_64-gcc

# 安装Python 2.7(Qt 4.8.7编译必需)
# pacman -S --needed mingw-w64-x86_64-python2
pacman -U https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-python2-2.7.18-8-any.pkg.tar.zst

# 安装Perl(Qt编译脚本依赖)
pacman -S --needed mingw-w64-x86_64-perl

# 安装CMake(辅助编译)
pacman -S --needed mingw-w64-x86_64-cmake

# 安装make工具(mingw32-make)
pacman -S --needed mingw-w64-x86_64-make

# 安装依赖库(如zlib、libpng,Qt编译需)
pacman -S --needed mingw-w64-x86_64-zlib mingw-w64-x86_64-libpng mingw-w64-x86_64-libjpeg-turbo

1.3 验证MSYS2环境

在MinGW64终端执行以下命令,验证依赖安装成功:

1
2
3
4
5
6
7
8
# 验证GCC
gcc -v
# 验证Python 2.7
python2 --version
# 验证Perl
perl -v
# 验证make
mingw32-make -v

三、步骤2:配置ARM32交叉编译工具链(适配MSYS2)

2.1 解压Linaro工具链到MSYS2路径

  • 下载Linaro工具链(如gcc-linaro-7.5.0-2019.12-i686-mingw32_arm-linux-gnueabihf.tar.xz);

  • 用7-Zip解压到MSYS2的目录下(建议路径:C:\msys64\opt\arm-linux-gnueabihf-7.5.0),避免路径含空格/中文。

2.2 配置MSYS2环境变量(永久生效)

  • 打开MSYS2 MinGW64终端,编辑环境变量配置文件:
1
vi ~/.bashrc
  • 在文件末尾添加以下内容(指定交叉工具链路径):
1
2
3
4
# ARM交叉工具链路径
export PATH=/opt/arm-linux-gnueabihf-7.5.0/bin:$PATH
# 别名(可选,简化命令)
alias arm-gcc='arm-linux-gnueabihf-gcc'
  • 生效配置:
1
source ~/.bashrc

2.3 验证交叉工具链

在MinGW64终端执行:

1
arm-linux-gnueabihf-gcc -v

输出Linaro 7.5.0版本信息则成功(若提示“找不到命令”,检查工具链解压路径是否正确)。

四、步骤3:修改Qt 4.8.7源码(与原生MinGW一致,适配MSYS2路径)

4.1 解压Qt源码到MSYS2路径

将Qt 4.8.7源码解压到MSYS2可识别的路径(如C:\msys64\home\你的用户名\qt-4.8.7-arm),MSYS2中路径表示为/home/你的用户名/qt-4.8.7-arm(避免Windows风格路径导致编译报错)。

4.2 修改交叉编译配置文件

复制并修改mkspecs配置文件(与原生MinGW步骤一致,仅路径适配MSYS2):

  1. 进入Qt源码目录:
1
cd /home/你的用户名/qt-4.8.7-arm
  1. 复制并修改配置文件:
1
2
3
4
# 复制ARM配置模板
cp -r mkspecs/qws/linux-arm-gnueabi-g++ mkspecs/qws/linux-arm-gnueabihf-g++
# 编辑qmake.conf
vi mkspecs/qws/linux-arm-gnueabihf-g++/qmake.conf
  1. 替换qmake.conf内容(与原生MinGW的配置一致,工具链前缀不变):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
MAKEFILE_GENERATOR      = UNIX
TEMPLATE = app
CONFIG += qt warn_on release incremental link_prl gdb_dwarf_index
QT += core gui
QMAKE_INCREMENTAL_STYLE = sublib

# 交叉编译工具链前缀(匹配Linaro)
CROSS_COMPILE = arm-linux-gnueabihf-
CC = $$CROSS_COMPILE gcc
CXX = $$CROSS_COMPILE g++
LINK = $$CROSS_COMPILE g++
AR = $$CROSS_COMPILE ar cqs
RANLIB = $$CROSS_COMPILE ranlib
STRIP = $$CROSS_COMPILE strip
RC = $$CROSS_COMPILE windres

# ARM架构参数(适配ARMv7)
QMAKE_CFLAGS = -march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=hard -O2
QMAKE_CXXFLAGS = $$QMAKE_CFLAGS
QMAKE_LFLAGS = -march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=hard

# 系统库与路径(适配MSYS2)
QMAKE_INCDIR =
QMAKE_LIBDIR =
QMAKE_INCDIR_QT = $$[QT_INSTALL_HEADERS]
QMAKE_LIBDIR_QT = $$[QT_INSTALL_LIBS]
QMAKE_LIBS = -lrt -ldl -lpthread
QMAKE_LIBS_QT_ENTRY = -lQtCore -lQtGui
QMAKE_LIBS_GUI = -lX11 -lXext -lXt -lm -lSM -lICE -lfontconfig -lfreetype
QMAKE_LIBS_CORE = -lz -lm -ldl -lpthread

# 输出目录
DESTDIR = ../bin

五、步骤4:配置并编译Qt 4.8.7 ARM版本(MSYS2终端执行)

5.1 清理旧配置(首次编译可跳过)

1
2
cd /home/你的用户名/qt-4.8.7-arm
mingw32-make distclean

5.2 执行configure(核心:适配MSYS2路径)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
./configure -prefix /home/你的用户名/qt-4.8.7-arm-build \
-opensource \
-confirm-license \
-release \
-shared \
-embedded arm \
-xplatform qws/linux-arm-gnueabihf-g++ \
-no-webkit \
-no-phonon \
-no-phonon-backend \
-no-qt3support \
-no-multimedia \
-no-ltcg \
-no-dbus \
-no-opengl \
-no-openvg \
-no-svg \
-no-javascript-jit \
-no-script \
-no-scripttools \
-no-declarative \
-no-declarative-debug \
-nomake demos \
-nomake examples \
-nomake docs \
-qt-libpng \
-qt-libjpeg \
-qt-zlib \
-little-endian \
-host-little-endian \
-verbose

关键差异:-prefix使用MSYS2的Unix风格路径(/home/...),而非Windows路径(D:...),避免Qt配置解析路径出错。

5.3 编译Qt源码

1
2
# 多核编译(-j后接CPU核心数,如8核则-j8)
mingw32-make -j8

5.4 安装编译结果

1
mingw32-make install

安装完成后,/home/你的用户名/qt-4.8.7-arm-build(对应Windows路径C:\msys64\home\你的用户名\qt-4.8.7-arm-build)即为ARM交叉编译环境。

六、步骤5:验证交叉编译环境(MSYS2终端)

6.1 新建测试工程

在MSYS2中新建测试目录:

1
2
mkdir /home/你的用户名/qt-arm-test
cd /home/你的用户名/qt-arm-test

新建test.pro

1
2
3
4
QT += core gui
TARGET = test
TEMPLATE = app
SOURCES += main.cpp

新建main.cpp

1
2
3
4
5
6
7
8
9
10
#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel lbl("Hello ARM32 Qt 4.8.7 (MSYS2)!");
lbl.show();
return a.exec();
}

6.2 生成Makefile并编译

1
2
3
4
# 使用ARM版本的qmake
/home/你的用户名/qt-4.8.7-arm-build/bin/qmake -spec qws/linux-arm-gnueabihf-g++ test.pro
# 编译
mingw32-make

6.3 验证ARM程序

  • 安装file命令(MSYS2终端):
1
pacman -S file
  • 验证程序架构:
1
file test

输出类似test: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked,说明成功。

七、MSYS2专属问题与解决

  1. configure提示“bash: ./configure: 权限不够”

  2. 执行chmod +x configure赋予执行权限。

  3. 编译时提示“找不到python”

  4. MSYS2中Python 2.7命令为python2,Qt 4.8.7默认找python,需创建软链接:

1
ln -s /mingw64/bin/python2.exe /mingw64/bin/python.exe
  1. 工具链路径识别失败

  2. MSYS2中/opt对应Windows路径C:\msys64\opt,确保工具链解压到该路径,且~/.bashrc中PATH配置正确。

  3. mingw32-make报错“recipe for target failed”

  4. 降低编译核心数(如-j4),避免内存不足;检查Qt源码路径是否含中文/空格。

总结

用MSYS2替代原生MinGW的核心优势是:

  1. 无需手动安装Python/Perl/CMake,包管理器一键部署,版本兼容性更高;

  2. POSIX终端环境更适配Qt 4.8.7的Unix风格编译脚本,减少路径/命令兼容问题;

  3. 环境变量配置永久生效,无需频繁修改系统PATH。

核心注意点:全程使用MSYS2 MinGW64终端(而非MSYS终端/系统CMD),路径统一使用MSYS2的Unix风格路径(/home/...),避免Windows路径解析错误。

常用Grafana模板

名称 模板ID 说明
Node Exporter Full 1860 Kubernetes 集群监控
K3s Cluster Dashboard 14205,7249 K3s专属监控
kube-state-metrics 12740
MySQL Overview 7362
Nvidia GPU Metrics 14574 英伟达显卡监控
PostgreSQL Database 9628
Redis 12497
EMQX 17446
Docker-cAdvisor 13946

Alloy配置模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 基础设施指标收集组件一般都是prometheus.exporter.*的形式, 见官方文档https://grafana.com/docs/alloy/latest/reference/components/prometheus/
// 目标组件, 这里目标组件是Linux
prometheus.exporter.unix "node" {}

// 抓取目标组件指标
prometheus.scrape "node" {
// 源
targets = prometheus.exporter.unix.node.targets
// 指标转发
forward_to = [prometheus.relabel.node.receiver]
// 10s抓取一次指标
scrape_interval = "10s"
}

// 筛选指标
// prometheus.relabel 组件通常用于筛选 Prometheus 指标或标准化传递给一个或多个下游接收器的标签集
prometheus.relabel "node" {
// 将address或者instance替换成host
rule {
action = "replace"
source_labels = ["__address__", "instance"]
separator = "/"
target_label = "host"
}
// 处理完后转发到组件
forward_to = [prometheus.remote_write.metrics_service.receiver]
}


prometheus.exporter.redis "redis" {
redis_addr = "redis:6379"
redis_password = "123456"
}

prometheus.scrape "redis" {
targets = prometheus.exporter.redis.redis.targets
forward_to = [prometheus.remote_write.metrics_service.receiver]
}


// 指标发送到prometheus中
prometheus.remote_write "metrics_service" {
endpoint {
url = "http://prometheus:9090/api/v1/write"
}
}

环境变量

用于配置 Ollama 的运行环境和行为,以下是一些常见的环境变量及其用途:

网络配置

  • OLLAMA_HOST:定义 Ollama 服务器的协议和主机地址。默认为 127.0.0.1:11434,仅本机地址可通过 11434 端口访问该服务。可以通过此变量自定义 Ollama 服务的监听地址和端口,例如设置为 0.0.0.0:8080 ,可允许其他电脑访问 Ollama(如:局域网中的其他电脑)。默认使用 http 协议。若要使用 https 协议,可设置为:https://0.0.0.0:443

  • OLLAMA_ORIGINS:配置允许跨域请求的来源列表。默认包含 localhost127.0.0.10.0.0.0 等本地地址以及一些特定协议的来源。通过设置此变量,可以指定哪些来源可以访问 Ollama 服务,例如 OLLAMA_ORIGINS=*,https://example.com 允许所有来源以及 https://example.com 的跨域请求。

模型管理

  • OLLAMA_MODELS:指定模型文件的存储路径。默认为用户主目录下的 .ollama/models 文件夹。通过设置此变量,可以自定义模型文件的存储位置,例如 OLLAMA_MODELS=/path/to/models 将模型存储在指定的路径下。

  • OLLAMA_KEEP_ALIVE:控制模型在内存中的存活时间。默认为 5 分钟。负值表示无限存活,0 表示不保持模型在内存中。此变量用于优化模型加载和运行的性能,例如 OLLAMA_KEEP_ALIVE=30m 可以让模型在内存中保持 30 分钟。

  • OLLAMA_LOAD_TIMEOUT:设置模型加载过程中的超时时间。默认为 5 分钟。0 或负值表示无限超时。此变量用于防止模型加载过程过长导致服务无响应,例如 OLLAMA_LOAD_TIMEOUT=10m 可以将超时时间设置为 10 分钟。

  • OLLAMA_MAX_LOADED_MODELS:限制同时加载的模型数量。默认为 0,表示不限制。此变量用于合理分配系统资源,避免过多模型同时加载导致资源不足,例如 OLLAMA_MAX_LOADED_MODELS=4 可以限制同时加载 4 个模型。

  • OLLAMA_MAX_QUEUE:设置请求队列的最大长度。默认为 512。此变量用于控制并发请求的数量,避免过多请求同时处理导致服务过载,例如 OLLAMA_MAX_QUEUE=1024 可以将队列长度设置为 1024。

  • OLLAMA_MAX_VRAM:设置 GPU 显存的最大使用量(以字节为单位)。默认为 0,表示不限制。此变量用于控制 GPU 资源的使用,避免显存不足导致的问题,例如 OLLAMA_MAX_VRAM=8589934592 可以将显存限制为 8GB。

  • OLLAMA_GPU_OVERHEAD:为每个 GPU 预留的显存(以字节为单位)。默认为 0。此变量用于确保每个 GPU 有一定的显存余量,避免显存不足导致的问题,例如 OLLAMA_GPU_OVERHEAD=1073741824 可以为每个 GPU 预留 1GB 的显存。

性能与调度

  • OLLAMA_NUM_PARALLEL:设置同时处理的并行请求数量。默认为 0,表示不限制。此变量用于优化服务的并发处理能力,例如 OLLAMA_NUM_PARALLEL=8 可以同时处理 8 个并行请求。

  • OLLAMA_SCHED_SPREAD:允许模型跨所有 GPU 进行调度。默认为 false。启用此变量可以提高模型运行的灵活性和资源利用率,例如 OLLAMA_SCHED_SPREAD=1 可以启用跨 GPU 调度。

调试与日志

  • OLLAMA_DEBUG:启用额外的调试信息。默认为 false。开启此变量可以获取更多的调试日志,帮助排查问题,例如 OLLAMA_DEBUG=1 可以启用调试模式。

  • OLLAMA_NOHISTORY:禁用 readline 历史记录。默认为 false。启用此变量可以避免保存命令历史记录,例如 OLLAMA_NOHISTORY=1 可以禁用历史记录。

  • OLLAMA_NOPRUNE:在启动时不清理模型文件。默认为 false。启用此变量可以保留所有模型文件,避免不必要的清理操作,例如 OLLAMA_NOPRUNE=1 可以禁用模型文件的清理。

特性开关

  • OLLAMA_FLASH_ATTENTION:启用实验性的 Flash Attention 特性。默认为 false。此变量用于测试和使用新的注意力机制特性,例如 OLLAMA_FLASH_ATTENTION=1 可以启用 Flash Attention。

  • OLLAMA_MULTIUSER_CACHE:为多用户场景优化提示缓存。默认为 false。启用此变量可以提高多用户环境下的缓存效率,例如 OLLAMA_MULTIUSER_CACHE=1 可以启用多用户缓存优化

代理设置

  • HTTP_PROXY:设置 HTTP 代理服务器地址。此变量用于配置 Ollama 在进行 HTTP 请求时使用的代理服务器,例如 HTTP_PROXY=http://proxy.example.com:8080 可以让 Ollama 使用指定的 HTTP 代理。

  • HTTPS_PROXY:设置 HTTPS 代理服务器地址。此变量用于配置 Ollama 在进行 HTTPS 请求时使用的代理服务器,例如 HTTPS_PROXY=https://proxy.example.com:8080 可以让 Ollama 使用指定的 HTTPS 代理。

  • NO_PROXY:设置不使用代理的地址列表。此变量用于指定哪些地址在进行请求时不使用代理,例如 NO_PROXY=localhost,example.com 可以让 Ollama 在访问 localhost 和 example.com 时不使用代理.


CLI命令

命令 用途
ollama serve 在本地系统上启动 Ollama。
ollama create 从现有模型创建一个新模型,用于定制或训练。
ollama show 显示特定模型的详细信息,例如其配置和发布日期。
ollama run 运行指定的模型,使其准备好进行交互。
ollama pull 将指定的模型下载到您的系统。
ollama list 列出所有已下载的模型。
ollama ps 显示当前正在运行的模型。
ollama stop 停止指定的正在运行的模型。
ollama rm 从您的系统中移除指定的模型。

在终端,输入上面的命令 + ‘-h’,可查看具体命令的帮助文档(如,ollama show -h, 可查看 show 命令的帮助文档)。下面列出每个命令的具体用法和示例。

启动 Ollama

在本地系统上启动 Ollama:ollama server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
ollama serve -h

---
Start ollama

Usage:
ollama serve [flags]

Aliases:
serve, start

Flags:
-h, --help help for serve

Environment Variables:
OLLAMA_DEBUG Show additional debug information (e.g. OLLAMA_DEBUG=1)
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)
OLLAMA_KEEP_ALIVE The duration that models stay loaded in memory (default "5m")
OLLAMA_MAX_LOADED_MODELS Maximum number of loaded models per GPU
OLLAMA_MAX_QUEUE Maximum number of queued requests
OLLAMA_MODELS The path to the models directory
OLLAMA_NUM_PARALLEL Maximum number of parallel requests
OLLAMA_NOPRUNE Do not prune model blobs on startup
OLLAMA_ORIGINS A comma separated list of allowed origins
OLLAMA_SCHED_SPREAD Always schedule model across all GPUs

OLLAMA_FLASH_ATTENTION Enabled flash attention
OLLAMA_KV_CACHE_TYPE Quantization type for the K/V cache (default: f16)
OLLAMA_LLM_LIBRARY Set LLM library to bypass autodetection
OLLAMA_GPU_OVERHEAD Reserve a portion of VRAM per GPU (bytes)
OLLAMA_LOAD_TIMEOUT How long to allow model loads to stall before giving up (default "5m")
---

模型管理

1. 列出所有已下载的模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ollama list -h
List models

Usage:
ollama list [flags]

Aliases:
list, ls

Flags:
-h, --help help for list

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)

2. 查看某个模型的详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ollama show -h
Show information for a model

Usage:
ollama show MODEL [flags]

Flags:
-h, --help help for show
--license Show license of a model
--modelfile Show Modelfile of a model
--parameters Show parameters of a model
--system Show system message of a model
--template Show template of a model

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)

3. 显示正在运行的大模型

1
2
3
4
5
6
7
8
9
10
11
ollama ps -h
List running models

Usage:
ollama ps [flags]

Flags:
-h, --help help for ps

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)

4. 停止指定的正在运行的模型

1
2
3
4
5
6
7
8
9
10
11
ollama stop -h
Stop a running model

Usage:
ollama stop MODEL [flags]

Flags:
-h, --help help for stop

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)

5. 删除已下载的大模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ollama rm -h

---
Remove a model

Usage:
ollama rm MODEL [MODEL...] [flags]

Flags:
-h, --help help for rm

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)
---

6. 下载大模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ollama pull -h

---
Pull a model from a registry

Usage:
ollama pull MODEL [flags]

Flags:
-h, --help help for pull
--insecure Use an insecure registry

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)
---

运行大模型

尽管我们可以通过第三方的 Web UI 等方式与基于 Ollama 部署的本地大模型进行交互,但通过 run 命令的方式与大模型交互,具有以下的优点:

  • 直接在终端运行 run 命令,可便捷高效地验证本地部署的大模型的响应效果
  • 记录保存大模型的响应:可将大模型的响应记录到本地文件。
  • 基于 run 命令开发自动化执行的脚本,定时调度执行,实现与大模型交互的自动化工作流程。

run 命令的帮助文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ollama run -h

---
Run a model

Usage:
ollama run MODEL [PROMPT] [flags]

Flags:
--format string Response format (e.g. json)
-h, --help help for run
--insecure Use an insecure registry
--keepalive string Duration to keep a model loaded (e.g. 5m)
--nowordwrap Don't wrap words to the next line automatically
--verbose Show timings for response

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)
OLLAMA_NOHISTORY Do not preserve readline history
---

Longhorn 单机部署指南

Longhorn 支持单机部署,它是一款基于 Kubernetes 的分布式块存储系统,同时也可以在单节点 Kubernetes 集群中完成部署和使用,适合开发测试、边缘计算等轻量场景。

一、单机部署的核心前提

  1. 环境要求

    1. 单节点 Kubernetes 集群(如 Minikube、k3s、MicroK8s 或手动搭建的单节点集群)

    2. 节点满足 Longhorn 基础条件:

      • 操作系统:Linux(内核 4.14+)

      • 容器运行时:Docker、containerd 等

      • 磁盘:至少 1 块空闲磁盘或目录(用于存储数据)

      • 依赖工具:open-iscsi(必须安装并启动)

  2. 网络要求

    1. 节点能访问互联网(用于拉取 Longhorn 镜像)

    2. Kubernetes 集群网络插件正常运行(如 Calico、Flannel)

二、单机部署的具体步骤(以 k3s 为例)

1. 部署单节点 k3s 集群

1
2
3
4
# 安装 k3s(禁用默认存储,避免冲突)
curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb --disable local-storage
# 配置 kubectl 环境变量
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

2. 安装依赖组件 open-iscsi

1
2
3
4
5
6
7
# Ubuntu/Debian
sudo apt update && sudo apt install -y open-iscsi
sudo systemctl enable --now iscsid

# CentOS/RHEL
sudo yum install -y iscsi-initiator-utils
sudo systemctl enable --now iscsid

3. 部署 Longhorn

推荐使用 Helm 部署(更易管理):

1
2
3
4
5
6
7
8
9
# 安装 Helm 3
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 添加 Longhorn Helm 仓库
helm repo add longhorn https://charts.longhorn.io
helm repo update

# 部署 Longhorn 到 longhorn-system 命名空间
helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace

4. 验证部署状态

1
2
3
4
5
6
# 检查 Pod 是否全部 Running
kubectl -n longhorn-system get pods

# 检查 Longhorn 存储类是否创建
kubectl get sc
# 正常会看到 longhorn 存储类,且标注为 default

三、单机部署的注意事项

  1. 高可用限制

    1. 单机部署下,Longhorn 无法提供数据冗余(默认副本数为 3,需手动调整为 1),节点故障会导致数据丢失,仅适合非生产环境。

    2. 修改副本数:在 Longhorn UI 中设置默认副本数为 1,或在 PVC 注解中指定 numberOfReplicas: 1

  2. 存储路径配置

    1. 如果节点没有空闲磁盘,可以指定目录作为存储后端:

      1
      2
      3
      4
      5
      6
      # 创建本地目录
      sudo mkdir -p /data/longhorn
      sudo chmod 777 /data/longhorn
      # 部署时通过 Helm 参数指定
      helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace \
      --set defaultSettings.defaultDataPath=/data/longhorn
  3. 访问 Longhorn UI

    1. 单机环境下可通过 NodePort 访问 UI:

      1
      2
      kubectl -n longhorn-system get svc longhorn-frontend
      # 访问 http://<节点IP>:<NodePort>

四、单机部署的适用场景

  • 开发/测试环境:快速验证 Longhorn 功能、测试存储与应用的兼容性。

  • 边缘节点:资源受限的边缘设备,仅需单节点提供块存储。

  • 个人学习:熟悉 Kubernetes 存储编排和 Longhorn 运维操作。

是否需要我为你整理单机部署 Longhorn 后的测试用例,比如创建 PVC 并挂载到 Nginx 容器进行验证?

在Windows 10中搭建Qt 4.8.7 ARM32交叉编译环境,核心是获取ARM32交叉编译工具链编译Qt 4.8.7源码适配ARM架构配置环境变量与编译规则,以下是分步实操指南(全程需管理员权限,建议关闭杀毒软件避免文件拦截)。

一、前置准备:确定核心依赖与下载清单

Qt 4.8.7本身不提供预编译的ARM32交叉编译包,需手动编译源码,需下载以下组件(版本需严格匹配,避免兼容性问题):

组件名称 用途 下载地址/获取方式
MinGW-w64(8.1.0) Windows下的GCC编译环境 SourceForge(选x86_64-posix-seh)
ARM32交叉编译工具链 编译ARM32架构程序 推荐Linaro 7.5.0(arm-linux-gnueabihf):Linaro官网
Qt 4.8.7源码 Qt核心源码(需适配ARM) Qt Archive
Python 2.7.x Qt 4.8.7编译依赖(必须2.7) Python官网(选Windows x86-64 MSI)
Perl 5.28+ Qt编译脚本依赖 Strawberry Perl(选64位版本)
CMake 3.10+ 辅助编译(可选但建议) CMake官网(选Windows x64 Installer)
7-Zip 解压tar.gz/压缩包 7-Zip官网

二、步骤1:安装基础编译环境(Windows侧)

1.1 安装MinGW-w64

  • 下载MinGW-w64后解压到固定路径(如D:\mingw64),将D:\mingw64\bin添加到系统环境变量Path(优先级高于系统自带MinGW)。

  • 验证:打开CMD,输入gcc -v,输出MinGW-w64 8.1.0版本信息则成功。

1.2 安装Python 2.7 + Perl + CMake

  • Python 2.7:安装时勾选“Add Python to PATH”,验证python --version输出2.7.x。

  • Strawberry Perl:默认安装即可,验证perl -v输出5.28+版本。

  • CMake:安装时勾选“Add CMake to the system PATH for all users”,验证cmake --version输出3.10+。

三、步骤2:配置ARM32交叉编译工具链

2.1 解压并配置Linaro工具链

  • 下载Linaro工具链(如gcc-linaro-7.5.0-2019.12-i686-mingw32_arm-linux-gnueabihf.tar.xz),解压到固定路径(如D:\arm-linux-gnueabihf-7.5.0)。

  • 将工具链bin目录(D:\arm-linux-gnueabihf-7.5.0\bin)添加到系统环境变量Path

  • 验证:CMD输入arm-linux-gnueabihf-gcc -v,输出Linaro 7.5.0版本信息则成功。

四、步骤3:修改Qt 4.8.7源码适配ARM交叉编译

Qt 4.8.7默认不支持Windows下ARM交叉编译,需手动修改配置文件:

4.1 解压Qt源码

qt-everywhere-opensource-src-4.8.7.tar.gz用7-Zip解压到固定路径(如D:\qt-4.8.7-arm),避免路径含中文/空格。

4.2 修改交叉编译配置文件

Qt 4.8.7的交叉编译需通过mkspecs目录下的配置文件定义,步骤:

  1. 复制D:\qt-4.8.7-arm\mkspecs\qws\linux-arm-gnueabi-g++目录,重命名为linux-arm-gnueabihf-g++(匹配Linaro工具链的hf(硬件浮点))。

  2. 编辑新目录下的qmake.conf,替换内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 基础配置
MAKEFILE_GENERATOR = UNIX
TEMPLATE = app
CONFIG += qt warn_on release incremental link_prl gdb_dwarf_index
QT += core gui
QMAKE_INCREMENTAL_STYLE = sublib

# 交叉编译工具链前缀(匹配Linaro工具链)
CROSS_COMPILE = arm-linux-gnueabihf-
CC = $$CROSS_COMPILE gcc
CXX = $$CROSS_COMPILE g++
LINK = $$CROSS_COMPILE g++
AR = $$CROSS_COMPILE ar cqs
RANLIB = $$CROSS_COMPILE ranlib
STRIP = $$CROSS_COMPILE strip
RC = $$CROSS_COMPILE windres

# ARM架构参数(适配ARM32,如ARMv7)
QMAKE_CFLAGS = -march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=hard -O2
QMAKE_CXXFLAGS = $$QMAKE_CFLAGS
QMAKE_LFLAGS = -march=armv7-a -mtune=cortex-a9 -mfpu=neon -mfloat-abi=hard

# Qt运行环境(QWS是Qt 4的嵌入式窗口系统)
QMAKE_INCDIR =
QMAKE_LIBDIR =
QMAKE_INCDIR_QT = $$[QT_INSTALL_HEADERS]
QMAKE_LIBDIR_QT = $$[QT_INSTALL_LIBS]
QMAKE_INCDIR_OPENGL_ES2 =
QMAKE_LIBDIR_OPENGL_ES2 =
QMAKE_INCDIR_EGL =
QMAKE_LIBDIR_EGL =
QMAKE_INCDIR_OPENVG =
QMAKE_LIBDIR_OPENVG =

# 系统库
QMAKE_LIBS = -lrt -ldl -lpthread
QMAKE_LIBS_QT_ENTRY = -lQtCore -lQtGui
QMAKE_LIBS_GUI = -lX11 -lXext -lXt -lm -lSM -lICE -lfontconfig -lfreetype
QMAKE_LIBS_CORE = -lz -lm -ldl -lpthread

# 输出目录
DESTDIR = ../bin

注:-march=armv7-a/-mtune=cortex-a9需根据目标ARM芯片调整(如ARMv6则改-march=armv6)。

五、步骤4:配置并编译Qt 4.8.7 ARM版本

5.1 生成编译配置(关键:避免Qt默认编译Windows版本)

打开MinGW-w64的CMD窗口(必须用MinGW的终端,而非系统CMD),执行以下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 进入Qt源码根目录
cd /d D:\qt-4.8.7-arm

# 清理旧配置(首次编译可跳过)
mingw32-make distclean

# 配置交叉编译参数(核心命令)
configure -prefix D:\qt-4.8.7-arm-build \
-opensource \
-confirm-license \
-release \
-shared \
-embedded arm \
-xplatform qws/linux-arm-gnueabihf-g++ \
-no-webkit \
-no-phonon \
-no-phonon-backend \
-no-qt3support \
-no-multimedia \
-no-ltcg \
-no-dbus \
-no-opengl \
-no-openvg \
-no-svg \
-no-javascript-jit \
-no-script \
-no-scripttools \
-no-declarative \
-no-declarative-debug \
-nomake demos \
-nomake examples \
-nomake docs \
-qt-libpng \
-qt-libjpeg \
-qt-zlib \
-little-endian \
-host-little-endian \
-verbose

参数说明:

  • -prefix:指定Qt ARM版本安装路径;

  • -embedded arm:启用嵌入式ARM支持(Qt 4的QWS);

  • -xplatform:指定步骤4.2中修改的交叉编译配置;

  • 禁用webkit/phonon等非必需模块,减少编译时间和报错。

5.2 编译Qt源码

配置成功后(无error),执行编译命令:

1
2
# 多核编译(-j后接CPU核心数,如8核则-j8)
mingw32-make -j8

⚠️ 编译耗时(1-2小时),若报错:

  • 检查工具链路径是否正确;

  • 检查qmake.conf中工具链前缀是否匹配;

  • 若提示“python找不到”,确保Python 2.7在PATH最前(Qt 4不兼容Python 3)。

5.3 安装编译后的Qt ARM版本

编译完成后执行:

1
mingw32-make install

安装完成后,D:\qt-4.8.7-arm-build即为可用于ARM32交叉编译的Qt环境。

六、步骤5:验证交叉编译环境

编写简单Qt测试程序,验证能否编译出ARM32可执行文件:

6.1 新建测试工程

在任意目录新建test.pro

1
2
3
4
QT += core gui
TARGET = test
TEMPLATE = app
SOURCES += main.cpp

新建main.cpp

1
2
3
4
5
6
7
8
9
10
#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel lbl("Hello ARM32 Qt 4.8.7!");
lbl.show();
return a.exec();
}

6.2 用Qt ARM版本的qmake生成Makefile

打开MinGW终端,执行:

1
2
# 指定ARM版本的qmake路径
D:\qt-4.8.7-arm-build\bin\qmake.exe test.pro -spec qws/linux-arm-gnueabihf-g++

6.3 编译生成ARM32程序

执行:

1
mingw32-make

编译完成后生成test可执行文件,用file命令(需安装Git for Windows,或复制到Linux)验证:

1
2
# Git Bash中执行
file test

输出类似test: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked,说明成功生成ARM32程序。

七、常见问题与解决

  1. configure阶段提示“无法找到arm-linux-gnueabihf-gcc”

    1. 检查工具链bin目录是否在PATH中,重启终端生效;

    2. 确认工具链解压完整,无文件缺失。

  2. 编译阶段提示“undefined reference to xxx”

    1. 禁用非必需模块(如webkit),Qt 4.8.7部分模块对ARM交叉编译支持差;

    2. 检查qmake.conf中链接库参数(QMAKE_LIBS)是否完整。

  3. Python版本错误

    1. 卸载Python 3,仅保留Python 2.7,或在PATH中优先放置Python 2.7路径。

总结

Win10下Qt 4.8.7 ARM32交叉编译的核心是:

  1. 搭建MinGW+ARM交叉工具链环境;

  2. 修改Qt源码的交叉编译配置文件;

  3. 通过configure指定ARM编译参数,编译源码;

  4. 验证生成的程序为ARM32架构。

若追求效率,也可在Linux虚拟机中完成Qt 4.8.7 ARM交叉编译(Linux下交叉编译兼容性更好),再将编译结果复制到Windows使用。

我们首先来了解一下 Go 语言中 string 类型的结构定义,先来看一下官方定义:

1
2
3
4
// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

string 是一个 8 位字节的集合,通常但不一定代表UTF-8编码的文本。string可以为空,但是不能为nil。 string的值是不能改变的

string 类型本质也是一个结构体,定义如下:

1
2
3
4
type stringStruct struct {
str unsafe.Pointer
len int
}

stringStructslice 还是很相似的, str 指针指向的是某个数组的首地址, len 代表的就是数组长度。怎么和 slice 这么相似,底层指向的也是数组,是什么数组呢?我们看看他在实例化时调用的方法:

1
2
3
4
5
6
//go:nosplit
func gostringnocopy(str *byte) string {
ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}
s := *(*string)(unsafe.Pointer(&ss))
return s
}

入参是一个 byte 类型的指针,从这我们可以看出 string 类型底层是一个 byte 类型的数组,所以我们可以画出这样一个图片:

![[Golang字符串拼接的6种方式/IMG-20251216170853116.png]]

string 类型本质上就是一个 byte 类型的数组,在 Go 语言中 string 类型被设计为不可变的,不仅是在 Go 语言,其他语言中 string 类型也是被设计为不可变的,这样的好处就是:在并发场景下,我们可以在不加锁的控制下,多次使用同一字符串,在保证高效共享的情况下而不用担心安全问题。

string 类型虽然是不能更改的,但是可以被替换,因为 stringStruct 中的 str 指针是可以改变的,只是指针指向的内容是不可以改变的,也就说每一个更改字符串,就需要重新分配一次内存,之前分配的空间会被 gc 回收。

关于 string 类型的知识点就描述这么多,方便我们后面分析字符串拼接。

字符串拼接的6种方式及原理

原生拼接方式”+”

Go 语言原生支持使用 + 操作符直接对两个字符串进行拼接,使用例子如下:

1
2
3
var s string
s += "hello"
s += "world"

这种方式使用起来最简单,基本所有语言都有提供这种方式,使用 + 操作符进行拼接时,会对字符串进行遍历,计算并开辟一个新的空间来存储原来的两个字符串。

字符串格式化函数fmt.Sprintf

Go 语言中默认使用函数 fmt.Sprintf 进行字符串格式化,所以也可使用这种方式进行字符串拼接:

1
2
str := "hello "
str = fmt.Sprintf("%s%s", str, str)

fmt.Sprintf 实现原理主要是使用到了反射,具体源码分析因为篇幅的原因就不在这里详细分析了,看到反射,就会产生性能的损耗,你们懂得!!!

Strings.builder

Go 语言提供了一个专门操作字符串的库 strings ,使用 strings.Builder 可以进行字符串拼接,提供了 writeString 方法拼接字符串,使用方式如下:

1
2
3
4
var builder strings.Builder
builder.WriteString("hello ")
builder.WriteString("world")
builder.String()

strings.builder 的实现原理很简单,结构如下:

1
2
3
4
type Builder struct {
addr *Builder // of receiver, to detect copies by value
buf []byte // 1
}

addr 字段主要是做 copycheckbuf 字段是一个 byte 类型的切片,这个就是用来存放字符串内容的,提供的 writeString() 方法就是像切片 buf 中追加数据:

1
2
3
4
5
func (b *Builder) WriteString(s string) (int, error) {
b.copyCheck()
b.buf = append(b.buf, s...)
return len(s), nil
}

提供的 String 方法就是将 []]byte 转换为 string 类型,这里为了避免内存拷贝的问题,使用了强制转换来避免内存拷贝:

1
2
3
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}

bytes.Buffer

因为 string 类型底层就是一个 byte 数组,所以我们就可以 Go 语言的 bytes.Buffer 进行字符串拼接。 bytes.Buffer 是一个一个缓冲 byte 类型的缓冲器,这个缓冲器里存放着都是 byte 。使用方式如下:

1
2
3
buf := new(bytes.Buffer)
buf.WriteString("hello")
buf.String()

bytes.buffer 底层也是一个 []byte 切片,结构体如下:

1
2
3
4
5
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly.
}

因为 bytes.Buffer 可以持续向 Buffer 尾部写入数据,从 Buffer 头部读取数据,所以 off 字段用来记录读取位置,再利用切片的 cap 特性来知道写入位置,这个不是本次的重点,重点看一下 WriteString 方法是如何拼接字符串的:

1
2
3
4
5
6
7
8
func (b *Buffer) WriteString(s string) (n int, err error) {
b.lastRead = opInvalid
m, ok := b.tryGrowByReslice(len(s))
if !ok {
m = b.grow(len(s))
}
return copy(b.buf[m:], s), nil
}

切片在创建时并不会申请内存块,只有在往里写数据时才会申请,首次申请的大小即为写入数据的大小。如果写入的数据小于64字节,则按64字节申请。采用动态扩展 slice 的机制,字符串追加采用 copy 的方式将追加的部分拷贝到尾部, copy 是内置的拷贝函数,可以减少内存分配。

但是在将 []byte 转换为 string 类型依旧使用了标准类型,所以会发生内存分配:

1
2
3
4
5
6
7
func (b *Buffer) String() string {
if b == nil {
// Special case, useful in debugging.
return "<nil>"
}
return string(b.buf[b.off:])
}

strings.join

Strings.join 方法可以将一个 string 类型的切片拼接成一个字符串,可以定义连接操作符,使用如下:

1
2
baseSlice := []string{"hello", "world"}
strings.Join(baseSlice, "")

strings.join 也是基于 strings.builder 来实现的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func Join(elems []string, sep string) string {
switch len(elems) {
case 0:
return ""
case 1:
return elems[0]
}
n := len(sep) * (len(elems) - 1)
for i := 0; i < len(elems); i++ {
n += len(elems[i])
}

var b Builder
b.Grow(n)
b.WriteString(elems[0])
for _, s := range elems[1:] {
b.WriteString(sep)
b.WriteString(s)
}
return b.String()
}

唯一不同在于在 join 方法内调用了 b.Grow(n) 方法,这个是进行初步的容量分配,而前面计算的n的长度就是我们要拼接的slice的长度,因为我们传入切片长度固定,所以提前进行容量分配可以减少内存分配,很高效。

切片append

因为 string 类型底层也是 byte 类型数组,所以我们可以重新声明一个切片,使用 append 进行字符串拼接,使用方式如下:

1
2
3
4
buf := make([]byte, 0)
base = "hello"
buf = append(buf, base...)
string(base)

如果想减少内存分配,在将 []byte 转换为 string 类型时可以考虑使用强制转换。

Benchmark对比

上面我们总共提供了6种方法,原理我们基本知道了,那么我们就使用 Go 语言中的 Benchmark 来分析一下到底哪种字符串拼接方式更高效。我们主要分两种情况进行分析:

  • 少量字符串拼接
  • 大量字符串拼接

我们先定义一个基础字符串:

1
var base  = "123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASFGHJKLZXCVBNM"

少量字符串拼接的测试我们就采用拼接一次的方式验证,base拼接base,因此得出benckmark结果:

1
2
3
4
5
6
7
8
9
10
11
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkSumString-16 21338802 49.19 ns/op 128 B/op 1 allocs/op
BenchmarkSprintfString-16 7887808 140.5 ns/op 160 B/op 3 allocs/op
BenchmarkBuilderString-16 27084855 41.39 ns/op 128 B/op 1 allocs/op
BenchmarkBytesBuffString-16 9546277 126.0 ns/op 384 B/op 3 allocs/op
BenchmarkJoinstring-16 24617538 48.21 ns/op 128 B/op 1 allocs/op
BenchmarkByteSliceString-16 10347416 112.7 ns/op 320 B/op 3 allocs/op
PASS
ok 8.412s

大量字符串拼接的测试我们先构建一个长度为200的字符串切片:

1
2
3
4
var baseSlice []string
for i := 0; i < 200; i++ {
baseSlice = append(baseSlice, base)
}

然后遍历这个切片不断的进行拼接,因为可以得出 benchmark:

1
2
3
4
5
6
7
8
9
10
11
12
goos: darwin
goarch: amd64

cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkSumString-16 7396 163612 ns/op 1277713 B/op 199 allocs/op
BenchmarkSprintfString-16 5946 202230 ns/op 1288552 B/op 600 allocs/op
BenchmarkBuilderString-16 262525 4638 ns/op 40960 B/op 1 allocs/op
BenchmarkBytesBufferString-16 183492 6568 ns/op 44736 B/op 9 allocs/op
BenchmarkJoinstring-16 398923 3035 ns/op 12288 B/op 1 allocs/op
BenchmarkByteSliceString-16 144554 8205 ns/op 60736 B/op 15 allocs/op
PASS
ok 10.699s

通过两次 benchmark 对比,我们可以看到

  • 当进行少量字符串拼接时,直接使用 + 操作符进行拼接字符串,效率还是挺高的,但是当要拼接的字符串数量上来时, + 操作符的性能就比较低了;
  • 函数 fmt.Sprintf 还是不适合进行字符串拼接,无论拼接字符串数量多少,性能损耗都很大,还是老老实实做他的字符串格式化就好了;
  • strings.Builder 无论是少量字符串的拼接还是大量的字符串拼接,性能一直都能稳定,这也是为什么 Go 语言官方推荐使用 strings.builder 进行字符串拼接的原因,在使用 strings.builder 时最好使用 Grow 方法进行初步的容量分配,观察 strings.join 方法的benchmark就可以发现,因为使用了 grow 方法,提前分配好内存,在字符串拼接的过程中,不需要进行字符串的拷贝,也不需要分配新的内存,这样使用 strings.builder 性能最好,且内存消耗最小。
  • bytes.Buffer 方法性能是低于 strings.builder 的, bytes.Buffer  转化为字符串时重新申请了一块空间,存放生成的字符串变量,不像 strings.buidler 这样直接将底层的 []byte 转换成了字符串类型返回,这就占用了更多的空间。

同步最后分析的结论:

无论什么情况下使用 strings.builder 进行字符串拼接都是最高效的,不过要主要使用方法,记得调用 grow 进行容量分配,才会高效。 strings.join 的性能约等于 strings.builder ,在已经字符串slice的时候可以使用,未知时不建议使用,构造切片也是有性能损耗的;如果进行少量的字符串拼接时,直接使用 + 操作符是最方便也是性能最高的,可以放弃 strings.builder 的使用。

综合对比性能排序:

1
strings.join  ≈ strings.builder > bytes.buffer > []byte转换string > "+" > fmt.sprintf

在使用 Qt 开发时,qDebug 打印包含中文的 QString 可能会出现乱码。这通常是由于字符编码不一致导致的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <QDebug>
#include <QTextCodec>
#include <Windows.h>

int main() {

//设置QTextCodec
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForLocale(codec);

// 将字符串转换为 UTF-8 编码,然后再传递给 qDebug 打印。
qDebug() << QString::fromUtf8(u8"你好,世界!");
return 0;
}

系统要求

  • 操作系统: Ubuntu 18.04 (Bionic Beaver)
  • ROS 版本: Melodic Morenia (官方长期支持版本)
  • Python 版本: 2.7.x (ROS Melodic 默认使用)

安装步骤

1. 配置软件源和密钥

1
2
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

2. 更新软件包列表

1
sudo apt update

3. 安装 ROS Melodic

完整桌面版 (推荐,包含 GUI 工具、仿真器和常用库):

1
sudo apt install ros-melodic-desktop-full

其他可选版本:

1
2
sudo apt install ros-melodic-desktop    # 基础桌面版(无仿真器)
sudo apt install ros-melodic-ros-base # 最小核心版(仅通信库和工具)

4. 初始化 rosdep

1
2
sudo rosdep init
rosdep update

5. 设置环境变量

1
2
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc

6. 安装构建工具和依赖

1
sudo apt install python-rosinstall python-rosinstall-generator python-wstool build-essential

7. 创建示例工作空间 (可选)

1
2
3
4
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make
source devel/setup.bash

验证安装

打开新终端,运行:

1
roscore

如果看到类似以下输出,说明安装成功:

1
2
3
4
5
6
7
... logging to /home/username/.ros/log/xxx/roslaunch-hostname-xxx.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://hostname:xxx/
ros_comm version 1.14.3

常见问题解决

1. 密钥获取失败

如果 apt-key adv 失败,可以手动下载并添加:

1
curl -sSL 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xC1CF6E31E6BADE8868B172B4F42ED6FBAB17C654' | sudo apt-key add -

2. 网络问题

如果下载速度慢,可以替换为国内镜像源(如清华源):

1
sudo sh -c '. /etc/lsb-release && echo "deb https://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ `lsb_release -cs` main" > /etc/apt/sources.list.d/ros-latest.list'

3. Python 版本验证

确保系统中已安装 Python 2.7:

1
python --version  # 应显示 Python 2.7.x

测试命令

安装完成后,可以使用以下命令测试 ROS 功能:

  • roscore - 启动 ROS master
  • rosrun roscpp_tutorials talker - 运行发布者节点
  • rosrun roscpp_tutorials listener - 运行订阅者节点

参考资源

0%