文章标题

Qt项目转换VS2022工程问题的小问题

在将 Qt 项目转换为 Visual Studio 工程时,可能会遇到 CMake 相关的报错问题,今天就来分享下对应的解决办法。

一、问题原因

由于在同一个构建目录中使用了不同的 CMake 生成器(比如从 “Visual Studio 16 2019” 切换到 “Visual Studio 17 2022”),导致 CMake 无法识别新的生成器,进而出现报错。

二、解决方法

(一)方法一:删除并重新创建构建目录

  1. 返回到项目根目录
    打开“Developer Command Prompt for VS 2022”,执行命令:
1
cd /d D:\QTproject\new_pro\samp6_2EventAndSignal
  1. 删除现有的 build_vs 目录
    在项目根目录下运行:
1
rmdir /s /q build_vs

注意此命令会永久删除该目录及其所有内容,确认无重要文件需保留。
3. 重新创建新的构建目录
可以使用不同目录名如 build_vs2022

1
2
mkdir build_vs2022
cd build_vs2022
  1. 运行 CMake 命令,指定正确的生成器
1
cmake -G "Visual Studio 17 2022" -A x64 -DQt6_DIR=D:\Qt\6.5.3\msvc2019_64\lib\cmake\Qt6..

各参数含义如下:

  • -G "Visual Studio 17 2022":指定生成器为 Visual Studio 2022。
  • -A x64:指定生成平台为 64 位。
  • -DQt6_DIR=...:指定 Qt 的 CMake 配置路径。
  • ..:表示 CMakeLists.txt 位于上一级目录。
  1. 编译解决方案(可选)
    • 使用 msbuild 编译
1
msbuild samp6_2EventAndSignal.sln /p:Configuration=Release /m:4

参数说明:

  • /p:Configuration=Release:指定编译配置为 Release,如需 Debug 版本则改为 /p:Configuration=Debug
  • /m:4:用 4 个并行线程编译加快速度。
    • 在 Visual Studio 中打开并编译
      1. 打开文件资源管理器,导航到 D:\QTproject\new_pro\samp6_2EventAndSignal\build_vs2022 目录。
      2. 双击 samp6_2EventAndSignal.sln 文件,用 Visual Studio 2022 打开。
      3. 在 Visual Studio 中进行开发、调试和编译操作。

(二)方法二:在现有构建目录中删除 CMake 缓存

  1. 导航到 build_vs 目录
1
cd /d D:\QTproject\new_pro\samp6_2EventAndSignal\build_vs
  1. 删除 CMake 缓存文件和生成目录
1
2
del /f /q CMakeCache.txt
rmdir /s /q CMakeFiles
  1. 重新运行 CMake 命令,指定正确的生成器
1
cmake -G "Visual Studio 17 2022" -A x64 -DQt6_DIR=D:\Qt\6.5.3\msvc2019_64\lib\cmake\Qt6..

三、其他注意事项

  1. 确保使用正确的命令提示符
    要使用“Developer Command Prompt for VS 2022”,可在开始菜单搜索后右键选择“以管理员身份运行”。
  2. 确认 Visual Studio 2022 安装了必要的组件
    打开 Visual Studio Installer,找到 Visual Studio 2022 点击“修改”,在“工作负载”选项卡确保选中“使用 C++ 的桌面开发”,在“单个组件”选项卡确认安装如“MSVC v143 - VS 2022 C++ x64/x86 build tools”等相关组件,缺少则勾选安装。
  3. 确认 CMake 版本
    建议使用 3.21 及以上版本,可用命令 cmake --version 查看,版本过旧需下载安装最新版并添加 bin 目录到系统 Path 环境变量。
  4. 确认 Qt6_DIR 路径正确
    确认 D:\Qt\6.5.3\msvc2019_64\lib\cmake\Qt6 目录存在且包含 Qt6Config.cmake 文件。

四、备用方案

若命令行方法不行,可尝试使用 Visual Studio 的图形界面来打开并配置 CMake 项目:

  1. 打开 Visual Studio 2022。
  2. 选择“打开本地文件夹”,导航到 D:\QTproject\new_pro\samp6_2EventAndSignal
  3. Visual Studio 会自动检测 CMakeLists.txt 并加载项目。
  4. 在 Visual Studio 中配置并生成项目。

懒汉模式与饿汉模式

懒汉模式与饿汉模式

懒汉模式:获取单例时才初始化

线程不安全:
两个线程同时判断 instance 为空,创建了两个 instance 实例,不符合单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};

Singleton* Singleton::instance = nullptr;

解决方法:
添加锁,实例还未创建时枷锁,创建实例前需要再判断一次。可能一个线程已经在创建实例了,确保不会因为枷锁期间多个线程同时进入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <mutex>

class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
Singleton() {}
public:
static Singleton* getInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}
};

Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

饿汉模式:在类一加载时就初始化了 instance 这个静态变量

(在编译时就可以被初始化), 不用判断是否存在,肯定已经存在,直接返回 instance

1
2
3
4
5
6
7
8
9
10
11
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
static Singleton* getInstance() {
return instance;
}
};

Singleton* Singleton::instance = new Singleton();

VS上qt配置的若干问题

一、“There’s no Qt version assigned to project WBoard.vcxproj for configuration Debug/x64”错误解决办法

在使用 Qt 与 Visual Studio 项目时,若遇到 Debug/x64 配置中没有指定 Qt 版本出现上述错误,可按以下步骤解决:

  1. 确保正确安装了 Qt
    • 要保证系统中已正确安装 Qt,并且在 Visual Studio 中选择了正确的 Qt 版本。可通过 Qt Maintenance Tool 或从 Qt 官网下载并安装 Qt。
  2. 在 Visual Studio 中配置 Qt
    • 打开项目(WBoard.vcxproj)。
    • 选择“工具”->“选项”。
    • 在“项目和解决方案”下,选择“Qt 项目设置”。
    • 确保为项目选择了正确的 Qt 版本。若没列出任何版本,可能需要手动添加,指定 Qt 安装路径。
  3. 为项目分配 Qt 版本
    • 右键点击项目(WBoard.vcxproj),然后选择“属性”。
    • 在“配置属性”->“Qt 项目”下,确保“Qt 版本”字段正确设置为已安装的有效 Qt 版本。
  4. 设置 Qt Kits
    • 若使用的是 Qt Creator 或手动管理 kits,确保在 Qt 设置中为 Debug/x64 配置了正确的 kit(匹配平台和编译器)。
  5. 重新构建项目
    • 配置好 Qt 版本后,重新构建项目,检查问题是否解决。

若按上述步骤操作后问题依然存在,尝试重新安装 Qt Visual Studio Tools,或检查系统中 Qt 的环境变量配置。

二、VS2022 中检查 Qt Version 配置与项目属性是否一致的方法

  1. 检查 Qt 版本配置

    • 打开 Visual Studio 2022,并确保已安装 Qt Visual Studio Tools 插件。
    • 打开 Qt 项目设置:
      • 在菜单栏中,选择“工具(Tools)”->“选项(Options)”。
      • 在左侧面板中,找到“Qt Project Settings”或类似选项(根据安装插件,Qt 项目设置可能在“Projects and Solutions”或单独的 Qt 选项卡下)。
    • 检查 Qt 版本:
      • 在“Qt Project Settings”中,查看“Qt Versions”,这里列出已配置的 Qt 版本。确保已选择正确版本,且该版本指向安装的 Qt 路径。
      • 若 Qt 版本没配置或者未显示想要的版本,可点击“添加(Add)”按钮,选择正确的 Qt 安装路径。
  2. 检查项目属性

    • 打开项目属性:
      • 在解决方案资源管理器中,右键点击项目(例如 WBoard.vcxproj),然后选择“属性(Properties)”。
    • 检查 Qt 版本是否一致:
      • 在项目属性窗口中,选择“配置属性”->“Qt 项目(Qt Project)”。
      • 在“Qt 版本”字段,确保它与上述配置中的 Qt 版本一致,若不一致,手动选择正确的 Qt 版本。
  3. 检查 Qt 模块

    • 在“Qt 项目”设置中,查看“Qt Modules”。
    • 确保项目使用了正确的 Qt 模块(例如 QtCore, QtWidgets, QtGui 等),且这些模块与选择的 Qt 版本兼容。
  4. 重新生成项目

    • 选择顶部菜单栏中的“生成(Build)”。
    • 点击“重新生成解决方案(Rebuild Solution)”以确保所有配置被正确应用。
  5. 检查输出日志
    在重新生成项目时,查看输出窗口中的日志,看是否有关于 Qt 配置的错误信息,若 Qt 版本配置不正确,通常会看到类似“No Qt version assigned”或“Qt version mismatch”的错误信息。

三、Visual Studio Installer 中安装 MSVC v142 工具链的步骤及相关事项

  1. 详细步骤说明

    • 打开 Visual Studio Installer
      • 在 Windows 搜索栏中输入“Visual Studio Installer”,点击打开。
    • 选择要修改的 VS2022 版本
      • 在安装器中找到已安装的“Visual Studio 2022”,点击右侧的“修改”按钮。
    • 切换到“单个组件”标签页
      • 在弹出的界面中,顶部有多个标签页(工作负载、单个组件、语言包等),点击“单个组件”。
    • 搜索并勾选 MSVC v142 工具链
      • 在搜索框中输入“v142”,找到以下组件并勾选:
        • “MSVC v142 - VS2019 C++ x64/x86 生成工具(最新)”(确保勾选的是针对 VS2019 的 v142 版本,而非 v143)。
    • 完成修改并安装
      • 点击右下角的“修改”按钮,等待安装完成。
  2. 注意事项

    • 权限问题
      • 如果提示需要管理员权限,请以管理员身份运行 Visual Studio Installer。
    • 组件名称可能随版本变化
      • 如果搜索不到“v142”,尝试关键词“VS2019”或“C++ 2019”。
    • 工具链路径验证
      • 安装完成后,工具链默认路径为:“C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\”(路径中的版本号可能不同)。
  3. 为什么需要安装 MSVC v142?

    • 若使用的 Qt 版本是“Qt 5.15.2 (msvc2019)”,它依赖的是 VS2019 的编译器(MSVC v142)。
    • VS2022 默认安装的是“MSVC v143”编译器,两者不兼容,通过安装 v142 工具链,可以让 VS2022 兼容旧版 Qt。
  4. 验证是否安装成功

    • 打开 VS2022,创建一个空的 C++ 控制台项目。
    • 右键项目 >“属性”>“配置属性”>“常规”,检查“平台工具集”是否包含“Visual Studio 2019 (v142)”。

WSL2的网络问题

Ubuntu APT Update 卡住的排查与解决方案

背景

最近在使用 Ubuntu 进行 sudo apt-get update 时,发现更新一直卡在 Connecting to 172.23.153.121,并最终超时。起初以为是网络问题,但 ping archive.ubuntu.comping security.ubuntu.com 都能返回数据,说明基础网络是通的。

深入排查后,发现 APT 可能被配置了错误的代理或镜像源,导致更新请求被错误地重定向到 172.23.153.121,最终导致超时。以下是详细的排查过程和解决方案。


排查过程

1. 检查 APT 源是否正确

首先查看 /etc/apt/sources.list,确认是否存在错误的源配置:

1
cat /etc/apt/sources.list

我的 sources.list 内容如下:

1
2
3
deb http://archive.ubuntu.com/ubuntu focal main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu focal-security main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu focal-updates main restricted universe multiverse

看起来没有问题,说明问题不是由于源地址错误引起的。


2. 检查 APT 代理设置

APT 可能被配置了一个错误的代理,导致所有请求都被重定向。检查 apt.conf.d 目录:

1
grep -r "172.23.153.121" /etc/apt/apt.conf.d/

果然,发现了类似的代理配置:

1
/etc/apt/apt.conf.d/00proxy:Acquire::http::Proxy "http://172.23.153.121:80/";

解决方案:删除或修改该文件

1
sudo rm /etc/apt/apt.conf.d/00proxy

或者手动编辑:

1
sudo nano /etc/apt/apt.conf.d/00proxy

删除 Acquire::http::Proxy 相关的行,保存退出。


3. 检查环境变量中的代理

如果 apt.conf.d 没有问题,可能是系统环境变量导致的。执行:

1
env | grep -i proxy

如果返回:

1
2
http_proxy=http://172.23.153.121:80
https_proxy=http://172.23.153.121:80

那么需要取消代理:

1
2
unset http_proxy
unset https_proxy

并检查 bash 配置文件:

1
grep -i proxy ~/.bashrc ~/.bash_profile ~/.profile /etc/environment /etc/bash.bashrc /etc/profile

如果发现 export http_proxyexport https_proxy,需要手动编辑相应文件删除,并执行:

1
source /etc/environment

4. 清除 APT 缓存并重试

清除旧的 APT 数据,防止缓存影响:

1
2
3
sudo rm -rf /var/lib/apt/lists/*
sudo apt-get clean
sudo apt-get update

5. 检查 DNS 设置

如果问题依然存在,可能是 DNS 解析问题。查看 DNS 配置:

1
cat /etc/resolv.conf

如果 DNS 服务器不是 8.8.8.88.8.4.4,可以手动修改:

1
sudo nano /etc/resolv.conf

添加:

1
2
nameserver 8.8.8.8
nameserver 8.8.4.4

然后再次运行:

1
sudo apt-get update

6. 强制 APT 使用 IPv4

如果仍然超时,尝试强制 APT 仅使用 IPv4:

1
sudo apt-get update -o Acquire::ForceIPv4=true

7. 更换国内镜像源(可选)

如果你在国内,使用官方的 archive.ubuntu.com 可能会较慢,可以更换为阿里云或清华源。

编辑 /etc/apt/sources.list

1
sudo nano /etc/apt/sources.list

替换为:

1
2
3
deb http://mirrors.aliyun.com/ubuntu focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu focal-updates main restricted universe multiverse

然后更新:

1
sudo apt-get update

总结

  1. 检查 APT 代理配置 (/etc/apt/apt.conf.d/),删除错误的代理设置。
  2. 检查系统环境变量 (env | grep -i proxy),删除不必要的代理。
  3. 清理 APT 缓存 (sudo rm -rf /var/lib/apt/lists/* && sudo apt-get clean)。
  4. 修复 DNS 设置 (sudo nano /etc/resolv.conf,改成 8.8.8.8)。
  5. 强制使用 IPv4 (sudo apt-get update -o Acquire::ForceIPv4=true)。
  6. 更换国内镜像源(阿里云、清华等)。

文章标题

先分配内存空间
初始化消息队列
系统自动为控制块方培内存空间

如存储位置,头指针,为指针,消息大小,队列长度,
消息队列和消息空间在同一段内存空间中,创建水分配了消息空间呵消息队列的容量,无法更改。

发送一般消息:

若队列未满或者允许覆盖,将信息拷贝到队尾。否则,根据指定的超时时间阻塞。
若等到队列未满,或者超过了阻塞时间,则从阻塞状态转换为就绪状态,后者会收到错误码。

发送紧急消息,消息会被复制到消息队列队头。

  • 读取消息队列

    如果队列为空,任务保持阻塞状态。队列不空时,自动转变为就绪态。超过了阻塞时间自动转变为就绪态。

  • 在中断总发消息不允许带有阻塞机制

    中断必须快速响应和硬件中断
    任务调度器不会在中断中运行
    高优先级中断可能要更快地执行

如果在中断过程中更高优先级的任务被唤醒,中断返回后会返回更高优先级的任务。
消息队列创建:创建一个消息队列返回这个队列的队列句柄。
每创建一个新的队列需要对其分配RAM,存储状态和队列信息。
xQueueCreate():
使用动态内存分配,需将configSUPPORT_DYNAMIC_ALLOCATION 置1,(FreeRTOSConfig.h)
xQueueCreateStatic() :静态内存分配

prvInitialiseNewQueue()

参数:消息队列长度,单个消息大小,存储消息起始地址
消息队列类型
未分配存储消息的内存空间: 单个消息大小为0时将pchead指向队列控制块 pxNewQueue,因为消息队列用作互斥量时pchead设置为NULL。
分配了存储消息的内存空间: pcHead 指向存储消息的
起始地址 pucQueueStorage。
初始化消息队列控制块->重置消息队列

重置消息队列 xQueueGenericReset()

进入临界段是为了确保多任务并发环境中的数据一致性
需要恢复发送阻塞消息队列,便于数据发送

队列创建

队列创建时也需要进入临界区:临界区锁定的中断屏蔽
内存分配和指针初始化不能被打断,被错误的初始化或访问。导致内存访问错误或者队列崩溃。

通用消息队列发送函数 xQueueGenericSend()

队列未满-》添加信息

恢复等待消息阻塞的任务

  1. 若恢复的任务优先级比当前任务高-》切换任务
  2. 如果没有等待的任务,拷贝成功也需要任务切换

队列已满-》

不指定阻塞超时时间,退出,返回错误
初始化阻塞超时结构体变量,初始化进入

结束之后

检查是否超时
如果队列还是满的-》添加任务到等待发送列表中和

接受信息xQueueGenericReceive()

从队列中接收消息并把消息从队列中删除
接收的信息以拷贝的形式存在,必须提供一个足够大的缓存区(中断时必须使用特定的函数)

设置阻塞时间,若队列为空,任务保持阻塞状态。若等待时间超过两万指定的阻塞时间,直接从阻塞状态转化成就绪态。

挂起任务调度器和进入临界区

在将任务设置为阻塞的过程中,挂起调度器并进入临界区
挂起调度器意味着任务不能切换并且不准调用可能引起任务切换的 API 函数。 但挂起调度器并不会禁止中断,中断服务函数
仍然可以操作队列事件列表,可能会解除任务阻塞、可能会进行上下文切换,这也是不允许的。解决办法是还要给队列上锁,禁止任何中断来操作队列

假设有一个高优先级任务 A 和一个低优先级任务 B。任务 A 想入队,但队列已满,任务 A 将进入阻塞状态,并等待队列空闲。如果在任务 A 被正确地阻塞并添加到等待列表之前,低优先级任务 B 或者中断修改了队列的状态,它可能抢先完成入队操作

互斥量

递归获取信号量时可能出现死锁的情况:

  • 信号量用完:
  • 阻塞 vs 死锁:

    单次获取信号量且信号量不可用,任务会进入阻塞状态,此时系统能够恢复正常运行。然而,如果任务递归获取信号量,并且没有释放信号量,导致信号量完全用尽,那么任务自身将无法继续执行,且也无法释放之前获取的信号量,这就形成了死锁。此时,没有其他任务可以释放信号量,系统无法恢复,导致死锁。

使用临界区资源的方法:
获取并释放信号量
xSemaphoreTake()xSemaphoreGive()
挂起任务调度器 vTaskSuspendAll();xTaskResumeAll();
禁用中断
taskENTER_CRITICAL()和 taskEXIT_CRITICAL()

FreeRTOS中的任务实现

任务函数的调用

  1. 硬件初始化
  2. 初始化与任务相关的列表
  3. 创建任务 xTaskCreateStatic
  4. 添加任务到就绪列表
  5. 启动调度器
  6. for 循环让任务死循环

实现具体的函数

任务的定义与任务切换的实现

实现任务创建函数

任务的栈,任务的函数实体,实现任务控制块的创建和联系。

静态创建,configSUPPORT_STATIC_ALLOCATIONFreeRTOSConfig.h 中定义,配置为 1。(静态创建)

xTaskCreateStatic() 函数

1
2
3
4
5
6
7
8
TaskHandle_t xTaskCreateStatic(
TaskFunction_t xx, // 任务入口
const char * const xx, // 任务名称,字符串形式
const uint32_t xx, // 任务栈大小
void * const xx, // 任务形参
StackType_t * const xx, // 任务句柄
TCB_t * const xx // 任务控制块
);

静态创建任务,内嵌 prvInitialiseNewTask 函数。

  • TaskFunction_t:任务函数本身(在 projdefs.h 中重定义的空指针)。任务运行时执行此函数,任务函数必须符合 void* 类型。
  • 运行传参:将函数指针传递给调度器,许多任务的具体实现不同,调度器运行时只需调用不同的任务函数,编译不需要硬编码不同的函数,否则调度器需要知道函数的具体实现,将函数的实现拷贝到调度器中。指针容易被赋值。
  • tskTaskControlBlock:任务的控制块,任务的状态和各种与调度相关的信息,是 FreeRTOS 调度器用来管理任务的核心结构 (tcb_t)。

TCB 并不直接暴露给用户,而是通过 TaskHandle_t 来间接访问

  • TaskHandle_t:任务句柄,在任务创建时获取,提供给用户,方便任务的挂起、恢复、删除等操作 (tcb_t)。
  • TaskFunction_tvoid* 类型,任务函数类型。

prvInitialiseNewTask() 函数

用于初始化新任务的 TCB 和相关数据结构。

参数设置

  • 任务入口
  • 任务名称
  • 任务栈大小
  • 任务形参
  • 任务句柄
  • 任务控制块指针

获取栈顶地址

(传入的 任务控制块指针(此函数参数)内部的 栈起始地址 加上 栈大小(此函数参数)减 1 作为栈顶地址(被重定义过的整型变量))

  • 向下做 8 字节对齐

将任务的名字存储在 TCB

  • 字符串长度小于 configMAX_TASK_NAME_LEN 的情况下递增循环
  • 储存在 xTaskCreateStatic() 函数中控制块索引指针中
  • 若出现 0x00(即以 '/0' 结尾则退出)
  • 强行给最大长度的前一位置 0

初始化 TCB 中的 xStateListItem (任务节点)

  • 使用 vListInitialiseItem 函数初始化链表项 xStateListItem
  • 使用 listSET_LIST_ITEM_OWNER 配置 xStateListItem 的拥有者项

调用函数初始化任务栈,并更新栈顶指针

  • 调用 pxPortInitialiseStack() 函数初始化任务栈,并更新栈顶指针
  • 任务句柄指向任务控制块

pxPortInitialiseStack() 函数

它的主要作用是为新创建的任务设置初始的栈状态(上文初始化栈状态时用到),以便任务能够正确地开始执行。确保栈的顶部包含正确的初始值。(存储的内容在异常发生时会自动加载到 CPU)

参数

1
2
3
StackType_t *pxTopOfStack,  // 栈顶指针
TaskFunction_t pxCode, // 任务入口函数
void *pvParameters // 任务形参

初始化栈

  • xPSRbit24 必须置 1(thumb 模式)
  • 任务的入口地址(这个函数在内存中的地址)

    当任务被创建时,FreeRTOS 会将任务入口地址(即任务函数的地址)保存到任务栈中,以便在任务切换时能够恢复这个地址并开始执行任务。

  • 任务的返回地址

    它是在任务执行过程中保存的,用于在任务切换时恢复任务的执行状态。不过 FreeRTOS 会将 PC 设置为 prvTaskExitError,而不是返回到任务的调用位置。这是为了确保任务结束后不会继续执行不应执行的代码。
    FreeRTOS 中的任务函数通常是一个无限循环。

  • R12, R3, R2R1 默认初始化为 0