【问题标题】:How much of C++11 is usable in Windows KernelWindows 内核中有多少 C++11 可用
【发布时间】:2016-03-01 06:50:56
【问题描述】:

最新的 WDK 已交付用于支持 C++11 的 Visual Studio 15。

但是,我还没有看到有多少功能可用的文档。

显然,我不会使用 std::threadstd::mutex,但不太清楚的是,是神奇的静态。

Class * function()
{
     static Class myInstance;

     return &myInstance;
}

现在在用户模式下是线程安全的,但尚不清楚这种构造是否可以在内核中工作。

更令人担忧的是,内核中是否可以接受 C++11 之前的代码(假设析构函数是微不足道的)。

【问题讨论】:

  • FWIW,在 g++ 中我看到这是用隐藏的自旋锁实现的,这在内核模式下也应该是安全的。
  • 可以使用“C with classes”编码风格。但可以肯定的是,到处都是陷阱。您认为这样的 static 声明不再起作用是正确的,代码生成依赖于 TEB 和线程本地存储。

标签: windows c++11 windows-kernel


【解决方案1】:

我找到了一些关于开关 /kernel msdn : /kernel (Create Kernel Mode Binary) 的文档,其中描述了一个开关,用于告诉编译器 .obj 是用于内核模式的。

这描述了异常和 RTTI 的不同行为,但没有提到神奇的静态。

从一个简单的可见反编译

Class * GetInstance()
{
   static Class instance;
   return &instance;
}

还有一些用户态测试,编译器

  1. 不发出任何线程本地代码。
  2. 不提供来自用户模式的线程安全行为。

编辑

为了清楚起见。当/kernel 未指定时,魔术静态工作和静态初始化是线程安全的。该机制(使用 fs:) 不适用于内核。

/kernel 的代码不是线程安全的,但与内核兼容。 (没有提到fs:)

【讨论】:

    【解决方案2】:

    除非您计划完全重写内核并部署一个完整的新标准化导出名称(这意味着丢弃所有现有的可执行文件),我认为这几乎是不可能的。许多 C++ 特性实际上是元函数,编译器必须将其转换为存在的东西。

    std::thread 这样的东西可以被实现为内核资源的包装器(这很可能是一个“指向数据结构的指针:这就是 HANDLE 的真正含义),通过让这些结构驻留在另一个处理并对 client 程序完全不透明。

    函数调用这样的东西来保持这种不透明度需要一个所有语言都可以依赖的标准ABI(应用程序二进制接口),同时生成必须链接的目标代码.

    现在,C ABI - 由于缺少重载 - 是微不足道的(只需将 _ 附加到函数名称:这是 Windows 内核库采用的约定,每个编译器开发人员在制作它的 windows 版本),但 C++ 名称修饰是......专有的(没有关于它必须如何在系统级别完成的定义,所以每个编译器都有自己的)。

    因此,唯一可靠的内核将是仅导出extern "C" 的内核。 它可以在内部用 C++ 编写,但它的接口在任何情况下都必须是纯 C(这只是你的 class* function();,它在语义上与 HANDLE CreateClass() 没有区别。

    请注意,即使在 C++ 规范级别定义标准 ABI 也无济于事:所有 C++ 编译器都将链接相同,但不需要遵循其他语言。

    而且系统级 ABI 必须足够简单,才能不需要并非所有语言都必须支持的客户端语言特性(如函数重载)。

    更复杂的是,C++ 也有模板,在编译期间实例化代码,并且无法跨进程边界或内核屏障可靠地实现:想象内核内部有 vector<int>vector<double>:我可以依赖在系统上我必须使用这些功能,但是vecotr<foo> 呢?

    在我的机器上编译我的软件期间,我如何要求我所有用户的系统内核使vector<foo> 存在?这可以在安装过程中完成,但同样需要对通信接口进行更多标准化。

    内核是存在于汇编器级别的物理资源的管理器。用 C 表示它们几乎是微不足道的。如果您使用 C++ 低级功能(与 C 基本相同),用 C++ 表示它们仍然是微不足道的,但是 - 一旦您升入抽象 - 您会立即遇到标准化缺失和关于位置的复杂非平凡选择以及如何实现这一点将转化为未来对内核级别的使用可能没有如此明确定义的事物的约束。


    当然,原则上没有什么禁止在内核实现内部使用 C++,但这会在跨资源使用的边界管理中产生问题。

    实际的协议是“让我访问资源”和“保留您的资源”。

    客户端程序可以将这两个调用包装在一个 RAII 类中,并在其上应用所有 C++11 功能,但是将其移到边界的另一侧呢? 内核可以返回win::unique<something> 吗?嗯......不是真的,因为你不能破坏你自己进程中没有的东西:那个“samrt句柄”将有一个析构函数,导致另一个内核函数调用。 o 内核在任何情况下都会有一个单独的创建/获取和删除/释放函数。

    我当然可以根据智能句柄为您的 C++ 程序开发一个 API,但程序与内核的通信仍将通过普通函数(因此智能句柄都管理到客户端,无论如何)

    类似地,如果调用都是同步的,则堆栈展开有效,但创建/删除本身不是同步的(它们不是在另一个内部调用)。它们通过 RAII 模式通过驻留在您的堆栈中的本地对象“同步”。但是*HPEN(注意取消引用)结构不在您的堆栈中:位于另一个进程(GDI 驱动程序)中,因此smart<HPEN> 必须在构造时调用CreatePen,在销毁时调用DeletePen,因为内核 -本身 - 无法知道您的“展开”(即您的,而不是内核)何时会到达正确的点。

    C++ //in kernel// 可以存在于完全由内核本身在同一个内核调用中同步构造和销毁的对象,然后 - 结果 - 不会离开内核,以某种方式被内核使用客户。这当然是可能的,但是由于内核的目的是导出功能并且导出需要平面 API,因此您很快将使用 C++ 内核导出客户端 C++ 包装器使用的普通 C 接口。

    我怀疑这种努力是否合理。

    【讨论】:

    • 抱歉,这没什么意义。运算符重载由编译器解决,在运行时不可见。这意味着它在运行时对内核也是不可见的。同样适用于模板。至于std::vector<double>,和MyType一样安全通过。在内核级别,您使用预定义格式的序列化数据进行通信。
    • 此答案侧重于内核中的 C++,而不是内核中的 C++11。我知道 ABI 问题,但我的问题是 C++xx 进程似乎给编译器增加了更多责任(安全堆栈展开、线程安全静态变量初始化),几乎没有证据表明在为内核编译的代码,可能会产生不良的副作用。
    • @MSalters 我不是在谈论现有的内核,而是最终重写的 C++ 内核。函数重载(不一定是运算符重载)也可以跨 DLL 边界实现。 “内核”有意义的地方。如果您坚持编译“本地”解决方案,那么您不会转向 C++ 内核,而是停留在 C 中。这正是我试图以荒谬的方式向这位教授展示的内容。你得出了和我一样的结论。
    • @EmilioGaravaglia:在那个内核中,您将定义 C++ ABI,就像今天的内核定义它们的 C ABI 一样。是的,你需要谈谈堆栈,就像今天的内核已经必须做的那样 - 内核堆栈不会像用户模式堆栈那样增长。此外,“跨 DLL 重载”是一个用户模式运行时概念,因此与 C++ 无关,因此在这两个方面它都与“内核中的 C++”无关。
    • 一个 C++ 内核和一个基于 C++ 的系统 ABI可以完成(参见 BeOS,现在是 Haiku);还可以在模板类型上保持二进制兼容性(参见 Qt,但在某种程度上也可以参见 libstdc++)。但在我看来,这个问题完全不同,而且更侧重于特定问题(即在特定驱动程序的代码中使用 C++11)。
    猜你喜欢
    • 2016-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-28
    • 2011-08-06
    相关资源
    最近更新 更多