Why are drivers and firmware almost always written in C or ASM, and not in C ++?

I'm just wondering why drivers and firmware are almost always written in C or Assembly, and not in C ++?

I heard that there is a technical reason for this.

Does anyone know this?

Lots of love, Louise

+41
c ++ c
Jan 11 '10 at 1:34
source share
15 answers

Because most of the time, the operating system (or the "runtime library") provides the stdlib functionality required by C ++.

In C and ASM, you can create simple executables that do not contain external dependencies.

However, since windows support stdlib C ++, most Windows drivers are written in (a limited subset of) C ++.

Also, when the firmware is written by ASM, this usually happens because either (A) on which it is running does not have a C ++ compiler, or (B) there are speed or size restrictions.

Please note that (B) has generally not been a problem since the early 2000s.

+28
Jan 11 '10 at 1:37
source share

The code in the kernel works in a completely different environment than in user space. There is no process separation, so errors are much more difficult to recover; exceptions are largely out of the question. There are different memory allocators, so it may be harder to get new and delete to work correctly in the kernel context. There is less standard library available, which makes it difficult to use a language like C ++.

Windows allows the use of a very limited subset of C ++ in kernel drivers; essentially those things that can be trivially translated into C, such as variable declarations in places other than the start of blocks. They recommend using new and delete and do not support RTTI or most of the C ++ standard library.

Mac OS X uses the I / O Kit , which is a framework based on a limited subset of C ++, although, as far as I can tell, it is more complete than what is allowed on Windows. This is essentially C ++ with no exceptions and RTTI.

Most Unix-like operating systems (Linux, BSD) are written in C, and I think no one has ever seen the benefits of adding C ++ support for the kernel, given that C ++ in the kernel is usually so limited.

+25
Jan 11 '10 at 1:56
source share

1) "Because it has always been like that," - this actually explains more than you think - given that the APIs in almost all existing systems were originally written based on the C or ASM model, and given that a lot The previous code exists in C and ASM, it is often easier to "go with the stream" than to determine how to use C ++.

2) The environment. To take full advantage of C ++, you need a pretty runtime environment, some of which are just painful to provide to the driver. This is easier to do if you limit your set of functions, but, among other things, memory management can be very interesting in C ++ if you have a little heap. Exceptions are also very interesting to consider in this environment, like RTTI.

3) "I do not see what he is doing." For any reasonably skilled programmer, you can take a look at line C and have a good idea of ​​what is going on at the machine code level to implement this line. Optimization is obviously changing a little, but for the most part you can tell what is going on. In C ++, with a given operator overload, constructors, destructors, exceptions, etc., it is very difficult to understand what will happen with this line of code. When writing device drivers, this can be deadly because you often MUST know if you will interact with the memory manager, or if the line of code affects (or depends on) the levels of interruptions or masking.

You can completely write device drivers for Windows using C ++ - I did it myself. The caveat is that you have to be careful which C ++ functions you use and where you use them from.

+11
Jan 11 '10 at 2:36
source share

With the exception of broader tool support and hardware portability, I don’t think there is a good reason to limit myself to C more. I often see complex manual-encoded material made in C, which can more naturally be done in C ++:

  • Grouping into “modules” functions (not general purpose) that work only with the same data structure (often called an “object”) → Use C ++ classes.
  • Using the handle pointer so that the module functions can work with instances of data structures → Use C ++ classes.
  • Using functionally similar macros -> C ++ templates and built-in functions
  • Different runtime behavior depending on the type identifier using the manual table vtable ("handle") or sent using the switch statement → C ++ polymorphism
  • Local arithmetic of error pointers for sorting / unmarching data from / to the communication port or using non-portable structures → C ++ stream concept (not necessarily std :: iostream)
  • The prefix trait from everything that avoids name conflicts: C ++ namespaces

None of the C ++ functions described above are worth more than hand-written C implementations. I probably missed something else. I think that the inertia of C in this area is more related to the fact that C is mainly used.

Of course, you cannot use STL freely (or in general) in a restricted environment, but this does not mean that you cannot use C ++ as "better than C".

+10
Jan 11 '10 at 2:45
source share

The biggest reason to use C instead of saying that highly protected Java is that it is very easy to keep track of what kind of memory is used for a given operation. C is very address oriented. A key issue when writing kernel code is the failure of a memory link, which can cause the page to crash at an uncomfortable moment.

C ++ can be used, but only if the runtime is specifically designed to refer only to internal tables in fixed memory (cannot be viewed) when the runtime mechanism is invoked implicitly, for example, using a virtual table when invoking virtual functions. This special adaptation is not used out of the box most of the time.

Integrating C with the platform is much easier to use, as it is easy to remove C from its standard library and have full control over memory access. So, what is also a well-known language with it, it is often the choice of kernel tool developers.

Edit : the link to new and deleted calls has been removed (this was incorrect / misleading); replaced by the more general phrase "work time".

+7
Jan 11 '10 at 1:48
source share

The reason C is used, not C ++, is NOT:

  • Since C ++ is slower
  • Or because c-runtime is already present.

This is because C ++ uses exceptions. Most implementations of C ++ exceptions are unsuitable for use in driver code, because drivers are called when the OS responds to hardware interrupts. During a hardware interrupt, the driver code is NOT allowed to use exceptions, as this could / could cause recursive interrupts. In addition, the stack space available to the code, while in the context of interruption, is usually very small (and cannot be obtained due to the exception exclusion rule).

Of course, you can use the new one (std :: nothrow), but since exceptions from C ++ are now ubiqutious, this means that you cannot rely on any library code to use the semantics of std :: nothrow.

This is also because C ++ has abandoned certain C functions: - It is important to place code in drivers. Device drivers must be able to respond to interrupts. The interrupt code MUST be placed in code segments that are not paged or permanently mapped into memory, because if the code was in paged memory, it can be unloaded when called, which will result in an exception that is prohibited. C compilers that are used to develop drivers have #pragma directives that can control which type of memory function ends. Since nonpaged pool is a very limited resource, you DO NOT want to mark your entire driver as nonpaged: C ++ generates a lot of implicit code. For example, default constructors. It is not possible to copy implicitly generated C ++ code to control its placement, and since conversion operators are automatically called, there is no way to check the code to ensure there are no side effects when calling the code.

So, we summarize: - The reason why C, and not C ++ is used to develop drivers, is that drivers written in C ++ will either consume unreasonable amounts of unloaded memory, or the kernel of the OS will crash.

+7
Jan 11
source share

Comments that I came across why the store uses C for the embedded system compared to C ++:

  • C ++ creates bloat code
  • C ++ exceptions are too many number.
  • C ++ polymorphism and virtual tables use too much memory or run time.
  • People in the store do not know the C ++ language.

The only valid reason may be the last. I have seen C programs that include OOP, function objects, and virtual functions. It gets very ugly very fast and inflates code.

Exception handling in C, if implemented correctly, takes up a lot of space. I would say roughly the same as C ++. Advantage for C ++ exceptions: they are in the language, and programmers do not need to redo the wheel.

The reason I prefer C ++ - C in embedded systems is because C ++ is a more complex typed language. During compilation, you can find more problems, which reduces development time. In addition, C ++ is a simpler language for implementing object-oriented concepts than C.

Most of the reasons against C ++ are related to design concepts, not the actual language.

+7
Jan 11 '10 at 18:13
source share

C is very close to machine independent assembly language. Most OS-type programs are at the bare metal level. With C, the code you are reading is the actual code. C ++ can hide what C cannot.

This is just my opinion, but I spent a lot of time in my life debugging device drivers and OS-related stuff. Often looking through assembly language. Keep it simple at a low level and give the app a level of imagination.

+5
Jan 11
source share

Windows drivers are written in C ++.
Linux drivers are written in c because the kernel is written in c.

+3
Jan 11 '10 at 1:39
source share

The reason drivers and firmware are mostly written in C or ASM is independent of the actual runtime libraries. If you could imagine this imaginary driver written in C here

 #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 ());
 }

Please note that during compilation / link, it will be necessary to insert and link the runtime library support, it will not work, since the runtime (that is, when the operating system is at the boot / initialization stage) is not fully installed and therefore will not there’s no clue about how printf , and it will probably sound the death ring of the operating system (kernel panic for Linux, blue screen for Windows), since there is no link to how to execute the function.

To deliver in another way, with the driver, this driver code has the right to execute the code together with the kernel code that will use the same space, ring0 is the ultimate privilege of code execution (all instructions are allowed), ring3 is where the front part of the operating room the system works (limited execution privilege), in other words, the code ring3 cannot have a command reserved for ring0, the kernel will kill the code, catching it, as if to say: "Hey, you do not have the privilege to test the domain ring0.

Another reason it is written in assembler is mainly related to the size of the code and the source speed, it could be, say, a serial port driver, where the input / output is “critical” for a function with respect to time, latency, and buffering.

Most device drivers (in the case of Windows) will have a special compiler combination ( WinDDK ) that can use C code but is not related to the usual standard C runtime libraries.

There is one toolkit that can help you create a driver in Visual Studio, VisualDDK . By all means, creating a driver is not for the faint of heart, you will get activity caused by stress, looking at blue screens, kernel panic and wonder why, debugging drivers, and so on.

The debugging side is more complicated, the ring0 code is not easily accessible by the ring3 code, since the doors to it are closed, through the door of the kernel trap (due to the lack of a better word) and, if you politely ask, the door still remains closed when the kernel delegates the task to the handler living on ring0 executes it, any results are returned back to ring3 code, and the door is still closed. This is a similar concept of how userland code can execute privileged code on ring0.

In addition, this privileged code can easily spoil the kernel memory space and damage something, hence the kernel panic / bluescreens ...

Hope this helps.

+2
Jan 11 '10 at 2:08
source share

Perhaps because the driver does not require object-oriented functions, the fact that C still has slightly more mature compilers will make a difference.

+1
Jan 11 '10 at 1:37
source share

Probably because c is still often faster, less at compilation and more consistent in compilation between different OS versions and with fewer dependencies. Also, since C ++ is really built on c, the question is, what do you need what it provides?

There is probably something that people who write drivers and firmware are usually used to work at the OS level (or lower) that is in c, and therefore are used to use c for this type of problem.

+1
Jan 11 '10 at 1:40
source share

There are many types of programming, such as procedural, functional, object-oriented, etc. Object-oriented programming is more suitable for modeling the real world.

I would use object oriented device drivers if it suits them. But most of the time when you program device drivers, you do not need the benefits provided by C ++, such as abstraction, polymorphism, code reuse, etc.

+1
Jan 11
source share

Well, the IOKit drivers for MacOSX are written in a subset of C ++ (with no exceptions, patterns, multiple inheritance). And there is even the possibility of writing Linux kernel modules in haskell.)

Otherwise, C, a portable assembly language, perfectly captures von Neumann's architecture and computational model, allowing you to directly control all its features and weaknesses (for example, von Neumann's bottleneck). C does exactly what it was designed for and fully captures the target abstraction model (well, with the exception of an implicit assumption in a single control flow that could be generalized to cover the reality of hardware flows), and that’s why I believe that this beautifully the language.) The limitation of the expressive ability of the language to such foundations eliminates most of the unpredictable details of the transformation when various computational models are applied to this actual standard. In other words, C forces you to adhere to the basics and allows you to pretty accurately control what you do, for example, when modeling common behavior with virtual functions, you precisely control how function pointer tables are saved and used when comparing with the implicit distribution of vtbl C ++ and control . This is really useful when considering caches.

Having said that, the object paradigm is very useful for representing physical objects and their dependencies. Adding inheritance, we get an object-oriented paradigm, which in turn is very useful for representing the hierarchy of structures and the behavior of physical objects. Nothing prevents anyone from using it and expressing it in C, which allows you to fully control how your objects are created, saved, destroyed and copied. This is actually the approach used in the Linux device model. They got “objects” for representing devices, a hierarchy of objects for modeling power management dependencies and hacked inheritance functionality for representing device families, all done in C.

+1
Jan 13
source share

because from the system level, drivers must control every bit of each byte of memory, another higher language cannot do this or cannot do it initially, only C / Asm reaches ~

0
Jan 11 '10 at 2:15
source share



All Articles