问题不在于Linux内核.它也不是C与C++的争论.
我做了一项研究,在我看来,C++在嵌入式系统的异常处理和内存分配方面缺乏工具支持:
为什么linux内核没有在C++中实现? 除了公认的答案,请参阅Ben Collins的回答.
"[...]任何为C++设计内核模块的人都是[...]
(b)无法看到他正在编写的C++偏见,实际上只是C"" - 整个C++异常处理事情从根本上被打破了.对于内核来说尤其突破.
- 任何喜欢隐藏内存分配等内容的编译器或语言都不是内核的好选择."
"不得使用AV规则208 C++例外"
异常处理和内存分配是C++显然缺乏工具支持的唯一要点(在此上下文中)?
要解决异常处理问题,必须提供时间限制,直到抛出异常后才捕获异常?
你能解释一下为什么内存分配是个问题吗?如何克服这个问题,必须做些什么?
正如我所看到的,在这两种情况下,必须在编译时为发生的非常重要的事情提供上限,并在运行时依赖于事物.
回答:
Lin*_*ios 14
嗯,有几件事.首先,您必须记住STL完全基于OS例程,C标准库和动态分配.当您编写内核时,没有动态内存分配给您(您提供它)没有C标准库(您必须提供一个内置于内核之上),并且您正在提供系统调用.然后有一个事实是C很好地与汇编很好地交互,而C++很难与汇编接口,因为ABI不一定是常数,也不是名称.由于名称损坏,您将获得全新的并发症.
然后,您必须记住,在构建操作系统时,您需要了解并控制内核使用的内存的各个方面.在C++中,有很多隐藏的结构你无法控制(vtable,RTTI,异常)会严重干扰你的工作.
换句话说,Linus所说的是,使用C,您可以轻松地理解正在生成的程序集,并且它非常简单,可以直接在计算机上运行.虽然C++可以,但你总是需要设置相当多的上下文,并且仍然需要一些C来连接程序集和C.另一个原因是在系统编程中,你需要确切地知道如何调用方法.C有很好的文档C调用约定,但在C++中你必须this处理,命名修改等.
简而言之,这是因为C++在没有你问的情况下做事情.
Per @Josh的评论如下:C++在你背后做的另一件事是构造函数和析构函数.它们增加了进入和退出堆栈帧的开销,最重要的是,使组装互操作更加困难,因为当您销毁C++堆栈帧时,您必须调用其中每个对象的析构函数.这很快就变丑了.
为什么某些内核拒绝代码库中的C++代码?政治和偏好,但我离题了.
现代OS内核的某些部分是用C++的某些子集编写的.在这些子集中,主要禁用异常和RTTI(有时也不允许多重继承和模板).
在C中也是如此.某些特征不应该在内核环境中使用(例如,VLA).
在异常和RTTI之外,当我们讨论内核代码(或嵌入代码)时,C++中的某些特性受到严厉批评.这些是vtable和构造函数/析构函数.他们带来了一些代码,这似乎被认为是"糟糕的".如果您不想要构造函数,则不要实现构造函数.如果您担心使用带有构造函数的类,那么也要担心必须使用的函数来初始化结构.C++的优点是,你不能忘记在忘记释放内存之外使用dtor.
但是vtable怎么样?
当您实现包含扩展点的对象(例如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();
}
Run Code Online (Sandbox Code Playgroud)
我们可以在这个函数中推断每个出口路径,因为我们可以很容易地看到所有返回,但是当启用异常时,函数可能会抛出,我们无法保证函数采用的实际路径是什么.这是代码可以做一些我们无法立即看到的事情的确切点.(当启用异常时,这是错误的C++代码).
第三点是,您希望用户模式应用程序崩溃,当出现意外情况时(例如,当内存耗尽时),用户模式应用程序应该崩溃(在释放资源之后)以允许开发人员调试问题或者至少获得一个好处错误信息.你不应该在内核模式模块中有一个未被捕获的异常.
请注意,所有这些都可以克服,Windows内核中存在SEH异常,因此点2 + 3在NT内核中并不是真正的好点.
内核中没有C++的内存管理问题.例如,NT内核头文件为new和delete提供了重载,这使您可以指定分配的池类型,但与用户模式应用程序中的new和delete完全相同.