为什么某些内核在其代码库中拒绝 C++ 代码?政治和偏好,但我离题了。
现代操作系统内核的某些部分是用 C++ 的某些子集编写的。在这些子集中,主要是异常和 RTTI 被禁用(有时也不允许多重继承和模板)。
在 C 语言中也是如此。某些特性不应该在内核环境中使用(例如 VLA)。
当我们谈论内核代码(或嵌入式代码)时,除了异常和 RTTI 之外,C++ 中的某些特性受到了严厉的批评。这些是 vtable 和构造函数/析构函数。他们在引擎盖下带来了一些代码,这似乎被认为是“坏的”。如果你不想要一个构造函数,那么就不要实现一个。如果您担心使用带有构造函数的类,那么也要担心必须用来初始化结构的函数。C++ 的好处是,除了忘记释放内存之外,您真的不能忘记使用 dtor。
但是 vtables 呢?
当您实现一个包含扩展点的对象时(例如,一个 linux 文件系统驱动程序),您实现的就是一个带有虚拟方法的类。那么,为什么有一个 vtable 这么糟糕呢?当您对 vtable 所在的页面有特定要求时,您必须控制此 vtable 的放置。据我回忆,这与linux无关,但在windows下,代码页可以被分页,当你从一个太高的irql调用一个分页函数时,你会崩溃。但是你真的必须注意你调用了什么函数,当你处于高 irql 时,不管它是什么函数。如果您在这种情况下不使用虚拟呼叫,您也不必担心。在嵌入式软件中,这可能会更糟,因为(很少)您需要直接控制代码所在的代码页,
那么为什么这么多人如此坚持“在内核中使用 C”呢?
因为他们要么被工具链问题烧毁,要么被过度热情的开发人员在内核模式下使用最新的东西烧毁。
可能内核模式的开发者比较保守,C++是个太新奇的东西……
为什么内核模式代码中不使用异常?
因为他们需要为每个函数生成一些代码,在代码路径中引入复杂性并且不处理异常对于内核模式组件是不利的,因为它会杀死系统。
在 C++ 中,当抛出异常时,堆栈必须展开,并且必须调用相应的析构函数。这至少涉及一些开销。这几乎可以忽略不计,但确实会产生成本,这可能不是您想要的。(请注意,我不知道展开表的实际成本是多少,我想我读到没有异常运行时没有成本,但是......我想我必须查一下)。
不能抛出异常的代码路径可以更容易推理,然后可能。所以 :
int f( int a )
{
if( a == 0 )
return -1;
if( g() < 0 )
return -2;
f3();
return h();
}
在这个函数中,我们可以推断每条退出路径,因为我们可以很容易地看到所有返回,但是当启用异常时,函数可能会抛出,我们不能保证函数采用的实际路径是什么。这是代码的确切点可能会做一些我们无法立即看到的事情。(当启用异常时,这是糟糕的 C++ 代码)。
第三点是,您希望用户模式应用程序崩溃,当发生意外情况时(例如内存耗尽),用户模式应用程序应该崩溃(释放资源后)以允许开发人员调试问题或至少获得一个好的错误信息。您永远不应该在内核模式模块中有未捕获的异常。
请注意,这一切都可以克服,Windows 内核中存在 SEH 异常,因此第 2+3 点在 NT 内核中并不是真正的好点。
内核中的 C++ 没有内存管理问题。例如,NT 内核头文件为 new 和 delete 提供重载,它允许您指定分配的池类型,但在其他方面与用户模式应用程序中的 new 和 delete 完全相同。