文章标题
Qt项目转换VS2022工程问题的小问题
在将 Qt 项目转换为 Visual Studio 工程时,可能会遇到 CMake 相关的报错问题,今天就来分享下对应的解决办法。
一、问题原因
由于在同一个构建目录中使用了不同的 CMake 生成器(比如从 “Visual Studio 16 2019” 切换到 “Visual Studio 17 2022”),导致 CMake 无法识别新的生成器,进而出现报错。
二、解决方法
(一)方法一:删除并重新创建构建目录
- 返回到项目根目录
打开“Developer Command Prompt for VS 2022”,执行命令:
1 | cd /d D:\QTproject\new_pro\samp6_2EventAndSignal |
- 删除现有的
build_vs
目录
在项目根目录下运行:
1 | rmdir /s /q build_vs |
注意此命令会永久删除该目录及其所有内容,确认无重要文件需保留。
3. 重新创建新的构建目录
可以使用不同目录名如 build_vs2022
:
1 | mkdir build_vs2022 |
- 运行 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
位于上一级目录。
- 编译解决方案(可选)
- 使用
msbuild
编译:
- 使用
1 | msbuild samp6_2EventAndSignal.sln /p:Configuration=Release /m:4 |
参数说明:
/p:Configuration=Release
:指定编译配置为 Release,如需 Debug 版本则改为/p:Configuration=Debug
。/m:4
:用 4 个并行线程编译加快速度。- 在 Visual Studio 中打开并编译:
- 打开文件资源管理器,导航到
D:\QTproject\new_pro\samp6_2EventAndSignal\build_vs2022
目录。 - 双击
samp6_2EventAndSignal.sln
文件,用 Visual Studio 2022 打开。 - 在 Visual Studio 中进行开发、调试和编译操作。
- 打开文件资源管理器,导航到
- 在 Visual Studio 中打开并编译:
(二)方法二:在现有构建目录中删除 CMake 缓存
- 导航到
build_vs
目录
1 | cd /d D:\QTproject\new_pro\samp6_2EventAndSignal\build_vs |
- 删除 CMake 缓存文件和生成目录
1 | del /f /q CMakeCache.txt |
- 重新运行 CMake 命令,指定正确的生成器
1 | cmake -G "Visual Studio 17 2022" -A x64 -DQt6_DIR=D:\Qt\6.5.3\msvc2019_64\lib\cmake\Qt6.. |
三、其他注意事项
- 确保使用正确的命令提示符
要使用“Developer Command Prompt for VS 2022”,可在开始菜单搜索后右键选择“以管理员身份运行”。 - 确认 Visual Studio 2022 安装了必要的组件
打开 Visual Studio Installer,找到 Visual Studio 2022 点击“修改”,在“工作负载”选项卡确保选中“使用 C++ 的桌面开发”,在“单个组件”选项卡确认安装如“MSVC v143 - VS 2022 C++ x64/x86 build tools”等相关组件,缺少则勾选安装。 - 确认 CMake 版本
建议使用 3.21 及以上版本,可用命令cmake --version
查看,版本过旧需下载安装最新版并添加bin
目录到系统Path
环境变量。 - 确认 Qt6_DIR 路径正确
确认D:\Qt\6.5.3\msvc2019_64\lib\cmake\Qt6
目录存在且包含Qt6Config.cmake
文件。
四、备用方案
若命令行方法不行,可尝试使用 Visual Studio 的图形界面来打开并配置 CMake 项目:
- 打开 Visual Studio 2022。
- 选择“打开本地文件夹”,导航到
D:\QTproject\new_pro\samp6_2EventAndSignal
。 - Visual Studio 会自动检测
CMakeLists.txt
并加载项目。 - 在 Visual Studio 中配置并生成项目。
懒汉模式与饿汉模式
懒汉模式与饿汉模式
懒汉模式:获取单例时才初始化
线程不安全:
两个线程同时判断 instance
为空,创建了两个 instance
实例,不符合单例模式
1 | class Singleton { |
解决方法:
添加锁,实例还未创建时枷锁,创建实例前需要再判断一次。可能一个线程已经在创建实例了,确保不会因为枷锁期间多个线程同时进入。
1 |
|
饿汉模式:在类一加载时就初始化了 instance
这个静态变量
(在编译时就可以被初始化), 不用判断是否存在,肯定已经存在,直接返回 instance
。
1 | class Singleton { |
VS上qt配置的若干问题
一、“There’s no Qt version assigned to project WBoard.vcxproj for configuration Debug/x64”错误解决办法
在使用 Qt 与 Visual Studio 项目时,若遇到 Debug/x64 配置中没有指定 Qt 版本出现上述错误,可按以下步骤解决:
- 确保正确安装了 Qt:
- 要保证系统中已正确安装 Qt,并且在 Visual Studio 中选择了正确的 Qt 版本。可通过 Qt Maintenance Tool 或从 Qt 官网下载并安装 Qt。
- 在 Visual Studio 中配置 Qt:
- 打开项目(WBoard.vcxproj)。
- 选择“工具”->“选项”。
- 在“项目和解决方案”下,选择“Qt 项目设置”。
- 确保为项目选择了正确的 Qt 版本。若没列出任何版本,可能需要手动添加,指定 Qt 安装路径。
- 为项目分配 Qt 版本:
- 右键点击项目(WBoard.vcxproj),然后选择“属性”。
- 在“配置属性”->“Qt 项目”下,确保“Qt 版本”字段正确设置为已安装的有效 Qt 版本。
- 设置 Qt Kits:
- 若使用的是 Qt Creator 或手动管理 kits,确保在 Qt 设置中为 Debug/x64 配置了正确的 kit(匹配平台和编译器)。
- 重新构建项目:
- 配置好 Qt 版本后,重新构建项目,检查问题是否解决。
若按上述步骤操作后问题依然存在,尝试重新安装 Qt Visual Studio Tools,或检查系统中 Qt 的环境变量配置。
二、VS2022 中检查 Qt Version 配置与项目属性是否一致的方法
检查 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 安装路径。
检查项目属性:
- 打开项目属性:
- 在解决方案资源管理器中,右键点击项目(例如 WBoard.vcxproj),然后选择“属性(Properties)”。
- 检查 Qt 版本是否一致:
- 在项目属性窗口中,选择“配置属性”->“Qt 项目(Qt Project)”。
- 在“Qt 版本”字段,确保它与上述配置中的 Qt 版本一致,若不一致,手动选择正确的 Qt 版本。
- 打开项目属性:
检查 Qt 模块:
- 在“Qt 项目”设置中,查看“Qt Modules”。
- 确保项目使用了正确的 Qt 模块(例如 QtCore, QtWidgets, QtGui 等),且这些模块与选择的 Qt 版本兼容。
重新生成项目:
- 选择顶部菜单栏中的“生成(Build)”。
- 点击“重新生成解决方案(Rebuild Solution)”以确保所有配置被正确应用。
检查输出日志:
在重新生成项目时,查看输出窗口中的日志,看是否有关于 Qt 配置的错误信息,若 Qt 版本配置不正确,通常会看到类似“No Qt version assigned”或“Qt version mismatch”的错误信息。
三、Visual Studio Installer 中安装 MSVC v142 工具链的步骤及相关事项
详细步骤说明:
- 打开 Visual Studio Installer:
- 在 Windows 搜索栏中输入“Visual Studio Installer”,点击打开。
- 选择要修改的 VS2022 版本:
- 在安装器中找到已安装的“Visual Studio 2022”,点击右侧的“修改”按钮。
- 切换到“单个组件”标签页:
- 在弹出的界面中,顶部有多个标签页(工作负载、单个组件、语言包等),点击“单个组件”。
- 搜索并勾选 MSVC v142 工具链:
- 在搜索框中输入“v142”,找到以下组件并勾选:
- “MSVC v142 - VS2019 C++ x64/x86 生成工具(最新)”(确保勾选的是针对 VS2019 的 v142 版本,而非 v143)。
- 在搜索框中输入“v142”,找到以下组件并勾选:
- 完成修改并安装:
- 点击右下角的“修改”按钮,等待安装完成。
- 打开 Visual Studio Installer:
注意事项:
- 权限问题:
- 如果提示需要管理员权限,请以管理员身份运行 Visual Studio Installer。
- 组件名称可能随版本变化:
- 如果搜索不到“v142”,尝试关键词“VS2019”或“C++ 2019”。
- 工具链路径验证:
- 安装完成后,工具链默认路径为:“C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\”(路径中的版本号可能不同)。
- 权限问题:
为什么需要安装 MSVC v142?:
- 若使用的 Qt 版本是“Qt 5.15.2 (msvc2019)”,它依赖的是 VS2019 的编译器(MSVC v142)。
- VS2022 默认安装的是“MSVC v143”编译器,两者不兼容,通过安装 v142 工具链,可以让 VS2022 兼容旧版 Qt。
验证是否安装成功:
- 打开 VS2022,创建一个空的 C++ 控制台项目。
- 右键项目 >“属性”>“配置属性”>“常规”,检查“平台工具集”是否包含“Visual Studio 2019 (v142)”。
WSL2的网络问题
Ubuntu APT Update 卡住的排查与解决方案
背景
最近在使用 Ubuntu 进行 sudo apt-get update
时,发现更新一直卡在 Connecting to 172.23.153.121
,并最终超时。起初以为是网络问题,但 ping archive.ubuntu.com
和 ping security.ubuntu.com
都能返回数据,说明基础网络是通的。
深入排查后,发现 APT
可能被配置了错误的代理或镜像源,导致更新请求被错误地重定向到 172.23.153.121
,最终导致超时。以下是详细的排查过程和解决方案。
排查过程
1. 检查 APT 源是否正确
首先查看 /etc/apt/sources.list
,确认是否存在错误的源配置:
1 | cat /etc/apt/sources.list |
我的 sources.list
内容如下:
1 | deb http://archive.ubuntu.com/ubuntu focal 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 | http_proxy=http://172.23.153.121:80 |
那么需要取消代理:
1 | unset http_proxy |
并检查 bash
配置文件:
1 | grep -i proxy ~/.bashrc ~/.bash_profile ~/.profile /etc/environment /etc/bash.bashrc /etc/profile |
如果发现 export http_proxy
或 export https_proxy
,需要手动编辑相应文件删除,并执行:
1 | source /etc/environment |
4. 清除 APT 缓存并重试
清除旧的 APT 数据,防止缓存影响:
1 | sudo rm -rf /var/lib/apt/lists/* |
5. 检查 DNS 设置
如果问题依然存在,可能是 DNS 解析问题。查看 DNS 配置:
1 | cat /etc/resolv.conf |
如果 DNS 服务器不是 8.8.8.8
或 8.8.4.4
,可以手动修改:
1 | sudo nano /etc/resolv.conf |
添加:
1 | nameserver 8.8.8.8 |
然后再次运行:
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 | deb http://mirrors.aliyun.com/ubuntu focal main restricted universe multiverse |
然后更新:
1 | sudo apt-get update |
总结
- 检查 APT 代理配置 (
/etc/apt/apt.conf.d/
),删除错误的代理设置。 - 检查系统环境变量 (
env | grep -i proxy
),删除不必要的代理。 - 清理 APT 缓存 (
sudo rm -rf /var/lib/apt/lists/* && sudo apt-get clean
)。 - 修复 DNS 设置 (
sudo nano /etc/resolv.conf
,改成8.8.8.8
)。 - 强制使用 IPv4 (
sudo apt-get update -o Acquire::ForceIPv4=true
)。 - 更换国内镜像源(阿里云、清华等)。
文章标题
先分配内存空间
初始化消息队列
系统自动为控制块方培内存空间
如存储位置,头指针,为指针,消息大小,队列长度,
消息队列和消息空间在同一段内存空间中,创建水分配了消息空间呵消息队列的容量,无法更改。
发送一般消息:
若队列未满或者允许覆盖,将信息拷贝到队尾。否则,根据指定的超时时间阻塞。
若等到队列未满,或者超过了阻塞时间,则从阻塞状态转换为就绪状态,后者会收到错误码。
发送紧急消息,消息会被复制到消息队列队头。
读取消息队列
如果队列为空,任务保持阻塞状态。队列不空时,自动转变为就绪态。超过了阻塞时间自动转变为就绪态。
在中断总发消息不允许带有阻塞机制
中断必须快速响应和硬件中断
任务调度器不会在中断中运行
高优先级中断可能要更快地执行
如果在中断过程中更高优先级的任务被唤醒,中断返回后会返回更高优先级的任务。
消息队列创建:创建一个消息队列返回这个队列的队列句柄。
每创建一个新的队列需要对其分配RAM,存储状态和队列信息。
xQueueCreate():
使用动态内存分配,需将configSUPPORT_DYNAMIC_ALLOCATION 置1,(FreeRTOSConfig.h)
xQueueCreateStatic() :静态内存分配
prvInitialiseNewQueue()
参数:消息队列长度,单个消息大小,存储消息起始地址
消息队列类型
未分配存储消息的内存空间: 单个消息大小为0时将pchead指向队列控制块 pxNewQueue,因为消息队列用作互斥量时pchead设置为NULL。
分配了存储消息的内存空间: pcHead 指向存储消息的
起始地址 pucQueueStorage。
初始化消息队列控制块->重置消息队列
重置消息队列 xQueueGenericReset()
进入临界段是为了确保多任务并发环境中的数据一致性
需要恢复发送阻塞消息队列,便于数据发送
队列创建
队列创建时也需要进入临界区:临界区锁定的中断屏蔽
内存分配和指针初始化不能被打断,被错误的初始化或访问。导致内存访问错误或者队列崩溃。
通用消息队列发送函数 xQueueGenericSend()
队列未满-》添加信息
恢复等待消息阻塞的任务
- 若恢复的任务优先级比当前任务高-》切换任务
- 如果没有等待的任务,拷贝成功也需要任务切换
队列已满-》
不指定阻塞超时时间,退出,返回错误
初始化阻塞超时结构体变量,初始化进入
结束之后
检查是否超时
如果队列还是满的-》添加任务到等待发送列表中和
接受信息xQueueGenericReceive()
从队列中接收消息并把消息从队列中删除
接收的信息以拷贝的形式存在,必须提供一个足够大的缓存区(中断时必须使用特定的函数)
设置阻塞时间,若队列为空,任务保持阻塞状态。若等待时间超过两万指定的阻塞时间,直接从阻塞状态转化成就绪态。
挂起任务调度器和进入临界区
在将任务设置为阻塞的过程中,挂起调度器并进入临界区
挂起调度器意味着任务不能切换并且不准调用可能引起任务切换的 API 函数。 但挂起调度器并不会禁止中断,中断服务函数
仍然可以操作队列事件列表,可能会解除任务阻塞、可能会进行上下文切换,这也是不允许的。解决办法是还要给队列上锁,禁止任何中断来操作队列
假设有一个高优先级任务 A 和一个低优先级任务 B。任务 A 想入队,但队列已满,任务 A 将进入阻塞状态,并等待队列空闲。如果在任务 A 被正确地阻塞并添加到等待列表之前,低优先级任务 B 或者中断修改了队列的状态,它可能抢先完成入队操作
互斥量
递归获取信号量时可能出现死锁的情况:
- 信号量用完:
- 阻塞 vs 死锁:
单次获取信号量且信号量不可用,任务会进入阻塞状态,此时系统能够恢复正常运行。然而,如果任务递归获取信号量,并且没有释放信号量,导致信号量完全用尽,那么任务自身将无法继续执行,且也无法释放之前获取的信号量,这就形成了死锁。此时,没有其他任务可以释放信号量,系统无法恢复,导致死锁。
使用临界区资源的方法:
获取并释放信号量
xSemaphoreTake()xSemaphoreGive()
挂起任务调度器 vTaskSuspendAll();xTaskResumeAll();
禁用中断
taskENTER_CRITICAL()和 taskEXIT_CRITICAL()
FreeRTOS中的任务实现
任务函数的调用
- 硬件初始化
- 初始化与任务相关的列表
- 创建任务
xTaskCreateStatic
- 添加任务到就绪列表
- 启动调度器
for
循环让任务死循环
实现具体的函数
for
循环;- 任务切换
- 任务切换和调度函数
任务的定义与任务切换的实现
实现任务创建函数
任务的栈,任务的函数实体,实现任务控制块的创建和联系。
静态创建,configSUPPORT_STATIC_ALLOCATION
在 FreeRTOSConfig.h
中定义,配置为 1
。(静态创建)
xTaskCreateStatic()
函数
1 | TaskHandle_t xTaskCreateStatic( |
静态创建任务,内嵌 prvInitialiseNewTask
函数。
TaskFunction_t
:任务函数本身(在projdefs.h
中重定义的空指针)。任务运行时执行此函数,任务函数必须符合void*
类型。- 运行传参:将函数指针传递给调度器,许多任务的具体实现不同,调度器运行时只需调用不同的任务函数,编译不需要硬编码不同的函数,否则调度器需要知道函数的具体实现,将函数的实现拷贝到调度器中。指针容易被赋值。
tskTaskControlBlock
:任务的控制块,任务的状态和各种与调度相关的信息,是 FreeRTOS 调度器用来管理任务的核心结构 (tcb_t
)。
TCB
并不直接暴露给用户,而是通过 TaskHandle_t
来间接访问
TaskHandle_t
:任务句柄,在任务创建时获取,提供给用户,方便任务的挂起、恢复、删除等操作 (tcb_t
)。TaskFunction_t
:void*
类型,任务函数类型。
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 | StackType_t *pxTopOfStack, // 栈顶指针 |
初始化栈
xPSR
的bit24
必须置1
(thumb 模式)- 任务的入口地址(这个函数在内存中的地址)
当任务被创建时,FreeRTOS 会将任务入口地址(即任务函数的地址)保存到任务栈中,以便在任务切换时能够恢复这个地址并开始执行任务。
- 任务的返回地址
它是在任务执行过程中保存的,用于在任务切换时恢复任务的执行状态。不过 FreeRTOS 会将
PC
设置为prvTaskExitError
,而不是返回到任务的调用位置。这是为了确保任务结束后不会继续执行不应执行的代码。
FreeRTOS 中的任务函数通常是一个无限循环。 R12
,R3
,R2
和R1
默认初始化为0
。