top | item 18274235

Writing an OS in Rust: Hardware Interrupts

398 points| ingve | 7 years ago |os.phil-opp.com | reply

63 comments

order
[+] __exit__|7 years ago|reply
Nice to see this series continued! I wish there were more, potentially non-discontinued series or books on how to build actual projects with Rust from scratch.
[+] d33|7 years ago|reply
Just wanted to say that I wish that this was how kernels were made - out of modular, reusable blocks. Would this scale? In other words, could Redox and the OS described in the system share IRQ code from the same crate and ultimately grow into two different systems that are built of mostly the same crates for low-level stuff, just wired differently?
[+] steveklabnik|7 years ago|reply
We’ll see; a lot of hobby OSes are already doing this to some extent, like using the x86 crate to manage the definitions of various data structures needed by the platform. We can’t really tell until more OSes get further along (my own is taking forever due to lack of time...)
[+] DoritoChef|7 years ago|reply
From the historical point of view, this heavy decoupling is associated with microkernels which fell out of favor with the rise of the Linux kernel. I think in this day and age, modern programming languages could make building a microkernel that doesn't fall prey to the shortcomings of MINIX a possibility.
[+] danellis|7 years ago|reply
I don't know what your definition of "reusable" is, but I keep seeing things like, "We used the TCP/IP stack from NetBSD".
[+] ajross|7 years ago|reply
All of the effort expended to enable the legacy PIC so that the legacy PIT doesn't fire timer interrupts into an invalid configuration seems weird, especially when justified with "the APIC is too complicated and we'll show that later".

Um... the Local APIC interface needed to catch an interrupt and wire up the timer is if anything simpler than what is presented here.

[+] ModernMech|7 years ago|reply
You've gotta do that remapping anyway when you enable the APIC. See: https://wiki.osdev.org/APIC

> masking all interrupts and remapping the IRQs. Masking all interrupts disables them in the PIC. Remapping is what you probably already did when you used the PIC: you want interrupt requests to start at 32 instead of 0 to avoid conflicts with the exceptions. You should then avoid using these interrupt vectors for other purposes. This is necessary because even though you masked all interrupts on the PIC, it could still give out spurious interrupts which will then be misinterpreted from your kernel as exceptions.

[+] davemp|7 years ago|reply
I'm with you on this. It's similar to how every x86_64 OS guide goes through the ancient ritual of working your way up to long mode. Why not just use UEFI?
[+] vectorEQ|7 years ago|reply
here was me hoping in such a modern langauge, perhaps someone would finally have a good APIC example. alas :D back to the 80s it is. :D it's ok i'll go back to my acpi document and try not to cry :D !

(cool series! keep it up!!)

[+] bgilroy26|7 years ago|reply
>What's next?

>As already mentioned, the 8259 APIC has been superseded by the APIC, a controller with more >capabilities and multicore support. In the next post we will explore this controller and learn how to >use its integrated timer and how to set interrupt priorities.

[+] tayo42|7 years ago|reply
Only slightly related, but interrupts are a pretty old concept as far as i know. I'm curious if there is anything that would replace them?
[+] AWildC182|7 years ago|reply
So are adders but nobody is trying to replace them... When your overhead is high enough you can poll obviously but why bother when you can set up an interrupt. They're more of a thing on embedded systems because you can get some seriously low latency numbers with them when used properly.
[+] monocasa|7 years ago|reply
They've been replaced a few times under the hood. Message signalled interrupts exist, and even regular interrupts on PCI-E and HyperTransport look more like network packets or rdma read/writes than a true electrical signal.

DPDK turns off interrupts and manually polls the card in a loop AFAIK.

But for most cases, the underlying semantics though are useful enough that the abstraction isn't going anywhere anytime soon.

[+] derefr|7 years ago|reply
I'm not a hardware guy, so I don't know if this is feasible or has ever been implemented, but:

I could imagine "polite" interrupts—where instead of the processor immediately jumping into the ISR's code, it simply places the address of the ISR that "wants to" run into an in-memory ring-buffer via a system register, and then the OS can handle things from there (by e.g. dedicating a core to interrupt-handling by reading the ring-buffer, or just having all cores poll the ring-buffer and atomically update its pointer, etc.)

The major difference with this approach is that pushing the interrupt onto the ring-buffer wouldn't steal cycles from any of the cores; it would be handled by its own dedicated DMA-like logic that either has its own L1 cache lines, or is associated to a particular core's L1 cache (making that core into a conventional interrupt-handling core.) Therefore, you could run hard-real-time code on any cores you like, without needing to disable/mask interrupts; delivering interrupts would become the job of the OS, which could do so any way it liked (e.g. as a POSIX signal, a Mach message, a UDP datagram over an OS-provided domain socket, etc.) Most such mechanisms would come down to "shared memory that the process's runtime is expected to read from eventually."

There would still be one "impolite" hardware interrupt, of course: a pre-emption interrupt, so that the OS can de-schedule a process, or cause a process to jump to something like a POSIX signal handler. However, these "interrupts" would be entirely internal to the CPU—it'd always be one core [running in kernel code] interrupting another [running in userland code.] So this mechanism could be completely divorced from the PIC, which would only deliver "polite" interrupts. (And even this single "impolite" interrupt you could get away from, if the OS's userland processes aren't running on the metal, but rather running off an abstract machine with a reduction-based scheduler, like that of Erlang.)

[+] acomjean|7 years ago|reply
Right now nothing. I used to work with HPUX with "Real time Extensions". One of the things we could do is shut off interrupts for certain processes.

There was a subset of systems calls we could use while in this realtime mode (A lot of unix system call really rely on interrupts, and the whole OS is built on them..).

I think interrupts started from hardware signals, but were expanded to include software.

[+] dbcurtis|7 years ago|reply
Consider the problem you are trying to solve. I/O devices, from the CPU's standpoint, are things that are very slow and require infrequent service, but when they do require service, have extremely strict hard-real-time latency requirements. Interrupts are one approach to being able to get useful work done while waiting for I/O, without burning a lot of CPU resources on polling.

Another approach is to have a processing hierarchy, like old mainframes did. Off-load the CPU with some kind of I/O processor or channel controller that can do the real-time data transfers, and "coalesce" low level interrupts into a single larger interrupt that captures more work -- think a single DMA-COMPLETE interrupt instead of a bunch of GET-SINGLE-BYTE interrupts.

You can of course push the processing into hardware but that is much harder to change than an I/O driver, so the interrupt-driven-driver design pattern wins on software maintainability.

[+] anonymfus|7 years ago|reply
Propeller microcontroller uses dedicated cores instead of interrupts.
[+] ModernMech|7 years ago|reply
You could get rid of interrupts if you take a completely different approach to CPU architecture. The high-level view of how a CPU works is that you load in a program, which involves setting an instruction pointer at the head of a list of instructions. This is the very first thing you do when you write a kernel; you set the instruction pointer to the entry function on your kernel and the CPU goes from there. Without interrupts, it would do that forever. You need the interrupt in order to make the CPU look at any instruction not prescribed by the program you loaded in the first place, which is how you get any sort of I/O going.

An alternative architecture that would not need interrupts would be something that is driven by data. Instead of loading in an initial program, you would load in some initial data, and the CPU's execution would be driven entirely by that. On every cycle, the CPU would look at any new data that has arrived and process it accordingly. In this view, key-presses or timer ticks would just be like any other data flowing through the system.

[+] samfisher83|7 years ago|reply
It is fundamentally how processors work. That is how context switching happens IO happens etc. I mean if you are at work and you need to go the doctor the school will call you to interrupt you. If someone didn't what would happen to your kid?
[+] als0|7 years ago|reply
To me an interrupt is a signal. They can be as complex or as simple as one desires. I'm unsure that concept can ever be replaced. Interrupt controllers, on the other hand, are very complex, which may warrant different abstractions someday.
[+] davemp|7 years ago|reply
Interrupts at their base are signals for crossing from an asynchronous, parallel world into a synchronous, sequential world. Some sort of signal needs to exist for that purpose.

There is likely a way to cross domains that can be formally reasoned about more easily. Although, like functional programming, implementing the abstraction directly on silicon probably wouldn't make much sense. Process calculus is the place to start if one is interested in this line.

[+] vectorEQ|7 years ago|reply
interrupts are still the main methods. but modenr osses use LAPIC /APIC which is a more modern form of interrupt controller. it has a little more abilities and i think 1 local per cpu or core or w/e. basically interrupts are good, where 'polling' is the legacy method (very legacy :D) of querying hardware untill it's read. via interupts hardware can let the OS know, so it's not wasting cycles waiting for hardware.