Ch20.4: Why Linus Torvalds Called It a "BLOODY STUPID IDEA"

中文(简体)

1. The Message

This is the most-quoted message in the history of C++ arguments, and it is usually quoted without the two pages that precede this one. Here it is in full, because the specific claims matter more than the tone:

From: Linus Torvalds
Subject: Re: Compiling C++ kernel module + Makefile
Date: Mon, 19 Jan 2004 22:46:23 -0800 (PST)


On Tue, 20 Jan 2004, Robin Rosenberg wrote:
> 
> This is the "We've always used COBOL^H^H^H^H" argument. 

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).

        Linus

The message is from January 2004. It is twenty years old and it is still the standard opening move in every argument about C++ in kernels, which means it is worth checking rather than cheering or booing. So: three bullets, one at a time, against what this chapter has actually measured.

2. Bullet One: "The Whole C++ Exception Handling Thing Is Fundamentally Broken"

Correct, and especially for kernels, and this is the bullet that is not an opinion. 19.8 measured 934,540 ps per legacy throw against 885 ps per herbception, and 19.3 showed the mechanism. 20.9 of this chapter shows what it costs at link time: six runtime functions, an unwinder, an exception table per function, a personality routine, and RTTI.

"Especially broken for kernels" is exactly right and deserves unpacking, because there are three separate reasons and people usually name one:

  1. The failure path allocates. A kernel reporting out-of-memory by allocating memory is a bug in the reporting, not in the allocation.
  2. The failure path walks memory. Unbounded latency in a context that may be holding a spinlock or servicing an interrupt is not "slow", it is incorrect.
  3. The runtime may not exist. A kernel is the thing an unwinder would have to be built on. There is no lower layer to ask.

The mechanism this book uses answers all three, which is why the criticism survived the mechanism: herbceptions put the error in two registers on the return path (20.10), allocate nothing, and need one data symbol from the linker.

3. Bullet Two: "Compilers That Hide Memory Allocations Behind Your Back"

Also correct, and it is the bullet that has aged the best, because the list of hidden allocations got longer after 2004, not shorter:

The answer is not "never allocate", it is "make the allocation source a type, so the caller can see it in the signature". That is what a fast_io allocator is (19.9): the policy is part of the container type, so a kernel function cannot accidentally allocate from a heap it does not own. And a kernel needs several heaps — per zone, per NUMA node, per device, DMA-safe or not — which is why a single ::operator new is not merely hidden, it is insufficient.

4. Bullet Three: "You Can Write OO Code in C Without the Crap"

This is the bullet that reads as 1992, and it is the one place where the message is answering a different question than the one you are asking. What Torvalds was rejecting was C++ as it existed then: exceptions, RTTI, hidden allocation, and template diagnostics that made a misplaced > produce four screens of noise. What he was defending was the ability to write a filesystem with an operations table — which is a vtable, spelled struct file_operations.

A modern freestanding C++ keeps the parts that structure code without a runtime: constexpr (Ch7), templates (Ch15), concepts, deterministic destruction (Ch14), and now herbceptions. None of those need an operating system. The complaint was never about the syntax; it was about what the generated code would do when the target had nothing underneath it.

5. What Has and Has Not Changed Since 2004

Claim2004Today
Legacy exceptions are broken for kernels True Still true, and still the mechanism C++ ships by default
Compilers hide allocations True Still true; ::operator new has no competitor in the standard
Freestanding C++ is a lie True (C++03) Partly fixed (C++23), then stalled: C++26 was not published, so std::array is still not a freestanding header
C cannot express the abstractions without cost True False: constexpr, concepts and templates at zero run-time cost are the one thing C never got
Error handling requires an operating system True False for herbceptions

Two of the three bullets were about the mechanism, and both mechanisms have a replacement now. What has not changed is the default: a C++ build in 2026 still gives you legacy exceptions, RTTI and a global heap unless you turn them off. That is why this book turns them off in every example and says so in 19.7.