C++ In The Linux kernel
An anonymous reader submits "A researcher at Reykjavik University Network Laboratory (netlab.ru.is) has just released a Linux patch allowing for complete kernel-level run-time support for C++ in the Linux kernel, including exceptions, dynamic type checking and global objects (with constructors and destructors) The implementation is based on the C++ ABI in GNU g++, but contains various kernel level optimizations, that reduces the cost of throwing exceptions by an order of magnitude, thus making C++ exceptions viable in several scenarios. Furthermore, the Linux module loader is extended to handle weak symbols in C++, so that dynamic type checking is reduced to a pointer comparison, in contrast to string comparison."
C++ in the Linux Kernel
We have implemented a complete kernel level run-time support for C++ in the Linux kernel. In particular our run-time support enables the full use of C++ exceptions in the Linux kernel, but notably also includes support for global constructors and destructors, and dynamic type checking. Our kernel level support is based on open source commodity components, specifically the GNU gcc/g++ compiler and its exception implementation, the C++ ABI version independent standard interface.
C++ runtime support for 2.6.6 (Last update 27 october 2004)
C++ runtime support for 2.6.9 (Last update 27 october 2004)
Using the C++ runtime support for Linux
Installing the C++ runtime support for Linux
The code is installed by applying a patch to the Linux kernel and enables the full use of C++ using the GNU g++ compiler. Programmers that have used C++ in Linux kernel modules have primarily been using classes and virtual functions, but not global constructors. dynamic type checking and exceptions. Using even this small part of C++ requires each programmer to write some supporting routines. Using the rest of C++ includes porting the C++ ABI that accompanies GNU g++ to the Linux kernel, and to enable global constructors and destructors.
The implementation of the C++ ABI is based on the implementation provided with the source of the GNU g++ compiler. We modified it to run in kernel space, and performed optimizations that reduces the cost of exceptions and dynamic cast considerably. Our paper contains thorough explanations of these optimizations. The cost of throwing an exception one level on a 990 MHz Intel Pentium is around 12-13 micro seconds in the plain GNU g++ implementation, which we reduced to 2.1 micro seconds by modifying the runtime library, including unwinding the stack in one phase, and caching information on exception paths. In contrast the cost of a trivial printk operation -- printk("Error\n") -- is 18 micro seconds.
In addition, we modified the linux kernel module loader to handle C++ weak symbols correctly. GNU g++ associates with each class a type information object that encodes the type of the class as a mangled string and puts a pointer to this object in the virtual table for the class. GNU g++ uses weak symbols to reduce the dynamic type checking to a pointer comparison, thus avoiding the more expensive string comparison. Each time a class, containing virtual functions, is used in a source file, GNU g++ generates the virtual table, type information object and type name string as weak symbols and the user space linker ensures that there is only one copy of this object, which renders the simple pointer comparison sufficient. However, the kernel module loader, which in the 2.6 versions of the kernel is exclusively in kernel space, does not handle these weak symbols correctly and always relocates references to weak symbols to the weak definition within each object file that is being loaded. Therefore multiple type information objects may exist for the same class and pointer comparison becomes insufficient when doing dynamic type check across kernel modules. To avoid this overhead we have modified the kernel module loader to handle these weak symbols; the first time a weak symbol is encountered it is added to the symbol map, and on subsequent encounters the relocation is done to the first symbol.
Paper:
Exceptional Kernel: Using C++ exceptions in the Linux kernel
Halldor Isak Gylfason, Gisli Hjalmtysson
Submitted for publication October 2004 abstract
Please direct bug reports, questions and comments to {halldorisak at ru dot is}
Linux doesn't want any C++, so as long as he's at the top, it likely won't get far. And I think that's good, given the state of C++ in gcc (hint: slow and memory-intensive compiles, generating subobtimal code).
But I'll shut up. I've pretty much turned my back on C and C++ anyway.
Please correct me if I got my facts wrong.
In fact, in Linux we did try C++ once already, back in 1992.
It sucks. Trust me - writing kernel code in C++ is a BLOODY STUPID IDEA.
The fact is, C++ compilers are not trustworthy. They were even worse in 1992, but some fundamental facts haven't changed:
* the whole C++ exception handling thing is fundamentally broken. It's _especially_ broken for kernels.
* any compiler or language that likes to hide things like memory allocations behind your back just isn't a good choice for a kernel.
* you can write object-oriented code (useful for filesystems etc) in C, _without_ the crap that is C++.
In general, I'd say that anybody who designs his kernel modules for C++ is either
* (a) looking for problems
* (b) a C++ bigot that can't see what he is writing is really just C anyway
* (c) was given an assignment in CS class to do so.
Feel free to make up (d).
Nothing will come out of it, for the simple reason that C++ does not belong in any kernel. In a kernel, all the code needs to be transparent, and you definitely don't want to hide implementation and the usual abstractions.
The simple reason for that is that otherwise the kernel would be unpredictable. Let's say the error logging function used the string class (which likes to allocate memory behind your back). If the memory allocation function fails and tries to print an error message... you got yourself a kernel crash. This is why the kernel is significantly more difficult to program than, say, a word processor.
I've only written one linux driver, so I'm no expert, but I can think of situations where exceptions can be helpful for device drivers.
Take, for example, a game controller or other hardware device that can become unplugged at any moment. It's useful to have an elegant way of handling this uncommon occurrence.
Exceptions are a useful way to separate uncommon sanity checks from the rest of your code, so you're not forced to use ugly nested conditionals.
It's not the slowliness, it's the obscuirty and the lack of control over the binary code size it introduces. Something as simple as 'a == b' may easily add few KB to the kernel.
If you think it's OK, you obviously haven't been involved in kernel or embedded development. If you say one should be careful what features of C++ he uses and not to use this and that, I say one should learn proper C skills instead.
3.243F6A8885A308D313
exceptions are a great way to structure your error handling.
they're an in-language mechanism similar to signals, only that they don't have the brain-deadedness of them.
Hell, just having smart pointers whose destructors are properly called on the stack unwind is enough.
Care about electronic freedom? Consider donating to the EFF!
Worse than C++.
Objective-C is too dynamic, and all objects are heap objects.
I think that's a bit backwards; ordinary user applications can just terminate ungracefully, and the kernel will clean up after them (close all open files, free memory, etc) if a section of kernel code runs into a problem, it has to roll back everything to a sane state before returning to the caller if there's going to be any hope for the entire kernel to keep going (without leaking). I believe right now the linux kernel mostly does this with an elaborate system of gotos and return value checking that's pretty much exactly what C++ stack-unwinding does, just by hand.
RTFA. This is not about a compiler in the kernel, it's about using C++ instead of C.
Nope. It's a condition that the throwing code couldn't handle. Someone else can handle it.
Classic example: a method calls another that calls another that calls openfile() for a temp file, which fails. the lower two methods don't care, and the toplevel one can give the user a proper error message and clean up.
People wonder why software is so hard to test, does so poorly on error handling, yet complain whenever we add mechanisms to languages to help.
Care about electronic freedom? Consider donating to the EFF!
Oh dear. Another person who thinks that exceptions should never be thrown.
If exceptions were never meant to be thrown, they wouldn't be in the language. Exceptions are an abstraction for dealing with exceptional conditions -- conditions that do not normally occur, but can occur. At the expense of some additional complexity, they make error checking a little simpler and less bug-prone. When (not if -- assuming you are a believer in Murphy's law) those exceptional conditions occur, your program better be able to handle them correctly.
You are right that some people do use exceptions when not appropriate. Exceptions are (generally) not appropriate for exiting loops, for example. But they are more than appropriate for out of memory conditions, out of disk space conditions, etc.
The reason they are not viable performance-wise is not because they are too expensive to throw; it is because they are too expensive when they are never thrown at all. There's generally a 5-10% performance hit just from having code that might possibly throw an exception, depending on your compiler's implementation. The numbers on the netlab page are for throwing exceptions, unfortunately; I would be interested in seeing if they got a performance benefit when exceptions are not thrown. Guess I'll have to dig to find a copy of the paper.
thus making C++ exceptions viable in several scenarios
By experience, C++ exceptions are the worse way of handling errors and leads to too many warp jumps that are difficult to track, trace and debug.
C++ in a kernel is fine with me. But throwing exceptions in there is asking for trouble.
essentially a quote of linus's comments from the 1992 test. boo hoo, he tested a pre-standard language and a pre-standard compiler (which was honestly crap on C++ until very very recently).
Care about electronic freedom? Consider donating to the EFF!
there can't be a distinction, C++ owes itself to an "improved C" mentality. C people can dissown C++ (except the parts that made it into ANSI C) but C++ people can't dissown C... and don't want to.
-pyrrho
if it matters which part of the code had the error, then use separate try blocks for each part. In real life, most exception handling code DOESN'T care which function called it, or can tell from the exception type. As we've got more expressiveness than the value for ERRNO, we can do that here.
Care about electronic freedom? Consider donating to the EFF!
Linux made his view on C++ in the kernel a while ago here
Yes, a kernel is more difficult than a word processor, but that doesn't mean that implementors must implement stupid C++ code.
No, the problem is not if it's difficult or not, is the fact that C++ implies the existence of an intrinsic memory management behaviour (new/delete), that is not really compatible with the strict memory management a kernel must implement.
What's in a sig?
The simple reason for that is that otherwise the kernel would be unpredictable. Let's say the error logging function used the string class (which likes to allocate memory behind your back). If the memory allocation function fails and tries to print an error message... you got yourself a kernel crash. This is why the kernel is significantly more difficult to program than, say, a word processor.
Put the strawman away. You don't absolutely have to use all the features C++ gives you if they aren't right for you. Don't use exceptions or run-time typing in an environment where you don't need it, or don't know if it'll work. Personally I never use either because I strongly disagree with the code they generate. If you don't understand that skilled usage of C++ generates exactly the same code that the equivalent C would (except the C++ took less time to type and checked your syntax better), you don't understand the point of C++ whatsoever. It's just a glorified type checking macro expander. And there's no reason to use plain C when you've got that at your disposal.
Now have a look at examples of kernels written in C++. One easy example: Red Hat eCos. Just saying that C++ doesn't do kernels well doesn't remove the fact that people do use it in kernels, it works very well, and there are many examples out there.
I'm an embedded developer. I've done some projects as C only and some as C++. With proper discipline C++ can actually generate smaller, more compact code than straight C. But getting the infrastructure done is a bit harder.
.38 special.
In fact, eCos, a very nice (GPL) embedded operating system has its kernel written in C++. eCos performs well and is cleaner than a competing straight C RTOS which has to build its object system by hand (VxWorks' WIND kernel).
The real difficulty in using C++ for embedded development comes from the toolchains themselvs. Frequently new processor architectures don't have very functional C++ back ends but C is somewhat stable.
In fact, I worked on porting some C++ TV middleware to a specialized "media DSP processor." The GCC back-end for C was rock solid but C++ constructs would give me constant ICEs.
C++ does fix some dumb things in C, but when it comes to shooting yourself in the foot, C++ is like an AK-47 while C is more like a
The core of Windows is written mostly in C. The GDI was written in C++ and the chief Kernel architect of WinNT (David Cutler) continually jabbed the Graphics group on their choice of C++.
I don't suspect you will be finding C++ in NTOSKRNL any time soon. I think Cutler would beat up anybody who tried.
Haiku (formerly OpenBeOS) has had parts of the kernel in C++ since the beginning.
/ cu rrent/src/add-ons/kernel/file_systems/bfs/
Most notably the reimplementation of the Be File System. (Now OpenBFS)
http://cvs.sourceforge.net/viewcvs.py/open-beos
The filesystem is not slow at all. Actually the Haiku-people did some speed comparisons with Be's BFS and OpenBFS won.
Other parts of the kernel is C++ as well. The device manager, an experimental vm..
I've never really understood this anti-c++ in kernel thing.
You're confusing exceptions with assertions. Exceptions can be used to elegantly handle boundary conditions. Assertions flag events that should never happen *if* the code logic is correct. Exceptions stay in both debug and release builds, assertions are not usually compiled into release binaries.
Exactly. Here's an example of poor use of exceptions:
//file object manages state
//file object is cleaned up by it's
//destructor, only thing we need to do is
//alert user
try {
while (1) {
read_from_file();
}
} catch (EOFException) {
clean_up_file();
}
This is bad because you're using an exception to manage normal program flow. Here's a better use of exceptions:
try {
File f("myfile");
while (!f.eof()) {
f.read();
}
} catch (FileOpenException) {
}
This is a simplistic case, because you can just easily do this by checking error codes. It shows it's worth when you have, say, a dozen different files to open. You save a lot of if-checks, a lot of duplicate error handling (alerting the user and returning), and you don't have to worry about manually closing all the files which might be open, preventing resource leaks. If your exception system is properly designed, you don't lose any information, because stuff like the file you were trying to open is preserved with the exception information.
I think that deserved +1 Funny.
Although the idea that C++ compiled object code is bloated is incorrect. It's normally either due to inexperience with the language or due to a poor compiler implementation. Part of the problem is that on old compilers, template instatiations were actually included once for every object file that refered to one. More recent compilers can identify and remove duplicates at link time, or can save templates in a separate file and link it in at the end of the build. (For some templates, though, you can actually be better off with one in each file if each file uses inline member functions, and each uses a different one).
Sure C++ has it's faults, but bloatedness is not one of them - although a standard library may be bloated, but that's not an issue for the kernel.
On the other hand, if the above were C++ code, it could mean absolutely anything, depending on how operators were overloaded. Somebody could have defined "^" to play the star spangled banner on the system speaker for all you know. In C++, you can never really say for sure what anything means without looking in every single include file.
The cake is a pie
Some people say exceptions. I say goto. Easy to understand, easy to implement, and very clean IMO:
/* reuse sin struct from accept() */
int addclient(int s,struct sockpair *sp)
{
struct sockaddr_in sin;
socklen_t sl;
int local,remote;
sl = sizeof (struct sockaddr_in);
if (-1 == (local = accept(s,(struct sockaddr *)&sin,&sl))) {
perror("accept()");
goto just_return;
}
fcntl(local,F_SETFL,O_NONBLOCK);
if (extent >= TCPFWD_NO) {
fprintf(stderr,"error: out of buckets! %u >= %u\n",extent,TCPFWD_NO);
goto close_local;
}
if (-1 == (remote = socket(PF_INET,SOCK_STREAM,0))) {
perror("socket()");
goto close_local;
}
memset(&sin,0,sizeof (struct sockaddr));
sin.sin_family = AF_INET;
sin.sin_port = htons(remote_port);
sin.sin_addr.s_addr = inet_addr(remote_addr);
if (INADDR_NONE == sin.sin_addr.s_addr) {
fprintf(stderr,"inet_addr('%s'): failed\n",remote_addr);
goto close_remote_local;
}
if (-1 == connect(remote,(struct sockaddr *)&sin,
sizeof (struct sockaddr_in))) {
perror("connect()");
goto close_remote_local;
}
fcntl(remote,F_SETFL,O_NONBLOCK);
sp[extent].allocated = 1;
sp[extent].a = local;
sp[extent].b = remote;
extent++;
return 0;
close_remote_local:
close(remote);
close_local:
close(local);
just_return:
return -1;
}
Well the major difference between C and C++ being that C++ supports "native" classes while in C you emulate them with arrays of stuctures.
A class is a way of organizing "objects" in a hiarchical manner. You could almost say an OOP manner. The fact you can do more with C++ doesn't take away the fact that oop is the major difference. In fact I can't think of any other reason to use C++ over C aside from classes and the various forms of inheritance.
The point is C didn't borrow what C++ had to offer not because the ISO C committee is lazy or incompetent but that C solves DIFFERENT problems.
Someday, I'll have a real sig.
If the Kernel is C++, this means the API calls can ALSO be C++ (with some restrictions, of course). This could mean no more kernel-recompiling for loading modules, no more .so recompiling and ending the .os dependancy hell...
What we're talking about is using a whole new computing paradigm in the very core of an operating system. (it may be not new for apps, but for the kernel... just think about it).
As an OOP'er, I'm frankly excited by the idea.
And no, we're not talking about interpreted runtime languages like C# or java. We're talking about the REAL thing.
C++ means that the code will be easier to maintain, changes will be easier to track, and who knows. Perhaps a boost in the development that might JUST be what Linux needs to defeat Winblows as the next Desktop OS.
Your first three are part of OOP [hint: Java has them too]
Sorry to break it to you pal, but you don't have to have classes to use these constructs, they work perfectly well with primitives and structs. Obviously they become much more useful in an OOP world, but they aren't part of OOP.
pass-by-reference is not a programming methodlogy either... it's just a function of C++.
No one said anything about programing methodologies. Your original post said:
In fact I can't think of any other reasons to use C++ over C aside from classes and the various forms of inheritance.
I just stated some language features available in C++ that aren't in C that are potential reason to use C++ over C (that aren't related to classes and inheritance).
Be had a strict "no c++ in the kernel" rule. You're correct in that they did use c++ extensively in user space.
Stop talking out of your ass. The Windows kernel developers use structured exception handling that was developed during the OS/2 days. The resulting code ends up nice looking and manageable because unlike C++ exceptions, this one also handles hardware exceptions (among other nifty features).
Yes, "undefined behavior" means, "this is not a feature of the language; DO NOT EVER DO THIS, EVER!". That is, it's a syntactically valid construct with no semantics attached to it. Since there is no semantic value in the statement, you should never use it. Just for the record, C has undefined behavior as well. Try the defined behavior of "int *px = 0; int x = *px;" for starters.
IE's renderering engine is not used to display anything in windows explorer except when you open up html files using the file manager or use that stupid webview thing which wastes space by putting dumb shit on the sides and top of the listview by default.
Can you give me a reference on DRM features being built into DirectX? If you mean the WMV codecs, that's not something anybody would consider DirectX.
The kernel definitely will not be rewritten in C#. Maybe they'll add a subsystem written in C# way in the future but definitely the low level NT api will be in C with a few bits in assembly.
Trolling, are we?
These changes would definately be applicable to user space as well, and indeed we mention it in our paper. In kernel however they are more important as performance is normally of more concern.
Ideally you should be able to turn on these optimizations by a switch. If any g++ developer is out there I'd be more than happy to explain what we did and they are free to use it.
Some of the overhead in the g++ exception implementation arises from the fact that ease of debugging is considered important. However in kernel space that does not apply.
C solves the same problems as C++. It makes different methods of solution easier.
In many problems, the best solution is a procedural function. In some, it is an OO design. I've written polymorphic functions (badly, admittedly) in Fortran 77 with VAX extensions %REF and %LOC. C++ makes it *easier* to do this.
One of the cardinal sins of the new C++ programmer is making *everything* an object. Makes the program impossible to debug.
Two things bug me, though, in combination:
Function Overloading is NOT recommended for the New/Delete functions. That is the ONE place I would see great benefit from overloading. You can write your own pooling mechanism, ad reference counts and so on by overloading the new and delete function. Why not?
You miss what is probably the biggest advantage of C++ (and possibly one of the most controversial).
Templates allow for some incredibly slick code, and vastly improve both programmer productivity and type safety.
according to these benchmarks, linux 2.6 is faster freebsd 5.1.
What he is saying is that exceptions should only be used as runtime assertions.