CppCon 2017 学习:Almost Unlimited Modern C++ in Kernel-Mode Applications

Almost Unlimited Modern C++ in Kernel-Mode Applications” 是对在内核模式(Kernel Mode)中使用现代 C++(C++11/14/17/20)技术的探索。这是一个高级、但越来越实用的话题,尤其是在 Windows 驱动开发、嵌入式系统、操作系统扩展、以及 kernel-mode 应用程序中。

基本理解

什么是 Kernel Mode?

  • 是操作系统运行核心代码的权限级别。
  • 相对的,用户空间是“User Mode”,比如普通的应用程序。
  • 在 Windows 中,驱动程序(.sys 文件)运行在 kernel mode。

为什么 C++ 在 Kernel 中用得少?

传统上内核代码偏好 C 或 C with Classes,是因为:

传统顾虑 原因
异常处理 会引入复杂的栈解构机制,不被内核支持
RTTI / vtable 增加代码大小且带运行时风险
动态内存管理 不容易控制分配(尤其是在中断或高IRQL时)
标准库依赖 std::vector, std::string 等使用 malloc/new,不适合内核
模板膨胀 编译开销、代码尺寸膨胀难以控制

“Almost Unlimited” 是什么意思?

它意味着你可以在内核中使用几乎所有现代 C++ 特性,前提是:

  • 不使用 标准库实现的动态行为(比如 iostream, exceptions, RTTI)
  • 提供内核友好的替代实现(例如:自定义 allocator、容器等)

可以安全使用的 Modern C++ 特性

特性 可用性 说明
constexpr, if constexpr 编译期优化,无运行时开销
lambda 表达式 不捕获 lambda 安全可用
模板 / 泛型编程 编译期行为,非常适合内核优化
类型推导 (auto) 编译期行为
范围 for (for(auto& x : ...)) 如果容器支持
std::optional, std::variant 前提是用你自己的替代或移植库
智能指针 (unique_ptr) 自定义 deleter 或 allocator
RAII (析构资源管理) 非常适合管理锁、资源释放等
move 语义 没有运行时依赖,适合性能关键代码
✳:你不能直接使用 libc++ 或 libstdc++,但可以实现或移植精简 STL 替代库

不建议使用的特性

特性 原因
Exceptions Windows 内核禁用 C++ 异常
RTTI(如 dynamic_cast 无法启用;不安全;不必要
std::thread, std::mutex 与用户空间线程模型耦合
流式 IO (std::cout) 依赖 C runtime,不能用
new/delete 默认操作 非 deterministic,不能直接用内核 heap

示例:自定义内核 allocator + 容器

template<typename T>
class kernel_allocator {
public:
    using value_type = T;
    T* allocate(size_t n) {
        return static_cast<T*>(ExAllocatePoolWithTag(NonPagedPoolNx, n * sizeof(T), 'TAG1'));
    }
    void deallocate(T* p, size_t) {
        ExFreePoolWithTag(p, 'TAG1');
    }
};
template<typename T>
using kvector = std::vector<T, kernel_allocator<T>>;

在 Kernel 中构建 C++ 模块时的策略

要点 做法
编译器支持 使用 MSVC 或 Clang with /kernel 编译标志
禁用异常 /EHs- /GR-
替代 STL 使用 EASTL, STLPort, 或自己实现子集
内存管理 使用内核 API,如 ExAllocatePoolWithTag
日志 / 输出 使用 DbgPrintKdPrint 替代 std::cout

实际用途

  • Windows 驱动(KMDF、WDM、NT 驱动)
  • Hypervisor(如使用 C++20 coroutine 构建 hypervisor logic)
  • 内核级防护/防病毒模块
  • 自定义内存管理、hook、IO 过滤器等高级内核模块

推荐实践库

  • EASTL (Electronic Arts STL) – 适配 allocator
  • libcxx-lite – Microsoft STL 可移植子集
  • ModernCppKernelDev (GitHub) – 专为 Windows kernel-mode C++ 开发设计

结论

你可以在 kernel mode 中使用大部分现代 C++ 技术,前提是:

  1. 避免标准库的动态特性(exception, RTTI, I/O)
  2. 使用自己的 allocator 和容器
  3. 保持资源控制、性能可预测
    如果你需要:
  • 示例代码(如:用 C++ 构建 kernel list、map)
  • 如何封装 DPC、IRQL-safe 的对象
  • 构建可移植的 Kernel Utility Library

这段话引用的是 Linus Torvalds 在 2004 年发表的著名观点:

“Tale as old as time… In fact, in Linux we did try C++ once already, back in 1992. It sucks. Trust me — writing kernel code in C++ is a BLOODY STUPID IDEA.”

这句话在操作系统社区非常有影响力,它反映了 Linus 对在内核中使用 C++ 的强烈反对。

理解这段话的背景和含义

Linus Torvalds 的核心观点

  1. 内核代码应该用 C 写,而不是 C++。
  2. 他和早期 Linux 社区在 1992 年确实尝试过用 C++ 写 kernel,但发现问题重重,最终放弃。
  3. 他认为 C++ 的复杂性和不确定性不适合内核开发。

为何 Linus 认为 C++ 在内核中是 “BLOODY STUPID”?

问题 解释
隐式行为过多 C++ 有自动构造/析构、隐式复制、临时对象,这些行为在内核中难以控制和调试。
异常机制(Exception Handling)不可接受 Linux 内核无法接受 throw / catch 带来的栈解构复杂性和性能成本。
运行时类型信息(RTTI)不可预测 引入 dynamic_cast、虚函数表等运行时机制增加了不确定性和二进制膨胀。
编译器行为依赖过重 不同编译器对 C++ 特性的支持程度不同,而 Linux 内核要求高可移植性。
模板过度膨胀(Code Bloat) 模板元编程在内核中容易导致二进制体积失控,且调试极其困难。

但要注意:Linus 说的是 “C++ 用于 Linux Kernel”,不是泛指 C++ 本身无用

在别的上下文里,比如:

  • Windows kernel-mode drivers(微软已经在 Windows Driver Framework 中使用 C++)
  • 自研操作系统 / RTOS
  • 高性能嵌入式内核
    现代 C++(特别是 C++17/20)如果使用得当,是完全可以安全并且高效地在 kernel 模式下工作的。

现代观点已经更为中立和务实

年代 主流观点
1990s C++ 太复杂,不适合内核
2000s 以 C 为主,但开始关注安全和封装性
2010s Windows 驱动广泛采用 C++
2020s C++20/23 的某些特性(RAII、constexpr、concepts)被视为内核友好
很多现代操作系统爱好者或高可靠性内核项目(例如 seL4、Zircon、Redox OS)都在探索如何使用“受控的现代 C++”。

总结

Linus 的批评不是对 C++ 本身的否定,而是对 C++ 在 Linux 内核中的不可控性 的否定。

理解这句话的重点:

  • C++ 的强大不是问题,但它的“隐式复杂性和不确定性”不适合一个极度可控、高度确定的环境 —— 这就是 Linux Kernel。
  • 如果你能克服这些隐式复杂性(如避免 exceptions、RTTI,使用自定义 allocator 等),那么现代 C++ 在内核开发中并不是“不可能”。
    如果你对:
  • 如何平衡 C++ 的强大表达能力和内核要求的严格可控性?
  • C++ 在 Redox OS、Fuchsia、Microsoft 内核模块中的实际用法

这段话主要是在批评 C++,特别是在操作系统内核(kernel)开发中的使用。下面是逐句解析和解释:

“The fact is, C++ compilers are not trustworthy.”

“事实上,C++ 编译器不可信。”

意思是 C++ 编译器行为复杂、不透明,可能会在不经意间引入 bug,尤其是在底层系统编程中。

“They were even worse in 1992, but some fundamental facts haven’t changed:”

“它们在 1992 年更糟糕,但一些根本问题至今未变。”

指出尽管 C++ 编译器随着时间进步了,但本质上的一些缺陷仍然存在。

“- the whole C++ exception handling thing is fundamentally broken. It’s especially broken for kernels.”

“- C++ 的异常处理机制本质上是有缺陷的,尤其在内核中尤其糟糕。”

C++ 的异常处理(try/catch)会引入不可预测的控制流和隐藏的资源管理逻辑,这对于追求精确控制的内核代码来说是灾难性的。

“- any compiler or language that likes to hide things like memory allocations behind your back just isn’t a good choice for a kernel.”

“- 任何喜欢在背后隐藏内存分配等操作的编译器或语言,都不适合用于内核开发。”

C++ 常常在你不察觉的情况下自动分配内存(比如构造函数、STL 容器),这在内核开发中是不允许的,因为你必须对每一次内存分配有绝对控制。

“- you can write object-oriented code (useful for filesystems etc) in C, without the crap that is C++.”

“- 你可以用 C 写面向对象的代码(例如用于文件系统),而不用 C++ 那些‘垃圾’。”

这是说即便在 C 语言中,也可以实现面向对象的思想(如用结构体+函数指针模拟类和多态),无需引入 C++ 的复杂性和不可控行为。

总结:

这段话是从操作系统内核开发者的角度(可能是 Linus Torvalds)对 C++ 的批评。核心观点包括:

  • C++ 过于复杂和隐式,不适合低层系统开发。
  • 内核开发需要对行为和资源的绝对控制,而 C++ 常隐藏这些细节。
  • 面向对象不是 C++ 的专利,C 也可以做到。

这段话是从 Windows 内核开发的角度来谈 C++ 在内核编程中的问题,口吻更温和、技术性更强,主要关注 C++ 的“高级特性”在内核模式(kernel-mode)下的风险。下面是逐句解析和理解:

原文逐句解析:

“It is ‘advanced’ C++ features such as non-POD (‘plain ol’ data’, as defined by the C++ standard) classes and inheritance, templates, and exceptions that present problems for kernel-mode code.”

“C++ 的一些‘高级’特性——如非 POD(Plain Ol’ Data,简单旧式数据)类、继承、模板和异常——会在内核模式代码中带来问题。”

这些特性会引入复杂的运行时行为,不适合资源敏感、控制精细的内核环境。

“These problems are due more to the C++ implementation and the kernel environment than to the inherent properties of the C++ language.”

“这些问题更多是由 C++ 的实现方式和内核的运行环境造成的,而不是 C++ 语言本身固有的问题。”

说明问题主要出在编译器生成的代码(如隐藏的构造/析构逻辑、异常表、模板展开)与内核运行时模型之间的不兼容。

“Anything involving class hierarchies or templates, exceptions, or any form of dynamic typing is likely to be unsafe.”

“任何涉及类层次结构、模板、异常或动态类型的东西,很可能是不安全的。”

原因是这些机制在底层的二进制代码中不可预测或依赖运行时支持,而内核代码往往没有这些支持。

“Using these constructs requires extremely careful analysis of the generated object code.”

“使用这些构造(features)时,必须对编译器生成的目标代码进行非常仔细的分析。”

强调:如果你非用不可,至少要精确地知道它们在汇编层面具体做了什么。

“Limiting use of classes to POD classes significantly reduces the risks.”

“将类的使用限制在 POD 类型上,会显著降低风险。”

POD 类本质上就是像 C 的结构体那样简单的数据容器——无虚函数、无继承、无构造析构副作用。

总结理解:

  • 观点更温和:这段不像 Linus 那样直接否定 C++,而是承认 C++ 有用,但其高级特性在内核中风险高。
  • 问题核心:不是语言本身的问题,而是内核运行环境限制(没有堆栈展开器、没有运行时类型信息 RTTI、不能容忍隐式资源管理等)。
  • 建议
    • 避免使用:异常、模板、继承、多态等。
    • 鼓励使用:POD 类、明确可控的构造方式。
    • 必要时:必须对汇编输出有深入了解,不能“盲信编译器”。

对你来说,这说明了:

如果你打算在内核级别写 C++,要非常有意识地选择语言特性。推荐的做法是:

  • 避免复杂类结构;
  • 使用类似 C 风格的结构体;
  • 不使用异常处理;
  • 禁用 RTTI;
  • 仔细阅读编译器生成的代码(例如使用 objdumpIDA)。

这段内容是在讲 2012 年时 Windows 内核开发中对 C++ 编译器的限制,特别是 Visual C++ 提供的 /kernel 编译选项。这个选项是为内核模块(如驱动程序)量身定做的,目的是确保生成的代码符合内核的稳定性和安全性要求。

Visual C++ 的 /kernel 选项

这是一个专门用于内核开发的编译开关,会启用一系列限制来确保生成代码可以安全运行在内核模式下。

禁用异常处理 —— 使用 try/catch 会导致编译错误

异常处理机制依赖运行时栈展开、隐藏表格等复杂机制,这在内核中是不安全的也不可用的。

禁用运行时类型识别(RTTI)—— 使用 dynamic_cast 或 typeid 会导致编译错误

RTTI 会引入额外的运行时信息(如虚函数表的类型信息),不适合在资源受限、必须可预测的内核环境中使用。

必须替换 new 和 delete 运算符

内核不能使用标准的堆分配方式(如 mallocnew),需要使用内核特有的分配函数(如 Windows 内核中的 ExAllocatePoolWithTagExFreePool)进行替换。

32 位编译需使用 /arch:IA32

在 32 位内核模式下,要求生成兼容的 IA-32 架构代码,不允许使用高级 SIMD 指令或不兼容指令。

64 位下不支持 AVX 指令集

即使你的 CPU 支持 AVX(Advanced Vector Extensions),内核模式下也不允许使用这些指令。原因是内核线程上下文切换时不能保证正确保存这些扩展寄存器。

总结:

2012 年的 Windows C++ 内核开发中,/kernel 编译选项 会对 C++ 特性做如下严格限制,以保证内核代码的稳定与安全:

限制项 原因
禁用 try/catch 异常机制不安全
禁用 RTTI 运行时类型检查引入不可控行为
必须自定义 new/delete 内核不能用标准堆
强制指定架构 保证兼容性和正确性
禁用 AVX 避免复杂寄存器上下文切换问题
这进一步说明:在内核开发中,C++ 语言只能用它“最简单、最可控的子集”。你写的是“内核中的 C++”,不是“标准 C++”。

Windows 上实现实时性(real-time)扩展的系统 —— InTimeRTX —— 的比较总结。下面是逐句的理解和解释:

第一部分:共同点(A lot alike)

这说明 InTime 和 RTX 在核心机制上有很多相似之处。

Intel/AMD architecture

两者都运行在 x86 / x64 架构(Intel 或 AMD CPU)上。

CPU segregation / alternate scheduler

使用“CPU 分离”和“替代调度器”机制:

  • 实时任务会被绑定(或隔离)到专用的 CPU 核心上(CPU segregation)。
  • 这些核心上的任务使用自己的实时调度器,而不是 Windows 默认的时间片调度。
No sharing of legacy IRQs

不与 Windows 共享传统中断(IRQs):

  • 传统 IRQ 是由 Windows 控制的,但实时系统为了保证确定性,会使用独立的或重定向的中断机制。
Custom shared libraries

使用自定义的共享库(如 DLL):

  • 实时部分不直接使用 Windows 系统库,而是使用自己专门设计的、轻量、可预测行为的库。

第二部分:不同点(With some differences)

这里强调 InTime 和 RTX 的执行模式不同

InTime: user-mode applications

InTime 的实时应用运行在用户模式(Ring 3):

  • 不具有最高权限,不能直接访问硬件或内核资源。
  • 优势是更安全、易于编写和调试。
  • 缺点是实时性略低于内核模式,尽管已有专属 CPU 和调度器来保障延迟。
RTX: kernel-mode (Ring 0, privileged) applications

RTX 的实时应用运行在内核模式(Ring 0):

  • 具有系统最高权限,可以直接操作硬件、中断、内存。
  • 延迟更低,响应更快,适合对实时性要求极高的场景(如运动控制、工业自动化)。
  • 缺点是更复杂、更容易出错、不容易调试。

总结表格:

特性 InTime RTX
执行模式 用户模式(Ring 3) 内核模式(Ring 0)
实时性能 较好,延迟低 更好,延迟更低
安全性/稳定性 较低(容易破坏系统)
编写/调试难度 简单 较复杂
适用场景 一般实时性要求(如控制界面) 高精度实时性(如伺服控制)

一句话总结:

InTime 和 RTX 都通过独立调度和 CPU 隔离,实现了在 Windows 上的实时性能,但 InTime 更安全易用,RTX 更接近硬实时,适合更严苛的控制场景。

多核系统的分区架构Partitioned Quad-Core Host),常用于实时系统(RTOS)嵌入式系统中,为了增强确定性、安全性和隔离性,将CPU、内存和I/O资源进行静态划分

一、第一部分:Partitioned Quad-Core Host(四核分区主机)

概念解释:

  • Quad-Core:有 4 个 CPU 核(CPU1~CPU4)。
  • Partitioned Host:主机中的资源(CPU、内存、I/O)是分区使用的,各分区之间彼此隔离,像运行在不同的“虚拟机”上。

图示理解(简化):

| Partitioned Memory |
+-------------------+
| APP.1             | ← 绑定 CPU1 + 某块内存
|-------------------|
| APP.2             | ← 绑定 CPU2 + 某块内存
|-------------------|
| APP.3             | ← 绑定 CPU3 + 某块内存
|-------------------|
| APP.4 + OS        | ← 绑定 CPU4 + 某块内存
+-------------------+
  • 每个分区可运行一个或多个 APP(应用) 和/或一个 OS 实例(如RTOS)
  • 各分区间:
    • 内存不可共享
    • CPU 核互不干扰
    • I/O 分别分配(Assigned I/Os)

二、第二部分:Or: Node A / B / C / D 模式

概念解释:

  • 把每个 CPU 核视作一个逻辑节点(Node A~D),每个节点像一个独立的处理单元,运行自己的操作系统、驱动和应用。

结构示意:

| Node A | Node B | Node C | Node D |
|--------|--------|--------|--------|
| CPU1   | CPU2   | CPU3   | CPU4   |
| OS     | OS     | OS     | OS     |
| APP.1  | APP.2  | APP.3  | APP.4  |
| I/O A  | I/O B  | I/O C  | I/O D  |
  • 每个“节点”拥有:
    • 自己的 CPU(物理隔离)
    • 自己的内存(逻辑隔离)
    • 自己的操作系统(可定制,如 RTOS、VxWorks、QNX 等)
    • 自己的 I/O 通道(比如绑定的串口、网络接口、GPIO)

应用场景:

  • 安全关键系统(如航空电子、汽车 ECU)
  • 工业自动化
  • 实时系统与多任务控制
  • 信息与控制双隔离系统

总结一句话:

这是一个将 4 个 CPU 核心、内存和 I/O 资源静态隔离成多个独立节点或分区的架构,每个节点/分区可以运行自己的 OS 和 APP,互不干扰,适合高安全、高实时性的应用环境。

Windows 与 RTX 实时扩展系统集成结构 的文字描述,类似一个模块图或系统架构图。下面是对每一部分的逐段拆解与完整理解:

总体理解:

RTX(IntervalZero RTX/RTX64) 是一个让 Windows 系统具备实时能力 的扩展,它通过引入一个**并行运行的实时子系统(RTSS)**来实现硬实时控制。你的描述就是 RTX 的典型系统架构,其中:

  • 一部分程序运行在 Windows 环境(非实时)
  • 一部分程序运行在 RTX 实时子系统(实时)
  • 两者可以通过 API 通信(如 RtApi 和 RtkApi)

分模块解释:

Windows Process

  • 普通的 Windows 用户态进程,如图形界面、服务进程等。
  • 运行在 User Mode(Ring 3)

RtApi / RtkApi (Linked with RTOS)

  • RtApi:Windows 应用访问 RTSS 的接口 API(用户态使用)
  • RtkApi:Windows 内核态组件(如驱动)访问 RTSS 的接口
    这些 API 是 Windows 和 RTX 实时子系统之间通信的桥梁,允许传递控制命令、共享数据等。

Real-time / RTSS (Real-Time Subsystem)

  • RTSS 是 RTX 提供的 实时操作环境,运行在 并行于 Windows 内核的独立调度器上。
  • 它提供:
    • 实时线程调度
    • 实时内存分配
    • 硬中断处理
  • 这些线程或进程运行在 Kernel Mode(Ring 0),具有直接访问硬件的权限。

Rtxtcpip

  • RTX 提供的 实时 TCP/IP 网络栈,可在 RTSS 中使用,允许实时任务进行网络通信,避免走 Windows 非实时网络路径。

Windows Subsystem / Server Console / Process or DLL

  • 提供界面、配置工具或服务,用于:
    • 配置 RTX 实时环境
    • 启动/停止实时任务
    • 收集日志与监控信息
    • 与 RTSS 实时任务通信

Kernel and Device Drivers

  • RTX 支持用户编写和加载自己的实时驱动程序(RTSS 驱动),也可通过 RTSS 与 Windows 驱动进行间接协作。

Windows HAL / Real-time HAL Extension

  • RTX 替换或扩展了部分 Windows 的 HAL(硬件抽象层),以便实现:
    • 中断重定向到 RTSS
    • 定时器资源分离
    • CPU 分区隔离(Core Isolation)

Processor / Platform / X, X+1, 31/63

  • 表示在 多核系统上,Windows 和 RTSS 是并行运行、互不干扰的:
    • Windows 使用部分核心(如 Core 0 ~ X-1)
    • RTSS 使用其他核心(如 Core X ~ N)
    • RTX 可在 64 核以上的平台上运行(如核心编号 0~63,甚至更高)
    • 分离运行实现硬实时保证

汇总图形结构化理解:

                   ┌──────────────────────────────┐
                   │     Windows Processes         │
                   │ (GUI, services, non-RT apps)  │
                   └────────────┬─────────────────┘
                                │
                             RtApi
                                │
            ┌──────────────────┼────────────────────┐
            │                  │                    │
      ┌─────▼──────┐     ┌─────▼─────┐         ┌────▼─────┐
      │ RTSS Tasks │     │ Rtxtcpip  │  ...    │ RT Drivers│
      │ (real-time │     │ (RT TCP/IP│         │           │
      │  apps)     │     │  stack)   │         │           │
      └────────────┘     └───────────┘         └───────────┘
               │                   │                   │
         Real-Time Scheduler   Memory Management   IRQ Handling
               │
         ┌─────▼─────┐
         │ RT HAL Ext│
         └─────┬─────┘
               │
         ┌─────▼──────┐
         │   Hardware │ (CPU, Memory, I/O)
         └────────────┘

一句话总结:

你提供的是 Windows + RTX 实时架构的模块图描述。它说明了 Windows 和 RTX 是如何共享平台但运行在分离的内核调度器和资源空间中,通过 API 实现数据交换和协作,从而既保证了 Windows 的通用性,又提供了可预测、低延迟的硬实时处理能力

什么是实时系统(Real-Time System)?

实时系统是一类在严格时间限制内完成任务的计算机系统,正确性不仅取决于结果本身,还取决于产生结果的时间。简单说:“快”不一定是实时,但实时一定要“准时”。

一句话定义:

实时系统是在规定时间内必须对输入作出响应的系统,超过这个时间就视为失败。

两类实时系统:

类型 描述 示例
硬实时系统 不能超时,一旦超时,系统功能将崩溃或产生灾难性后果。 飞机控制、心脏起搏器
软实时系统 尽量不要超时,但偶尔超时不会导致系统失效 视频播放、在线游戏

特点总结:

特性 说明
确定性 任务完成时间是可预测的
低延迟 响应迅速,毫秒级甚至微秒级
可靠性高 通常用于关键场合,不能容忍系统挂掉
资源隔离 常通过 CPU 分配、内存分区、I/O 专用等手段实现
调度可控 通常使用实时调度器,如 Rate Monotonic、EDF

实时系统常见组成:

  • 实时操作系统(RTOS):如 FreeRTOS、VxWorks、QNX、RTX 等
  • 定时器和中断机制:对外部信号做出快速响应
  • 专用硬件:支持中断优先级、核隔离等
  • 应用任务调度器:确保关键任务按时执行

应用场景:

领域 说明
工业控制 PLC、机器人、传送带控制等
航空航天 飞行控制系统、导航
汽车电子 自动驾驶、刹车控制(ABS)
医疗设备 心电图分析、手术辅助系统
音视频处理 实时音频效果器、视频编解码等

小结:

实时系统 ≠ 高性能系统

实时系统强调的是“在正确的时间做正确的事”,不是“尽量快”。

对在任务关键型系统中使用 Windows 操作系统的批评性总结,强调了 Windows 在可靠性方面的不足,特别是在需要高度稳定性的实时或嵌入式系统中。

列出的负面经验:

  1. 频繁重启
    • Windows 主机系统常常需要重启,稳定性不足。
  2. 难以调试的问题
    • 存在一些间歇性错误(intermittent misbehaviors),非常难以定位和修复。
  3. 系统不稳定
    • IOS 和 Lesson Plan 等系统变得不稳定,需要频繁重启。
  4. 后台任务干扰或崩溃系统
    • Windows 的后台任务(如服务、更新等)可能影响或导致仿真系统崩溃。
  5. 嵌入式设备内存不足
    • SOUNDAUDIO 这样的单板处理器运行 Windows Embedded 会频繁耗尽内存,导致频繁重启。

他们的理解和建议:

我们理解开发人员(TDMs)喜欢使用 Windows 提供的强大开发环境

→ Windows 开发体验确实好(IDE、调试工具、驱动支持等)。

但是,交叉编译(cross-compilation)完全可以让我们在 Windows 上开发、却部署到更可靠的 Linux 主机上运行。

一句话总结:

虽然 Windows 提供了良好的开发工具,但它在任务关键、嵌入式或实时系统中表现出频繁重启、不稳定和难以调试等问题,建议采用更可靠的 Linux 系统进行部署。

现代 C++ 在内核模式(Kernel-Mode)或嵌入式实时系统中应用的讨论、警告和历史案例。它聚焦于:技术潜力、语言规范、实际事故、以及潜在问题。以下是逐段理解和总结:

“I Can Do Bad All By Myself”:引子+讽刺

  • 暗示:即使没有语言和工具帮倒忙,系统开发本身就足够容易出错
  • 典型语气:对使用**复杂或不受控的现代语言特性(如 C++)**表示警惕。

Toyota Brake Case — 软件失误导致安全事故

参考链接:

  • Koopman 的分析报告(CMU 安全软件专家)
    问题包括:
  • “Spaghetti code”(意大利面式代码):结构混乱、耦合严重,难以维护。
  • MISRA 违规:未遵守 MISRA C/C++ 安全编码标准。
  • 错误使用看门狗(Watchdog):系统死机时未能及时复位。
  • 并发问题(concurrency issues):线程、任务调度错误,导致竞态条件。
    结论:现代汽车使用的嵌入式 C/C++ 系统如果缺乏规范和控制,可能导致致命故障

Nest Thermostat 软件故障 — 实际嵌入式 C++事故

  • 链接指向:Google 收购的 Nest 智能恒温器发生故障,导致用户家中断电、变冷
  • 原因:OTA 更新中代码 bug 导致设备无法运行。
    再次说明:智能设备也可因 C++ 代码错误导致大规模用户受影响

《Real-Time C++》— Chris Kormanyos

使用现代 C++ 在嵌入式实时系统(RTOS)中是可能的,但要注意极端资源受限的环境。

典型硬件环境:

  • 程序大小:4 KB – 1 MB
  • RAM:256 B – 128 KB
  • CPU:8-bit 到 32-bit
  • 时钟:8 MHz – 200 MHz
    结论:在极小资源下用 C++ 不是不可能,但必须极度克制和规范。

“1.21 Gigawatts” 段落 — 面向现代硬件的新希望

  • 对比嵌入式“微机”环境:
    • 现在我们有64核、GHz频率、GB级内存
  • 推论:可以大胆使用现代 C++(包括模板、类型推导、内联 lambda 等)来写内核级代码?
    但作者并不是盲目乐观,而是紧接着提出警告

Freestanding vs Hosted — 标准限制警示

C++ 两种实现环境:

模式 特点
Hosted 标准完整支持(如在 Linux、Windows 上)
Freestanding 用于裸机、RTOS、内核模式。标准库支持非常有限。

Freestanding 至少支持的头文件:

<atomic>, <exception>, <initializer_list>, <limits>, <new>, <type_traits>,
<ciso646>, <cstddef>, <cfloat>, <climits>, <cstdint>, <cstdlib>, 
<cstdarg>, <cstdalign>, <cstdbool>

实际实现 可能无法使用 , , 等复杂功能。

核心警告:即便硬件很强,语言和标准库的限制仍存在,尤其在 freestanding 模式下。

最后一句:What Could Go Wrong? (反问)

  • 明知潜力无限,但如果不加限制、缺乏规范,C++ 的复杂性会迅速变成缺陷
  • 与前面的事故(Toyota、Nest)形成照应。

总结一句话:

尽管现代 C++ 在内核模式和嵌入式实时系统中具备巨大潜力(多核、高频、内存充裕),但历史经验清晰地提醒我们:滥用复杂特性、缺乏控制与规范,可能造成灾难性后果。

如你需要,我还可以进一步解释:

  • freestanding C++ 的应用技巧
  • MISRA C++ 与安全子集
  • 内核/RTOS 中推荐的 C++ 编码实践

内核模式或受限环境中使用 std::random_devicerand_s 时可能遇到的平台依赖性问题。下面是详细理解与拆解:

std::random_device 是什么?

std::random_device 是 C++11 引入的**“非确定性随机数生成器”**(如果可用)。它设计用于从系统级熵源(比如 /dev/urandom、硬件 RNG)中获取随机值。

标准规定([rand.device]/2):

如果实现无法提供非确定性随机数源,它可以使用一个“伪随机数引擎”作为替代。

也就是说:

  • 优先使用硬件或系统熵源(非确定性)
  • 如果做不到 → 可以退而使用确定性伪随机生成器(但这样就不是真随机了)

示例代码解释:

#include 
int main() {
    try {
        std::random_device rd;
        ...
    } catch (const std::exception&) {
        // implementation-defined exception
    }
}

这里的 try-catch 是为了捕获 std::random_device 构造失败的异常。标准没有规定具体会抛什么异常,异常类型是“实现定义的”(implementation-defined)

问题:Visual C++ 的实现细节(你提供的信息)

在 Visual C++ 2012 CRT(C Runtime)中:

  1. rand_s 使用 RtlGenRandom() (又名 SystemFunction036)获取安全随机数。
  2. 动态链接 cryptbase.dlladvapi32.dll,去找 SystemFunction036
  3. 如果没有找到 → 没有备用方案(no fallback):
    • rand_s 会失败,返回 ENOMEM(内存不足错误,但其实是找不到 API)
    • 或者 直接 abort()

这在某些精简版系统(如内核模式、嵌入式版 Windows)中很容易出现!

SystemFunction036 是什么?

extern "C" BOOLEAN WINAPI SystemFunction036(
  PVOID buffer,
  ULONG buffer_count
);

这是 Windows 的内部加密 API,别名为 RtlGenRandom,用于生成安全随机数。它不是标准 Win32 API,但被广泛使用在:

  • rand_s(安全随机数)
  • std::random_device(在 MSVC 上的实现依赖于它)

小结:

项目 问题
std::random_device 不一定总是可用,可能会 fallback(或抛异常)
rand_s(MSVC) 严重依赖 SystemFunction036,如果系统组件缺失 → 会失败
在内核/精简环境中使用 这些 API 很可能不可用,导致崩溃或无法获取随机数

建议做法(内核模式或嵌入式 C++):

  • 避免使用 std::random_devicerand_s,除非你确信目标环境支持所需的底层 API。
  • 考虑使用以下方式生成随机数:
    • 硬件 RNG(如 RDSEED, RDRAND
    • RTOS 提供的熵源
    • 自己封装一个种子可控的伪随机数引擎(如 std::mt19937
  • 明确 fallback 策略,避免构造失败时导致系统异常退出。

1. Shared Objects 和 Kernel Modules 区别

  • Linux 里的 kernel modules 是内核模块(.ko 文件),它们是内核空间代码,不是用户态共享库。
  • Windows 里的 export drivers 是内核驱动程序,表现为“内核 DLL”(Kernel DLL),同样没有用户态代码部分。
  • RTX(Windows 实时扩展) 不能使用普通的 Windows DLL,因为:
    • 它们的入口点(entry points)不同
    • RTX 实时系统内核限制了模块的加载方式和接口调用

2. 关于 rand_s 在内核环境的困境

  • RTX 里有 SystemFunction036(就是 Windows 里的安全随机数生成函数)
  • 但是它总是返回失败,不能用
  • 解决思路:
    • 试图让 rand_s 工作(但 SystemFunction036 异常)
    • 或干脆让 rand_s 始终返回失败(ENOMEM),避免假装成功导致错误

3. 关于文件系统的简单示例

#include 
#include 
#include 
#include 
std::uintmax_t file_size(const std::string& file) {
    struct stat s{};
    stat(file.c_str(), &s);
    return s.st_size;
}
  • 这是一个 C++ 计算文件大小的简易函数
  • 使用 POSIX 标准的 stat 函数
  • 可能暗示在内核或低级系统编程中访问文件元数据的需求

结合前文的理解:

  • 内核和实时系统中共享对象(共享库)和用户态 DLL 完全不同,所以使用时必须注意接口、调用约定和可用功能。
  • rand_s 和随机数功能在内核或 RTX 中可能不可用或表现不稳定,建议谨慎使用。
  • 低级系统编程时常常要用到类似 stat 这样的接口读取文件元信息,但在内核或 RTOS 中可用 API 也会更有限。
    如果你需要,我可以帮你:
  • 详细分析内核模块和 DLL 之间技术差异
  • 提供 RTX 下可用的随机数生成替代方案
  • 设计适合内核/实时系统的文件访问接口范例

这组内容讲了Windows SDK 在内核模式或受限环境下调用文件系统相关API的问题、路径处理的复杂性以及并发支持的限制:

1. 8 个 unresolved externals(未解析的外部符号)

  • SetCurrentDirectory
  • GetCurrentDirectory
  • GetFullPathName
  • PeekNamedPipe
  • FindClose
    这些是 Windows API 函数,主要用于:
  • 目录操作(设置/获取当前目录,获取完整路径)
  • 管道操作(读取命名管道数据)
  • 关闭文件查找句柄
    问题:
    这些函数在某些 Windows SDK 版本,特别是用于内核模式或某些受限环境时,链接器找不到实现,导致“unresolved external”错误。

2. 无当前工作目录、无相对路径、无目录遍历

在内核模式或某些实时操作系统里:

  • 没有“当前工作目录”这一概念
  • 不支持相对路径(比如 ..\folder\file
  • 无法进行目录递归或遍历
    这大大限制了文件路径的使用和文件系统的访问方式。

3. P0544 - User Injection of Filesystems

  • 这是一个 C++ 标准委员会(WG21)提案,链接:http://wg21.link/P0544
  • 目的是允许用户态代码注入自定义的“虚拟文件系统”,比如内存文件系统用于测试。
  • 这对内核和嵌入式环境里没有传统文件系统很有用。

4. 路径字符类型问题

  • Visual C++ 2012/2013 版本中,非 TS(Technical Specification)文件系统实现:
    • 路径用 char 类型字符(ASCII)
  • Visual C++ 2015/2017 版本,TS 文件系统实现:
    • 路径用 wchar_t(宽字符,支持 Unicode)
    • 需要外部函数将宽字符路径转换为窄字符路径(wide-to-narrow)
    • 这种转换依赖大量 Windows API,而这些API在内核模式或受限环境不可用

5. 并发设施

你代码示例:

#include 
int main() {
    std::thread t([] { });
    t.join();
}

是标准 C++11 线程库的使用示范。
理解

  • 在内核模式或一些嵌入式/实时环境下, 支持可能缺失或者行为不稳定。
  • 你可能不能直接用 std::thread,需要依赖 RTOS 或内核提供的线程/任务API。

综合总结

  • Windows SDK 的文件系统相关API在内核/实时环境缺失或不可用。
  • 路径操作限制多,传统路径和当前目录概念不适用。
  • 文件系统接口趋向于虚拟化或用户注入实现(如 P0544 提案)。
  • 不同版本 MSVC 对路径编码支持不同,宽字符与窄字符转换带来额外依赖。
  • 标准 C++ 并发支持受限,必须针对环境选择合适的线程模型。
    如果你想,我可以帮你:
  • 设计适合内核模式的文件访问或路径处理方案
  • 介绍如何在无文件系统或虚拟文件系统环境中进行数据管理
  • 解释内核环境下的多线程或任务管理最佳实践

这部分内容继续聚焦在C++多线程支持在Windows内核/实时环境中的实现难题和资源限制,我帮你详细拆解:

1. std::thread 在 Visual C++ 上的问题

  • VC++ 2012
    • 出现了 17个 unresolved externals(链接错误)
    • 相关符号包括 DuplicateHandle(Windows API,用于复制句柄)、以及互斥锁和条件变量的初始化代码
    • 原因是当时实现依赖 Concurrency Runtime (ConcRT),这是微软为并发设计的运行时库
  • VC++ 2017
    • 仍有 14个 unresolved externals
    • 微软对 STL 的多线程原语重新实现,避免依赖 ConcRT
    • 因为 ConcRT 的使用“麻烦多于好处”
      总结:
      Windows 内核模式和某些受限环境对 ConcRT 支持不佳,导致 std::thread 和相关同步原语不能直接使用。

2. boost::thread 的情况

  • 只有 3个未解析的外部符号,相对少很多
  • 依赖 Windows API:
    • WaitForMultipleObjectsEx:Windows API,用于等待多个对象状态
    • GetLogicalProcessorInformation:获取 CPU 核心信息,确定物理并发度
    • LocalFree:释放由 FormatMessage 分配的内存
      说明:
      boost::thread 依赖较少,且这些 Windows API 通常在用户模式存在,内核模式下可能缺失。

3. system_errorLocalFree

  • system_error 的错误信息获取机制依赖 FormatMessage
  • FormatMessage 可能分配内存,释放需要 LocalFree
  • Boost 允许 FormatMessage 自行分配缓冲区
  • Visual C++ STL 实现会多次分配大缓冲区(分别为32767个char和wchar_t)

4. 线程栈大小

  • Windows kernel 线程栈大小约:
    • 12 KB (32位)
    • 24 KB (64位)
    • Itanium:32 KB + 32 KB backing store
  • RTX 实时操作系统默认 8 KB,可以在线程创建时指定
    注意:
    线程栈大小远比普通应用小得多,写代码时必须注意栈使用,避免栈溢出。静态分析工具可以帮助识别大栈使用函数。

综合理解

方面 说明
std::thread 实现 依赖微软的并发运行时,内核模式难支持,导致多链接错误
boost::thread 实现 依赖较少,但仍依赖部分 Windows API,可能限制内核或实时使用
错误处理 C++库错误消息依赖 Windows 内存管理函数,可能导致额外资源管理难题
线程栈 内核/RTOS线程栈更小,需注意栈空间节约
如果你想,我可以帮你:
  • 提供适合内核/RTOS环境的线程启动与同步代码示例
  • 介绍如何绕开 Windows API 限制,自定义轻量线程模型
  • 讲解如何进行线程栈大小分析和优化

内核模式或实时系统中使用C++线程、并发、计时器、异常处理时的各种挑战和限制,帮你总结归纳一下:

1. 线程实现相关限制

  • 没有堆栈保护页(no stack guard pages),内核线程栈溢出风险较大。
  • 线程栈大小必须显式指定,且在 Windows 创建线程时,栈的“提交大小(commit)”必须等于“保留大小(reserve)”。
  • 线程栈大小参数 STACK_SIZE_PARAM_IS_A_RESERVATION 指示栈的保留内存。
  • 无内存保护机制,导致线程栈错误难以检测。
  • **AddressSanitizer(内存错误检测工具)**需要 Windows/Linux 环境支持,内核/RTOS中难以使用。
  • 参考标准提案:P0320(线程相关)和 P0484。

2. RTX与C++并发特性支持

特性 支持情况
原子操作(atomics) 支持良好
锁守卫(lock guards) 支持良好
异步操作(async) 不支持
future、promise、packaged_task 不支持
条件变量(condition_variable) 不支持
互斥锁(mutex) 不支持
协程(coroutines) 不支持(额外幻灯片有说明)
call_once 状况不明

3. 线程安全初始化

  • 例子:
    struct bar {
        std::string s;
    };
    void foo() {
        static bar b; // 静态局部变量,线程安全初始化问题
    }
    
  • 线程安全初始化在 Visual C++ 2017 加入了 /Zc:threadSafeInit 编译选项。
  • 但是 RTX 运行时调用此特性会导致崩溃,说明内核/RTOS环境中这项功能还不可靠。

4. 计时器与时钟

  • 自定义时钟结构:
    struct clock {
        using duration = std::chrono::duration<int64_t, std::ratio<1, 10'000'000>>;
        using rep = duration::rep;
        using period = duration::period;
        using time_point = std::chrono::time_point<clock>;
        static constexpr bool is_steady = false;
        static time_point now() noexcept;
    };
    
  • 时间戳计数器(TSC)
    • 读操作快
    • 增长恒定(Invariant TSC)
    • 但不保证不同CPU核同步
    • RTX按需启动时,时间差(Δt)较大
  • 高精度事件计时器(HPET)
    • 从2004年开始普及
    • 单个32/64位计数器寄存器,3个以上比较器
    • 不依赖具体核心
    • 读取周期多于TSC
  • HPET示例代码:
    struct hpet {
        hpet() {
            // 启用定时器
        }
        ~hpet() {
            // 恢复系统原状,关闭定时器,计数器清零
        }
    };
    

5. 异常处理

  • 简单示例:
    #include 
    bool bar();
    void foo() {
        if (!bar()) {
            throw std::out_of_range("bar is upset");
        }
    }
    
  • 更确定性的异常:
    #include 
    struct bar_upset_exception : std::exception {
        const char* what() const noexcept override {
            return "bar is upset";
        }
    };
    bool bar();
    void foo() {
        if (!bar()) {
            throw bar_upset_exception();
        }
    }
    

总结

  • 内核模式或实时系统的线程管理需要显式栈大小配置且没有保护机制,容易导致崩溃。
  • RTX环境中C++标准并发库只支持基础原子操作和锁守卫,复杂并发机制不支持。
  • 静态局部变量的线程安全初始化在RTX中不可靠。
  • 时间相关功能多依赖硬件时钟,如TSC和HPET,需考虑同步和精度问题。
  • 异常处理可以用,但需要更小心地设计异常类型和行为以保证确定性。

围绕内核模式和实时系统中异常处理、调试、跨语言支持、硬件检测与性能监控,帮你总结重点和理解:

1. 从结构化异常处理(SEH)中抛异常

  • /EHa 编译选项支持从 SEH(Structured Exception Handling)抛出 C++ 异常。
  • 可以用 SetUnhandledExceptionFilter 捕获未处理异常。
  • 64位 Windows 支持捕获异常时的调用栈回溯(stack backtrace)。
  • Raymond Chen 博客详细讨论了相关的未定义行为问题:
    Old New Thing Blog

2. 异常时的调用栈追踪

  • Boost 1.65 提供了堆栈追踪(stacktrace)库。
  • 宏选项:
    宏名 作用
    BOOST_STACKTRACE_USE_WINDBG 使用COM接口显示调试信息
    BOOST_STACKTRACE_USE_CACHED 缓存COM实例以提高频繁采集时的性能
    BOOST_STACKTRACE_USE_NOOP 禁用调用栈追踪,stacktrace::size() 永远返回0

3. 内核模块中的地址偏移问题

  • 内核模块不能用绝对地址,必须用偏移量。
  • 这和地址空间布局随机化(ASLR)策略类似,因为内核模块加载基地址不固定。

4. 模块和运行时支持

  • Visual C++ 只支持 DLL 运行时,这在内核模块环境是个限制。

5. 不仅仅是C++

  • RTX 内核应用可以链接多种语言,如 Fortran、D、Lua。
  • Lua 由 C 实现,能在 RTX 内核中调用。
  • 非确定性排序函数可能会被使用。

6. Fortran代码解析

  • Intel Fortran 依赖于 C 数学库函数(如 pow)满足符号依赖。
  • 字符串操作(比较、拼接、复制)方面,Compaq Fortran 不需要运行时支持。
  • 对于边界检查,Intel Fortran 提供错误报告,Compaq Fortran 不支持生成边界检查。

7. 内存保护扩展(MPX)

  • Intel SkyLake 及以后CPU支持 MPX,用于数组边界检查的新寄存器和指令。
  • 自定义线程实现可以方便创建MPX资源和寄存器编程。
  • Intel MPX开发指南:链接PDF

8. MPX与编译器

  • GCC 5开始加入MPX支持,GCC 6默认启用,7.0版本是否弃用尚不明确。
  • Visual C++ 2015 update 1 加入了 MPX 的实验性编译标志 /d2MPX

9. 性能监控限制

  • 内核模式应用缺乏诸如 perf、valgrind、Intel PMU、vTune、Windows Performance Analyzer 等工具支持。
  • 名言:“你无法测量你未测量的” — Alexandrescu。
  • 但可自行用指令 rdmsr/wrmsr 访问性能监控寄存器。
  • 还可以直接访问内存控制器性能计数器和缓存管理。
  • 支持 QoS(服务质量)I/O监控。
    如果你想,我可以帮你:
  • 讲解如何用 SEH 抛异常,或者怎样在内核中实现异常捕获和栈追踪;
  • 介绍 MPX 的使用原理及在内核环境如何配置;
  • 或者设计一个简单的性能计数器采集框架示例。

1. 内核模式下的 C++ 编程

  • 在内核环境(非操作系统内核本身)中用 C++ 编程是可行且更容易的,但前提是有合适的执行环境。
  • 由于编译器和标准库通常不专门支持内核环境,实际开发中需要:
    • 对 Boost 等库做修改以适配内核;
    • 可能需要自行实现标准库或运行时的一部分功能;
    • 放弃某些标准功能,因为它们依赖于用户态环境或操作系统支持。

2. 协程(Coroutines)支持

  • Visual C++ 2017 已经支持了协程技术(TS),且在 RTX 实时系统下运行示例代码是没问题的。
  • 但是,基于 std::futurestd::promise 的标准异步机制无法正常工作。

3. 其他标准 C++ 问题

  • assert 断言会尝试弹出消息框,这在内核模式或无图形环境下不适用。
  • 网络技术规范(Networking TS)依赖标准的线程和并发功能,使用上存在问题。
  • Visual C++ 的实现通常依赖 Windows 套接字(Winsock)兼容的网络栈,这在内核或特殊环境下不可用。
  • 定时器等基础设施可能不可用或有限制。

你可能感兴趣的:(CppCon,学习,c++,开发语言)