为什么驱动程序和固件几乎总是用 C 或 ASM 而不是 C++ 编写的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2039444/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why are drivers and firmwares almost always written in C or ASM and not C++?
提问by Louise
I am just curious why drivers and firmwares almost always are written in C or Assembly, and not C++?
我只是好奇为什么驱动程序和固件几乎总是用 C 或汇编而不是 C++ 编写的?
I have heard that there is a technical reason for this.
我听说这有技术原因。
Does anyone know this?
有人知道吗?
Lots of love, Louise
很多爱,露易丝
采纳答案by John Gietzen
Because, most of the time, the operating system (or a "run-time library") provides the stdlib functionality required by C++.
因为在大多数情况下,操作系统(或“运行时库”)提供 C++ 所需的 stdlib 功能。
In C and ASM you can create bare executables, which contain no external dependencies.
在 C 和 ASM 中,您可以创建不包含外部依赖项的裸可执行文件。
However, since windows does support the C++ stdlib, most Windows drivers are written in (a limited subset of) C++.
但是,由于 Windows 确实支持 C++ stdlib,因此大多数 Windows 驱动程序都是用(有限的)C++ 编写的。
Also when firmware is written ASM it is usually because either (A) the platform it is executing on does not have a C++ compiler or (B) there are extreme speed or size constraints.
此外,当固件被写入 ASM 时,通常是因为 (A) 它正在执行的平台没有 C++ 编译器或 (B) 存在极端的速度或大小限制。
Note that (B) hasn't generally been an issue since the early 2000's.
请注意,自 2000 年代初以来,(B) 通常不是问题。
回答by Brian Campbell
Code in the kernel runs in a very different environment than in user space. There is no process separation, so errors are a lot harder to recover from; exceptions are pretty much out of the question. There are different memory allocators, so it can be harder to get newand deleteto work properly in a kernel context. There is less of the standard library available, making it a lot harder to use a language like C++ effectively.
内核中的代码运行在与用户空间截然不同的环境中。没有进程分离,所以错误更难恢复;例外几乎是不可能的。有不同的内存分配器,因此在内核上下文中获取new和delete正常工作可能会更困难。可用的标准库较少,因此很难有效地使用像 C++ 这样的语言。
Windows allows the use of a very limited subsetof C++ in kernel drivers; essentially, those things which could be trivially translated to C, such as variable declarations in places besides the beginning of blocks. They recommend against use of newand delete, and do not have support for RTTI or most of the C++ standard library.
Windows 允许在内核驱动程序中使用非常有限的 C++子集;本质上,那些可以简单地转换为 C 的东西,例如除了块开头之外的地方的变量声明。他们建议不要使用newand delete,并且不支持 RTTI 或大多数 C++ 标准库。
Mac OS X use I/O Kit, which is a framework based on a limited subset of C++, though as far as I can tell more complete than that allowed on Windows. It is essentially C++ without exceptions and RTTI.
Mac OS X 使用I/O Kit,这是一个基于 C++ 有限子集的框架,但据我所知比 Windows 允许的更完整。它本质上是 C++,没有例外和 RTTI。
Most Unix-like operating systems (Linux, the BSDs) are written in C, and I think that no one has ever really seen the benefit of adding C++ support to the kernel, given that C++ in the kernel is generally so limited.
大多数类 Unix 操作系统(Linux、BSD)都是用 C 编写的,我认为没有人真正看到向内核添加 C++ 支持的好处,因为内核中的 C++ 通常如此有限。
回答by Michael Kohne
1) "Because it's always been that way" - this actually explains more than you think - given that the APIs on pretty much all current systems were originally written to a C or ASM based model, and given that a lot of prior code exists in C and ASM, it's often easier to 'go with the flow' than to figure out how to take advantage of C++.
1)“因为它一直都是这样”——这实际上比你想象的要解释的更多——考虑到几乎所有当前系统上的 API 最初都是写成基于 C 或 ASM 的模型,并且考虑到许多先前的代码存在于C 和 ASM,“顺其自然”通常比弄清楚如何利用 C++ 更容易。
2) Environment - To use all of C++'s features, you need quite a runtime environment, some of which is just a pain to provide to a driver. It's easier to do if you limit your feature set, but among other things, memory management can get very interesting in C++, if you don't have much of a heap. Exceptions are also very interesting to consider in this environment, as is RTTI.
2) 环境 - 要使用 C++ 的所有功能,您需要相当多的运行时环境,其中一些只是提供给驱动程序的痛苦。如果您限制您的功能集,这样做会更容易,但除此之外,如果您没有太多的堆,内存管理在 C++ 中会变得非常有趣。在这种环境中考虑异常也很有趣,RTTI 也是如此。
3) "I can't see what it does". It is possible for any reasonably skilled programmer to look at a line of C and have a good idea of what happens at a machine code level to implement that line. Obviously optimization changes that somewhat, but for the most part, you can tell what's going on. In C++, given operator overloading, constructors, destructors, exception, etc, it gets really hard to have any idea of what's going to happen on a given line of code. When writing device drivers, this can be deadly, because you often MUST know whether you are going to interact with the memory manager, or if the line of code affects (or depends on) interrupt levels or masking.
3)“我看不到它在做什么”。对于任何相当熟练的程序员来说,查看一行 C 语言并很好地了解在机器代码级别发生了什么来实现该行是可能的。显然优化会有所改变,但在大多数情况下,您可以知道发生了什么。在 C++ 中,给定运算符重载、构造函数、析构函数、异常等,很难知道在给定的代码行上会发生什么。在编写设备驱动程序时,这可能是致命的,因为您通常必须知道是否要与内存管理器交互,或者代码行是否影响(或依赖)中断级别或屏蔽。
It is entirely possible to write device drivers under Windows using C++ - I've done it myself. The caveat is that you have to be careful about which C++ features you use, and where you use them from.
完全有可能使用 C++ 在 Windows 下编写设备驱动程序——我自己已经完成了。需要注意的是,您必须小心使用哪些 C++ 功能,以及从何处使用它们。
回答by Emile Cormier
Except for wider tool support and hardware portability, I don't think there's a compelling reason to limit yourself to C anymore. I often see complicated hand-coded stuff done in C that can be more naturally done in C++:
除了更广泛的工具支持和硬件可移植性之外,我认为没有什么令人信服的理由再限制自己使用 C。我经常看到在 C 中完成的复杂的手工编码的东西,在 C++ 中可以更自然地完成:
- The grouping into "modules" of functions (non-general purpose) that work only on the same data structure (often called "object") -> Use C++ classes.
- Use of a "handle" pointer so that module functions can work with "instances" of data structures -> Use C++ classes.
- Use of function-like macros -> C++ templates and inline functions
- Different runtime behavior depending on a type ID with either hand-made vtable ("descriptor") or dispatched with a switch statement -> C++ polymorphism
- Error-prone pointer arithmetic for marshalling/demarshalling data from/to a communications port, or use of non-portable structures -> C++ stream concept (not necessarily std::iostream)
- Prefixing the hell out of everything to avoid name clashes: C++ namespaces
- 将仅适用于相同数据结构(通常称为“对象”)的函数(非通用)分组为“模块”-> 使用 C++ 类。
- 使用“句柄”指针,以便模块函数可以使用数据结构的“实例”-> 使用 C++ 类。
- 使用类似函数的宏 -> C++ 模板和内联函数
- 不同的运行时行为取决于具有手工制作的 vtable(“描述符”)或使用 switch 语句调度的类型 ID -> C++ 多态性
- 用于从/向通信端口编组/解组数据的易出错指针算法,或使用非可移植结构 -> C++ 流概念(不一定是 std::iostream)
- 为所有内容添加前缀以避免名称冲突:C++ 命名空间
None of the C++ features described above cost more than the hand-written C implementations. I'm probably missing some more. I think the inertia of C in this area has more to do with C being mostly used.
上面描述的 C++ 特性没有一个比手写的 C 实现成本更高。我可能还缺少一些。我认为 C 在这方面的惯性更多地与 C 被广泛使用有关。
Of course, you may not be able to use STL liberally (or at all) in a constrained environment, but that doesn't mean you can't use C++ as a "better C".
当然,您可能无法在受限环境中自由地(或根本无法)使用 STL,但这并不意味着您不能将 C++ 用作“更好的 C”。
回答by Chris Becke
The reason that C, not C++ is used is NOT:
使用 C 而不是 C++ 的原因不是:
- Because C++ is slower
- Or because the c-runtime is already present.
- 因为 C++ 比较慢
- 或者因为 c-runtime 已经存在。
It IS because C++ uses exceptions. Most implementations of C++ language exceptions are unusable in driver code because drivers are invoked when the OS is responding to hardware interrupts. During a hardware interrupt, driver code is NOT allowed to use exceptions as that would/could cause recursive interrupts. Also, the stack space available to code while in the context of an interrupt is typically very small (and non growable as a consequence of the no exceptions rule).
这是因为 C++ 使用异常。大多数 C++ 语言异常的实现在驱动程序代码中是不可用的,因为驱动程序是在操作系统响应硬件中断时调用的。在硬件中断期间,不允许驱动程序代码使用异常,因为这会/可能导致递归中断。此外,在中断上下文中可用于编码的堆栈空间通常非常小(并且由于无例外规则而无法增长)。
You can of course use new(std::nothrow), but because exceptions in c++ are now ubiqutious, that means you cannot rely on any library code to use std::nothrow semantics.
您当然可以使用 new(std::nothrow),但由于 C++ 中的异常现在无处不在,这意味着您不能依赖任何库代码来使用 std::nothrow 语义。
It IS also because C++ gave up a few features of C :- In drivers, code placement is important. Device drivers need to be able to respond to interrupts. Interrupt code MUST be placed in code segments that are "non paged", or permanently mapped into memory, as, if the code was in paged memory, it might be paged out when called upon, which will cause an exception, which is banned. In C compilers that are used for driver development, there are #pragma directives that can control which type of memory functions end up on. As non paged pool is a very limited resource, you do NOT want to mark your entire driver as non paged: C++ however generates a lot of implicit code. Default constructors for example. There is no way to bracket C++ implicitly generated code to control its placement, and because conversion operators are automatically called there is no way for code audits to guarantee that there are no side effects calling out to paged code.
也是因为 C++ 放弃了 C 的一些特性:- 在驱动程序中,代码放置很重要。设备驱动程序需要能够响应中断。中断代码必须放置在“非分页”的代码段中,或永久映射到内存中,因为如果代码在分页内存中,则在调用时可能会被分页,这将导致异常,这是被禁止的。在用于驱动程序开发的 C 编译器中,有 #pragma 指令可以控制最终使用哪种类型的内存函数。由于非分页池是一种非常有限的资源,您不希望将整个驱动程序标记为非分页:然而,C++ 会生成大量隐式代码。例如默认构造函数。没有办法用括号括起来 C++ 隐式生成的代码来控制它的位置,
So, to summarise :- The reason C, not C++ is used for driver development, is because drivers written in C++ would either consume unreasonable amounts of non-paged memory, or crash the OS kernel.
因此,总结一下:- 驱动程序开发使用 C 而不是 C++ 的原因是因为用 C++ 编写的驱动程序要么消耗不合理数量的非分页内存,要么使操作系统内核崩溃。
回答by Thomas Matthews
The comments I run into as why a shop is using C for an embedded system versus C++ are:
我遇到的关于为什么商店将 C 用于嵌入式系统而不是 C++ 的评论是:
- C++ produces code bloat
- C++ exceptions take up too much room.
- C++ polymorphism and virtual tables use too much memory or execution time.
- The people in the shop don't know the C++ language.
- C++ 产生代码膨胀
- C++ 异常占用了太多空间。
- C++ 多态性和虚拟表使用过多的内存或执行时间。
- 店里的人不懂C++语言。
The only valid reason may be the last. I've seen C language programs that incorporate OOP, function objects and virtual functions. It gets very ugly very fast and bloats the code.
唯一有效的原因可能是最后一个。我见过包含 OOP、函数对象和虚函数的 C 语言程序。它变得非常丑陋非常快并且使代码膨胀。
Exception handling in C, when implemented correctly, takes up a lot of room. I would say about the same as C++. The benefit to C++ exceptions: they are in the language and programmers don't have to redesign the wheel.
C 中的异常处理如果正确实现,会占用大量空间。我会说与 C++ 大致相同。C++ 异常的好处:它们在语言中,程序员不必重新设计轮子。
The reason I prefer C++ to C in embedded systems is that C++ is a stronger typed language. More issues can be found in compile time which reduces development time. Also, C++ is an easier language to implement Object Oriented concepts than C.
在嵌入式系统中,我更喜欢 C++ 而非 C 的原因是 C++ 是一种更强的类型语言。在编译时可以发现更多问题,从而减少了开发时间。此外,C++ 是一种比 C 更容易实现面向对象概念的语言。
Most of the reasons against C++ are around design concepts rather than the actual language.
大多数反对 C++ 的原因是围绕设计概念而不是实际语言。
回答by martinr
The biggest reason C is used instead of say extremely guarded Java is that it is very easy to keep sight of what memory is used for a given operation. C is very addressing oriented. Of key concern in writing kernel code is avoiding referencing memory that might cause a page fault at an inconvenient moment.
使用 C 而不是说高度受保护的 Java 的最大原因是很容易看到给定操作使用了哪些内存。C非常注重寻址。编写内核代码的关键是避免在不方便的时候引用可能导致页面错误的内存。
C++ canbe used but only if the run-time is specially adapted to reference only internal tables in fixed memory (not pageable) when the run-time machinery is invoked implicitly eg using a vtable when calling virtual functions. This special adaptation does not come "out of the box" most of the time.
可以使用C++,但前提是当运行时机制被隐式调用时,例如在调用虚拟函数时使用 vtable,运行时专门适用于仅引用固定内存中的内部表(不可分页)。大多数情况下,这种特殊的改编并不是“开箱即用”的。
Integrating C with a platform is much easier to do as it is easy to strip C of its standard library and keep control of memory accesses utterly explicit. So what with it also being a well-known language it is often the choice of kernel tools designers.
将 C 与平台集成要容易得多,因为它很容易从 C 的标准库中剥离出来,并完全明确地控制内存访问。那么它也是一种众所周知的语言,它通常是内核工具设计者的选择。
Edit: Removed reference to new and delete calls (this was wrong/misleading); replaced with more general "run-time machinery" phrase.
编辑:删除了对 new 和 delete 调用的引用(这是错误的/误导性的);替换为更一般的“运行时机器”短语。
回答by Richard Pennington
C is very close to a machine independent assembly language. Most OS-type programming is down at the "bare metal" level. With C, the code you read is the actual code. C++ can hide things that C cannot.
C 非常接近于独立于机器的汇编语言。大多数操作系统类型的编程都处于“裸机”级别。使用 C,您阅读的代码就是实际代码。C++ 可以隐藏 C 不能隐藏的东西。
This is just my opinion, but I've spent a lot of time in my life debugging device drivers and OS related things. Often by looking at assembly language. Keep it simple at the low level and let the application level get fancy.
这只是我的意见,但我在我的生活中花了很多时间调试设备驱动程序和操作系统相关的东西。通常通过查看汇编语言。在底层保持简单,让应用层变得花哨。
回答by Martin Beckett
Windows drivers are written in C++.
Linux drivers are written in c because the kernel is written in c.
Windows 驱动程序是用 C++ 编写的。
Linux 驱动程序是用 c 编写的,因为内核是用 c 编写的。
回答by t0mm13b
The reason that drivers and firmwares are mostly written in C or ASM is, there is no dependency on the actual runtime libraries. If you were to imagine this imaginary driver written in C here
驱动程序和固件主要用 C 或 ASM 编写的原因是,不依赖于实际的运行时库。如果你想像这里用 C 语言编写的这个虚构的驱动程序
#include <stdio.h>
#define OS_VER 5.10
#define DRIVER_VER "1.2.3"
int drivermain(driverstructinfo **dsi){
if ((*dsi)->version > OS_VER){
(*dsi)->InitDriver();
printf("FooBar Driver Loaded\n");
printf("Version: %s", DRIVER_VER);
(*dsi)->Dispatch = fooDispatch;
}else{
(*dsi)->Exit(0);
}
}
void fooDispatch(driverstructinfo *dsi){
printf("Dispatched %d\n", dsi->GetDispatchId());
}
Notice that the runtime library support would have to be pulled in and linked in during compile/link, it would not work as the runtime environment (that is when the operating system is during a load/initialize phase) is not fully set up and hence there would be no clue on how to printf, and would probably sound the death knell of the operating system (a kernel panic for Linux, a Blue Screen for Windows) as there is no reference on how to execute the function.
请注意,必须在编译/链接期间引入和链接运行时库支持,因为运行时环境(即操作系统处于加载/初始化阶段时)未完全设置,因此无法正常工作没有关于如何执行的线索printf,并且可能会敲响操作系统的丧钟(Linux 内核崩溃,Windows 蓝屏),因为没有关于如何执行该函数的参考。
Put it another way, with a driver, that driver code has privilege to execute code along with the kernel code which would be sharing the same space, ring0 is the ultimate code execution privilege (all instructions allowed), ring3 is where the front end of the operating system runs in (limited execution privilege), in other words, a ring3 code cannot have a instruction that is reserved for ring0, the kernel will kill the code by trapping it as if to say 'Hey, you have no privilege to tread up ring0's domain'.
换句话说,对于驱动程序,驱动程序代码有权与共享相同空间的内核代码一起执行代码,ring0 是最终的代码执行权限(允许所有指令),ring3 是前端操作系统运行在(有限的执行权限)中,换句话说,ring3 代码不能有为 ring0 保留的指令,内核将通过捕获它来杀死代码,好像在说'嘿,你没有权限踩向上 ring0 的域'。
The other reason why it is written in assembler, is mainly for code size and raw native speed, this could be the case of say, a serial port driver, where input/output is 'critical' to the function in relation to timing, latency, buffering.
用汇编程序编写的另一个原因主要是为了代码大小和原始本机速度,这可能是串行端口驱动程序的情况,其中输入/输出对与时序、延迟相关的功能“至关重要” ,缓冲。
Most device drivers (in the case of Windows), would have a special compiler toolchain (WinDDK) which can use C code but has no linkage to the normal standard C's runtime libraries.
大多数设备驱动程序(在 Windows 的情况下)都有一个特殊的编译器工具链 ( WinDDK),它可以使用 C 代码但没有与普通标准 C 的运行时库的链接。
There is one toolkit that can enable you to build a driver within Visual Studio, VisualDDK. By all means, building a driver is not for the faint of heart, you will get stress induced activity by staring at blue screens, kernel panics and wonder why, debugging drivers and so on.
有一个工具包可以让您在 Visual Studio 中构建驱动程序VisualDDK。无论如何,构建驱动程序不适合胆小的人,您会因盯着蓝屏、内核崩溃并想知道原因、调试驱动程序等而获得压力诱发的活动。
The debugging side is harder, ring0 code are not easily accessible by ring3 code as the doors to it are shut, it is through the kernel trap door (for want of a better word) and if asked politely, the door still stays shut while the kernel delegates the task to a handler residing on ring0, execute it, whatever results are returned, are passed back out to ring3 code and the door still stays shut firmly. That is the analogy concept of how userland code can execute privileged code on ring0.
调试方面更难,ring0 代码不容易被 ring3 代码访问,因为它的门是关闭的,它是通过内核陷阱门(想要更好的词),如果礼貌地询问,门仍然保持关闭状态,而内核将任务委托给驻留在 ring0 上的处理程序,执行它,无论返回什么结果,都将传递回 ring3 代码,并且门仍然牢牢关闭。这就是用户态代码如何在 ring0 上执行特权代码的类比概念。
Furthermore, this privileged code, can easily trample over the kernel's memory space and corrupt something hence the kernel panic/bluescreens...
此外,这个特权代码可以轻松地践踏内核的内存空间并破坏某些内容,因此内核恐慌/蓝屏......
Hope this helps.
希望这可以帮助。

