[TUHS] the true reason why c++ always wins

G. Branden Robinson via TUHS tuhs at tuhs.org
Thu May 28 14:01:37 AEST 2026


At 2026-05-27T18:43:09-0700, al kossow via TUHS wrote:
> https://www.youtube.com/watch?v=I7fEsbksKRE
> 
> this video is disturbing at so many levels

Lacking patience, I read the transcript instead.

> if you follow the Gabriel paper links, the one missing is
> 
> https://blog.reverberate.org/2011/04/eintr-and-pc-loser-ing-is-better-case.html

This is a good resource, and sheds much light on a frustrating problem.

For me, the heart of the matter is this quote of Stevens's _APUE_:

>> A characteristic of earlier UNIX systems is that if a process caught
>> a signal while the process was blocked in a "slow" system call, the
>> system call was interrupted.  The system call returned an error and
>> `errno` was set to `EINTR`.  This was done under the assumption that
>> since a signal occurred and the process caught it, there is a good
>> chance that something has happened that should wake up the blocked
>> system call. [...]  The problem with interrupted system calls is that
>> we now have to handle the error return explicitly.

I submit that there are couple of ways to cut this Gordian Knot, one
"pragmatic", and one fundamental.  The pragmatic one could have been
done, to all our benefit, ages ago.

The pragmatic solution
----------------------

Better libraries.  Period.  Userspace programs generally shouldn't be
making system calls directly.  Instead you should have a library that
handles interfacing with the system for you.  A library API has more
flexibility than the OS kernel, and (paradoxically) can _permit_ more
flexibility in the system call interface by advertising "legacy" entry
points for system calls that change their signatures or disappear.  (I
propose that Unix's historical failure to provide any such thing is what
prompted Linux Torvalds to proclaim his Iron Rule about not ever
breaking userspace.[1]  With a library layer and a bit more type
discipline than early C had, lseek(2) could have remained simply
seek(2).  I think I've heard someone on this list observe that this is
how Microsoft Windows (NT and its successors?) works.

I can imagine the objections.  (1) If Microsoft's doing it, it must be
wrong.  Well, that's not a bad way to bet, but sometimes Microsoft does
the right thing after first trying every wrong way they can think of,
and eventually tiring of getting burned.  (2) "I'm a C programmer and
I'm close to the metal and therefore I go straight to system calls".
Fine, but then your app needs to do what the library would have been
doing: having shims to abstract the version of the OS kernel, handling
interrupted system calls, and so forth.

I think I've posted here before about how the grudging acceptance of a
`-u` flag to cat(1) in Eighth Edition Unix, and the vigorous rejection
of any _other_ flags to it, was a symptom of people not really thinking
of that program as an application, but as a test apparatus for the
kernel.  The latter is not, I submit, an appropriate first-order purpose
for a user-facing application.  By _all_ means, ship a kernel-testing
apparatus with the OS!  But it doesn't need to live in the $PATH.

If one accepts that perspective, the flutter of other flags waving from
cat's tail becomes less offensive.

The fundamental solution
------------------------

...arises from consideration of this clause of Stevens.

'if a process caught a signal while the process was blocked in a "slow"
system call'

My thesis: An OS kernel should not _have_ "slow" system calls.

The modern Linux kernel is festooned with things called "worker threads"
in an attempt to solve the same problem.[2]  (I presume successfully.)
But because Linux is still monolithic--and has to be, lest its lead
author's now largely achieved objective of "world domination"[3] be
squandered--it has an ever-growing set of worker threads as its
revisions steadily find more things for idle cores to do.

This strategy isn't wrong or stupid, but in my view it quietly concedes
an argument that Torvalds and his acolytes declared themselves as having
prevailed in over 30 years ago.

You _could_ reach the same point, with more easily managed permission/
security boundaries (I claim), by employing a microkernel design.

And let us please forever erase, or at least suffix a fat asterisk to,
the name "Mach" from association with microkernels.  That was indeed the
status its promulgators hoped for, but by starting with the BSD kernel
and cutting it down, instead of building one up from nothing, they
picked an approach that frustrated their objectives.

https://cs.nyu.edu/~mwalfish/classes/15fa/ref/liedtke93improving.pdf

Thus my enthusiasm for microkernels.  Yes, context switches are costly,
and mode switches are more costly still.  Where these costs are
unbearable, you either need more CPU, more memory, or to move away from
a general-purpose OS kernel.  Solve your problem in a free-standing, not
a hosted, runtime environment.

As we've seen, any proper engineering problem requires making tradeoffs
somewhere.  (If it doesn't, it's an arithmetic problem.)  An OS kernel,
in days of yore a "job monitor", was supposed to be a small, lean thing
of minimal footprint that perturbed the availability of CPU cycles and
RAM storage to the "real programs"--the _jobs_--as little as possible.

But if we count lines of code, monolithic (or semi-monolthic, "modular")
kernels are some of the biggest software projects in the world, with the
ones we've all heard of weighing in at tens of millions of lines.

https://interestingengineering.com/lists/whats-the-biggest-software-package-by-lines-of-code

Zero-copy operations are not a silver bullet, either.  They can be
beautiful but you then have to spend more time considering whose
responsibility it is to the validate the data in shared buffers, lest
you end up with _no one_ taking responsibility for it...and suffering
security pwnage.

While researching this message I happened across the following paper
proposing a dedicated memory allocator for I/O operations in the Linux
kernel.

https://netdevconf.info/0x15/papers/1/maio_netdev0x15.pdf

I don't know what has become of that work.  To me, the very existence of
{e,}BPF is a concession that monokernels are too complex, and the
proposed MAIO (from the foregoing paper) suggests the same.

All right, tell me how I'm wrong!  :)

Regards,
Branden

[1] https://lkml.org/lkml/2012/12/23/75

    As is the way of iron rules, people have found ways to game them.

    https://lwn.net/Articles/1070072/

[2] https://docs.kernel.org/core-api/workqueue.html
[3] https://www.linuxjournal.com/article/36
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://www.tuhs.org/pipermail/tuhs/attachments/20260527/5ec35777/attachment-0001.sig>


More information about the TUHS mailing list