Chemmy's Blog

chengming0916@outlook.com

在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;
}

概述

Windows Server 2016默认情况下只允许一个用户通过远程桌面连接登录。通过配置远程桌面服务,可以实现多用户同时远程登录服务器,提高团队协作效率,避免用户争抢登录权限。

一、安装远程桌面服务

1.1 安装步骤

  1. 打开服务器管理器
  2. 点击添加角色和功能
  3. 在”服务器角色”页面,勾选远程桌面服务
  4. 继续选择远程桌面会话主机远程桌面授权
  5. 完成安装向导,重启服务器

二、配置组策略设置

2.1 打开组策略编辑器

  1. Win + R 打开运行对话框
  2. 输入 gpedit.msc 并回车

2.2 配置连接限制

  1. 导航到:计算机配置管理模板Windows组件远程桌面服务
  2. 双击打开 远程桌面会话主机连接
  3. 找到并双击 限制连接的数量
  4. 选择 已启用
  5. 设置 允许的RD最大连接数(建议根据服务器性能设置,如10-20个)
  6. 点击 确定 保存设置

三、配置远程桌面授权(破解120天限制)

3.1 激活远程桌面授权服务器

  1. 打开 服务器管理器工具远程桌面服务远程桌面授权管理器
  2. 右键点击服务器名称,选择 激活服务器
  3. 按照向导完成激活过程

3.2 安装许可证

  1. 在远程桌面授权管理器中,右键点击服务器
  2. 选择 安装许可证
  3. 按照向导选择合适的许可证类型

3.3 配置授权模式

  1. 打开组策略编辑器
  2. 导航到:计算机配置管理模板Windows组件远程桌面服务远程桌面会话主机授权
  3. 配置以下设置:
    • 设置远程桌面授权模式:选择 按用户按设备
    • 指定远程桌面授权服务器:输入许可证服务器的地址

四、其他重要配置

4.1 用户权限配置

  1. 打开 服务器管理器工具计算机管理
  2. 导航到:系统工具本地用户和组
  3. 双击 Remote Desktop Users
  4. 添加需要远程登录的用户或用户组

4.2 防火墙配置

确保防火墙允许远程桌面连接:

1
2
# 允许远程桌面连接
netsh advfirewall firewall add rule name="Remote Desktop" dir=in action=allow protocol=TCP localport=3389

4.3 性能优化

  1. 打开 系统属性高级性能设置
  2. 选择 调整为最佳性能 或根据需求自定义
  3. 在远程桌面会话主机配置中,可以调整:
    • 会话超时设置
    • 重定向设置
    • 客户端体验设置

五、验证配置

5.1 测试多用户登录

  1. 从不同的客户端计算机同时使用远程桌面连接
  2. 使用不同的用户账户登录
  3. 验证所有用户都能成功连接

5.2 检查连接状态

  1. 在服务器上打开 任务管理器
  2. 切换到 用户 选项卡
  3. 查看当前登录的用户数量

5.3 监控性能

  1. 使用 性能监视器 监控服务器资源使用情况
  2. 关注CPU、内存和网络使用率
  3. 根据监控结果调整连接数量限制

六、故障排除

6.1 常见问题及解决方案

问题1:用户无法远程登录

解决方案

  • 检查用户是否已添加到Remote Desktop Users组
  • 验证防火墙设置
  • 检查网络连通性

问题2:连接数量达到上限

解决方案

  • 增加连接数量限制
  • 检查是否有闲置会话未断开
  • 配置会话超时设置

问题3:120天试用期警告

解决方案

  • 确保远程桌面授权已正确激活
  • 安装有效的许可证
  • 配置授权服务器设置

6.2 诊断命令

1
2
3
4
5
6
7
8
# 查看远程桌面服务状态
Get-Service TermService

# 查看当前远程桌面会话
qwinsta

# 查看远程桌面授权状态
gpresult /h report.html

七、安全建议

7.1 访问控制

  1. 使用强密码策略
  2. 启用账户锁定策略
  3. 定期审计用户权限

7.2 网络安全

  1. 更改默认的RDP端口(3389)
  2. 使用VPN进行远程访问
  3. 启用网络级别身份验证(NLA)

7.3 监控与审计

  1. 启用安全审计日志
  2. 监控异常登录尝试
  3. 定期检查系统日志

八、最佳实践

8.1 容量规划

  • 根据服务器硬件配置合理设置最大连接数
  • 每个用户建议分配至少1GB内存
  • 监控性能指标,及时调整配置

8.2 维护计划

  • 定期更新操作系统和安全补丁
  • 备份重要配置和用户数据
  • 定期测试灾难恢复计划

8.3 用户管理

  • 建立清晰的用户权限管理制度
  • 定期清理不再需要的用户账户
  • 提供用户培训,确保正确使用远程桌面

总结

通过以上配置,Windows Server 2016可以支持多用户同时远程登录,显著提高团队协作效率。关键配置步骤包括:

  1. 安装远程桌面服务:包括远程桌面会话主机和授权服务
  2. 配置组策略:设置最大连接数和其他相关参数
  3. 激活授权服务:破解120天试用期限制
  4. 优化性能和安全:根据实际需求调整配置

建议在生产环境中部署前进行充分测试,确保配置满足业务需求和安全要求。定期维护和监控是保证系统稳定运行的重要保障。

系统要求

  • 操作系统: 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 - 运行订阅者节点

参考资源

概述

LIO-SAM (Lidar Inertial Odometry and Mapping) 是一个紧耦合的激光雷达惯性里程计框架,集成了 IMU 预积分和 GPS 数据,适用于机器人建图和定位。

环境要求

组件 版本 下载地址
Ubuntu 18.04+ -
ROS Melodic -
gtsam 4.0.2 GitHub
Eigen 3.3.7 GitLab
LIO-SAM 最新版 GitHub

安装步骤

1. 安装系统依赖

1
2
3
4
5
# 更新系统包列表
sudo apt update

# 安装必要的开发工具和依赖库
sudo apt install -y build-essential cmake libboost-all-dev

2. 安装 Eigen 库

1
2
3
4
5
6
7
8
9
10
11
12
# 下载并解压 Eigen
wget https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.tar.gz
tar -zxvf eigen-3.3.7.tar.gz

# 编译安装
cd eigen-3.3.7
mkdir build && cd build
sudo cmake ..
sudo make install

# 创建符号链接以便系统找到 Eigen 头文件
sudo cp -r /usr/local/include/eigen3/Eigen/ /usr/local/include/

3. 安装 ROS Melodic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 设置 ROS 软件源(清华镜像)
sudo sh -c '. /etc/lsb-release && echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ $DISTRIB_CODENAME main" > /etc/apt/sources.list.d/ros-latest.list'

# 添加 ROS 密钥
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F42ED6FBAB17C654

# 更新包列表并安装 ROS
sudo apt update
sudo apt-get install -y ros-melodic-desktop-full

# 安装 ROS 开发工具
sudo apt-get install -y ros-melodic-rqt* python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential

# 初始化 rosdep
sudo rosdep init
rosdep update

4. 配置 ROS 环境

1
2
3
# 将 ROS 环境变量添加到 bashrc
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc

5. 安装 gtsam

1
2
3
4
5
6
7
8
9
10
11
12
# 克隆 gtsam 仓库
git clone https://github.com/borglab/gtsam.git
cd gtsam

# 切换到 4.0.2 版本
git checkout 4.0.2

# 编译安装
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install

6. 安装 LIO-SAM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src

# 克隆 LIO-SAM
git clone https://github.com/TixiaoShan/LIO-SAM.git

# 安装依赖
cd ~/catkin_ws
rosdep install --from-paths src --ignore-src -y

# 编译
catkin_make -j$(nproc)

# 设置工作空间环境
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc

验证安装

测试 ROS 安装

1
2
3
4
5
# 启动 ROS 核心
roscore

# 新终端中检查 ROS 环境
echo $ROS_PACKAGE_PATH

测试 LIO-SAM 编译

1
2
3
4
5
# 检查 LIO-SAM 包是否存在
rospack find lio_sam

# 尝试运行节点(需要相应的启动文件)
roslaunch lio_sam run.launch

常见问题解决

1. Eigen 头文件找不到

问题:编译时出现 fatal error: Eigen/Dense: No such file or directory

解决方案

1
2
# 确保 Eigen 头文件在正确位置
sudo ln -s /usr/local/include/eigen3/Eigen /usr/local/include/Eigen

2. gtsam 版本不兼容

问题:需要特定版本的 gtsam

解决方案

1
2
3
# 确保使用 gtsam 4.0.2 版本
cd gtsam
git checkout 4.0.2

3. ROS 依赖问题

问题:缺少 ROS 包依赖

解决方案

1
2
# 安装所有缺失的依赖
rosdep install --from-paths src --ignore-src -y

使用说明

启动 LIO-SAM

1
2
3
4
5
# 启动 LIO-SAM 主要节点
roslaunch lio_sam run.launch

# 启动可视化工具
roslaunch lio_sam visualization.launch

数据播放

1
2
# 播放 bag 文件
rosbag play your_data.bag

目录结构

1
2
3
4
5
6
7
~/catkin_ws/
└── src/
└── LIO-SAM/
├── config/ # 配置文件
├── launch/ # 启动文件
├── src/ # 源代码
└── package.xml # ROS 包配置

注意事项

  1. 版本匹配:确保所有组件的版本兼容性
  2. 内存要求:编译过程需要足够的内存,建议 8GB+ RAM
  3. 网络连接:下载依赖需要稳定的网络连接
  4. 权限问题:某些操作需要 sudo 权限

后续步骤

  1. 配置参数:根据你的传感器调整 config/params.yaml
  2. 数据采集:使用你的传感器采集数据
  3. 性能调优:根据实际场景调整算法参数

参考资源

一、什么是 NVIDIA-SMI?

nvidia-smi 是 NVIDIA 的系统管理界面(System Management Interface),用于监控和管理 NVIDIA GPU 设备。它是 NVIDIA 驱动安装包的一部分,无需额外安装。

核心功能

  • 监控:实时查看 GPU 状态、温度、功耗、显存使用率、进程占用等。
  • 管理:修改 GPU 配置选项,如启用/禁用 ECC 内存、设置持久化模式、调整计算模式等。
  • 诊断:查询设备拓扑、时钟速度、支持的功能等。

支持的产品

  • 完全支持:所有 Tesla(Fermi 架构起)、Quadro(Fermi 架构起)、GRID(Kepler 架构起)、GeForce Titan(Kepler 架构起)产品。
  • 有限支持:所有 GeForce 产品(Fermi 架构起)。

二、基础信息查询命令

2.1 查看 GPU 概览信息

命令nvidia-smi
这是最常用的命令,显示所有 GPU 的实时状态摘要。

输出字段详解

字段 含义
GPU 本机中的 GPU 编号(从 0 开始)
Name GPU 型号(如 Tesla V100)
Fan 风扇转速(百分比)
Temp GPU 核心温度(摄氏度)
Perf 性能状态(P0-P12,P0 为最高性能)
Pwr:Usage/Cap 当前功耗 / 最大设计功耗
Bus-Id PCI 总线 ID(格式:domain:bus:device.function
Disp.A Display Active,显示是否初始化
Memory-Usage 显存使用情况(已用 / 总量)
Volatile GPU-Util GPU 计算单元利用率(百分比)
Uncorr. ECC 未纠正的 ECC 错误计数
Compute M. 计算模式(Default, Exclusive_Process 等)
Processes 显示占用该 GPU 的进程及其显存使用量

2.2 列出所有 GPU 设备

命令nvidia-smi -L
以简洁格式列出系统中所有 NVIDIA GPU 设备。

1
2
GPU 0: Tesla V100-SXM2-32GB (UUID: GPU-xxxxxx)
GPU 1: Tesla V100-SXM2-32GB (UUID: GPU-yyyyyy)

2.3 查看系统拓扑

命令nvidia-smi topo --matrix
显示 GPU 之间以及 GPU 与 CPU、其他 PCIe 设备(如 InfiniBand 网卡)的互连拓扑。这对于配置 GPUDirect 等高级功能至关重要。


三、高级查询与监控

3.1 查询详细 GPU 信息

命令nvidia-smi -q
显示指定 GPU(或所有 GPU)的极其详细的信息报告。可以通过 -i <gpu_id> 指定 GPU。

报告包含的主要部分

  • GPU 基本信息:产品名称、序列号、UUID、VBIOS 版本等。
  • PCI 信息:总线 ID、带宽、链路宽度等。
  • 时钟信息:图形时钟、内存时钟、SM 时钟的当前值、默认值和最大值。
  • 温度与功耗:当前温度、功耗限制、电源管理状态。
  • 显存信息:总显存、已用显存、ECC 模式、BAR1 内存使用情况。
  • ECC 错误:易失性和聚合性 ECC 错误计数。
  • 计算进程:当前正在使用 GPU 的计算进程列表。

3.2 查询时钟信息

  • 查看当前时钟nvidia-smi -q -d CLOCK
    显示 GPU 的当前图形、内存和 SM 时钟速度,以及默认和最大时钟。

  • 查看支持的时钟列表nvidia-smi -q -d SUPPORTED_CLOCKS
    列出 GPU 支持的所有可用时钟速度组合。

3.3 虚拟 GPU (vGPU) 状态查询

命令nvidia-smi vgpu
查看虚拟化环境中 vGPU 的状态信息。如果系统未启用 vGPU,会提示不支持。

动态监控 vGPU 进程nvidia-smi vgpu -p
以滚动刷新的方式循环显示虚拟桌面中应用程序对 GPU 资源的占用情况。


四、设备管理与配置

4.1 设置持久化模式

命令nvidia-smi -pm <0/1>

  • 0DISABLED:禁用持久化模式(默认)。GPU 驱动在最后一个客户端断开连接后会卸载。
  • 1ENABLED:启用持久化模式。驱动始终保持加载状态,减少后续应用启动的延迟。
    1
    2
    # 为 GPU 0 启用持久化模式
    nvidia-smi -i 0 -pm 1

4.2 配置 ECC 内存

  • 启用/禁用 ECCnvidia-smi -e <0/1>
    • 0DISABLED:禁用 ECC。
    • 1ENABLED:启用 ECC(需要重启生效)。
  • 重置 ECC 错误计数nvidia-smi -p <0/1>
    • 0VOLATILE:重置易失性错误计数。
    • 1AGGREGATE:重置聚合性错误计数。

4.3 设置计算模式

命令nvidia-smi -c <MODE>
控制 GPU 如何被计算应用程序共享。

模式值 模式名称 描述
0 DEFAULT 多个计算应用可以共享 GPU(默认)。
1 EXCLUSIVE_THREAD 已弃用。
2 PROHIBITED 禁止任何计算应用使用 GPU。
3 EXCLUSIVE_PROCESS 一个计算进程可以独占 GPU,该进程内的多个线程可共享。
1
2
# 将 GPU 0 设置为独占进程模式
nvidia-smi -i 0 -c 3

4.4 重置 GPU

命令nvidia-smi -r
在 GPU 无响应或状态异常时,尝试触发 GPU 复位。此操作可能导致正在运行的进程崩溃,请谨慎使用。


五、实用参数与监控技巧

5.1 周期性监控

  • 按秒间隔循环刷新nvidia-smi -l <秒数>
    例如 nvidia-smi -l 2 每 2 秒刷新一次显示。
  • 按毫秒间隔循环刷新nvidia-smi -lms <毫秒数>
    例如 nvidia-smi -lms 500 每 500 毫秒刷新一次。

5.2 输出到文件

使用 -f--filename 参数将输出重定向到文件,便于后续分析。

1
2
3
4
# 将详细查询结果保存到文件
nvidia-smi -q -f gpu_info.log
# 周期性监控并记录到文件
nvidia-smi -l 1 -f monitor.log

5.3 指定目标 GPU

使用 -i--id 参数对特定的 GPU 进行操作。

1
2
3
4
# 仅查看 GPU 1 的详细信息
nvidia-smi -q -i 1
# 仅监控 GPU 0 和 GPU 2
nvidia-smi -i 0,2 -l 1

5.4 以 XML 格式输出

使用 -x--xml-format 参数,便于脚本解析。

1
nvidia-smi -q -x

六、命令参数速查表

参数类别 参数 功能描述
帮助 -h, --help 打印帮助信息。
列表 -L, --list-gpus 列出所有连接的 GPU。
查询 -q, --query 查询 GPU 详细信息。
-d, --display= 选择性显示信息(如 MEMORY, CLOCK)。
目标 -i, --id= 指定目标 GPU ID(如 0, 0,2)。
输出 -f, --filename= 将输出保存到文件。
-x, --xml-format 以 XML 格式输出。
监控 -l, --loop= 按指定秒数间隔循环刷新。
-lms, --loop-ms= 按指定毫秒数间隔循环刷新。
管理 -pm, --persistence-mode= 设置持久化模式 (0/1)。
-e, --ecc-config= 启用/禁用 ECC (0/1)。
-c, --compute-mode= 设置计算模式 (0-3)。
-r, --gpu-reset 触发 GPU 复位。

七、总结与最佳实践

nvidia-smi 是管理和监控 NVIDIA GPU 不可或缺的工具。掌握其核心用法:

  1. 日常监控:使用 nvidia-sminvidia-smi -l 1 实时查看 GPU 状态。
  2. 问题诊断:使用 nvidia-smi -q 获取详细报告,分析温度、功耗、ECC 错误和进程占用。
  3. 性能调优:通过 -pm-c 等参数调整 GPU 运行模式以适应不同工作负载。
  4. 自动化脚本:利用 -f-x 参数将输出重定向或格式化为 XML,便于集成到监控系统中。

官方资源

准备

建议准备一个干净、换好源的 Ubuntu 16.04 及以上版本(建议 清华源 ),本教程也适用其他 ROS1版本。

查看ubuntu 版本

1
lsb_release -a

根据自己的 Ubuntu 的版本选择 ROS 版本 (示例是 Ubuntu 18.04 所以对应ROS版本为 melodic
![[Ubuntu部署ROS/IMG-20250829234441526.png]]

ROS安装

1. 安装源

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

2. 设置密钥

1
2
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

3. 安装

1
2
3
4
sudo apt update
sudo apt install ros-melodic-desktop
# 其他版本替换对应的版本(例如 noetic )
# sudo apt install ros-noetic-desktop

4. 配置环境变量

1
2
3
4
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc #使环境生效
# 替换对应版本同上
# echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc

5. 配置rosdep

在使用许多 ROS 工具之前,需要初始化 rosdep,有些功能包源码编译需要rosdep 来安装这些系统依赖项,不配置也不影响ros使用,所以后面需要时再来配置也可以。 rosdep 请求的文件都放在 github 上的, 推荐使用代理。

1
2
3
4
5
6
7
8
9
10

# 安装依赖
sudo apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential
# 对于Ubuntu20
# sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential

# 初始化
sudo rosdep init
rosdep update

测试

1
2
3
roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key

参考
官方文档(melodic)
ubuntu18.04安装ROS Melodic(最详细配置)-CSDN博客
基于Ubuntu18.04的ROS Melodic环境详细配置(含各种大坑及填坑)
[ROS 系列学习教程] ROS与操作系统版本对应关系_ros版本-CSDN博客

0%