Pardon my ignorance, but given the complexity of Rust, the incoherence of the async story etc, wouldn't something like Zig be a more reasonable choice for Linux, with its ability to be introduced gradually etc?
Zig isn't finished yet. Rust has stable language features.
You also don't need async in many if not most cases, especially if you're writing kernel code. I wouldn't want to use a kernel module that pulls in all of tokio/reqwest/hyper just like I don't want a kernel module that links against openssl.
Async makes a lot of sense for userland code, but not so much for kernel code. I haven't seen async get used in any demo for kernel Rust modules and I doubt it'll happen anyway.
Looking at a comparison between common kernel structures and theoretical Rust implementations of those (like on https://security.googleblog.com/2021/04/rust-in-linux-kernel...) I think the kernel can get improved security and reliability from Rust without any "fancy" Rust. Just the basic type system improvements, nullability improvements and explicit checks will be enough for some real benefits.
> You also don't need async in many if not most cases, especially if you're writing kernel code.
Have you seen how much communication with the hardware in drivers is actually very asynchronous? It’s all hand rolled C, of course, but there’s a lot of it.
But look at the bright side: people likely won’t be enjoying hand-rolling asynchronous Rust since language-level primitives exist, so maybe there will be enough pressure and enough of use case diversity to make a more solid implementation that doesn’t suck, or sucks way less than the current one.
Currently the rust ecosystem is rapidly moving towards treating tokio as the default-and-only async runtime. If the linux kernel community establishes an async runtime tuned for embedded/kernel use cases that could bring some much needed diversity and speed up the process of making async ergonomic and less bolted-on.
What do you consider "hand rolled"? The kernel has extensive support for these use cases since, as you said, hardware is usually async. Async with hardware is rarely tangled, from my limited experience. It's usually event driven.
I mean that it’s not hidden behind some all-encompassing event loop, nor is there an all-encompassing framework with an event loop hidden from you. It’s more like an exploded view of a mechanism in comparison.
Rust is mainly introduced to the kernel to allow writing safer drivers, which makes a lot of sense considering a crashing driver likely means a crashing computer.
I've ran Linux (various) for near on 20 years and ran it on hundreds servers for 15. I've only ever hit one such crash for an external piece of custom hardware with a noddy kernel module. Plenty of crashing kernel modules yea.
I've run into many Linux driver bugs, usually for video drivers but sometimes for other components. On one laptop, AMD's kernel driver corrupted every third boot for a while, though that's been fixed. It's still dumping error messages and failures during boot but other than that it seems to work fine. On my new laptop, the kernel didn't even support my audio interface for a while.
There was a bug with Intel GPUs that was unfixed for at least three kernel versions where plugging in an external monitor over an HDMI-to-Displayport adapter would reliably freeze the system the moment the kernel tried to modeset. That's finally been fixed from what I can tell, I believe it had something to do with a change to an NFS driver somehow.
Server hardware often doesn't need complex or bleeding edge kernel modules. You don't need sound or video, you don't need framebuffer resolutions, you don't even need that much in terms of keyboard compatibility. It's a lot easier to run a kernel of the most complex hardware out there isn't hooked up to your system. That doesn't mean Linux is bug free, it merely means that the kernel code for the limited hardware of your choice has been maintained well.
The real challenge for Linux or any kernel really is to run reliably on a laptop with an uncommon variation of common hardware, preferably sold for only a few months. That's where the real bugs come in.
Both of statements can be true, though. Developers skilled enough / brave enough to write kernel drivers can be very careful about not leaking memory or mismanaging the hardware in question, and Rust can make that job much easier by being pedantic about memory mistakes or type misuses (with custom types that are validated at compile time but no runtime overhead).
I've worked on C and Rust codebases, though nothing near as complex as the Linux kernel, and I'm excited for this change as it will reduce the burden on the kernel developers to reach the same quality output, so we either get drivers faster, or drivers for hardware we wouldn't have otherwise received, or both.
As far as the fear of Rust kernel drivers that don't compile with GCC's Rust frontend, I trust Linus to keep the bar high on letting this feature in, and that kernel drivers accepted into the tree compile with just GCC as they always have.
Third-party drivers may not, but that's also true today if someone wrote a driver that only compiled with LLVM. This doesn't really happen in practice as it goes against the path-of-least-resistance for the developer: they'd have to switch their toolset out when working on that driver vs everything else, and the auto-building by DKMS would probably fail on them if they used it during development -- all of the conventions used by the rest of the kernel infrastructure will keep them in line.
Lucky you to not have had to use the abomination that is any Realtek wireless card, because drivers for those were super-atrocious. I'm having problems with established good citizen stuff like amdgpu and i915 on a regular basis.
(Frankly, I doubt that those would be resolved with Rust, or even if the developers all got attendants that would massage their feet.
The GPU driver developers are hugely busy with cranking support for new cards, and bugs in the older cards support are rarely tended to, when they are it makes headlines. I blame skewed incentives.)
There are definitely kernel bugs, but yes they are relatively infrequent. It takes a lot of effort to achieve that though, and better tooling can help.
there a incredible maintenance effort behind linux stability. memory safe languages like rust won't have a immediate measurable impact in the user experience of someone using an LTS Linux distribution, but they will, in the long term, improve the development cycle.
I'm just curious what would a panic!() in Rust kernel code look like - an oops log in dmesg? panics happen in Rust for example to handle indexing out of bounds errors with the default bounds checking or to guard against divide by zero.
The "async is bad" meme is getting old. Someone has written an article about JS's limitation (which Rust doesn't have) and how they don't like C# syntax, and it gets endlessly slapped on every language as a vague notion of "colors".
Async in Rust is incredibly well designed, and works very well for such a low-level design that minimizes heap allocations and virtual dispatch. Rust intentionally prefers locally explicit syntax for things that affect control flow, and wants to be back-end agnostic without a runtime, which goes against implicit built-in "colorless" magic.
I have no idea what I'm talking about, but rust can also be introduced gradually, and async isn't a language feature the kernel will use, actually the kernel will probably not even use the standard library because of how rust assumes it'll never run out of RAM.
One could try to use it for those use-cases, but it probably warrants some investigation if it's actually the best possible solution for the problem.
The kernel has some different properties than userspace - e.g. context switches between kernel threads can already be cheap, which minimizes some of the reasons for using async/await. Then allocations for continuations using in-kernel slab allocators might also be cheaper then in userspace, whereas placing huge objects (Future)s on the stack (preferred mode of Rust Future composition) might be more expensive with tiny kernel stacks.
I guess it might be worth to do experiments for some use-cases to see how it actually would work out.
Zig's safety is lower than Rust's, that's true, but it is still a very big improvement over C, so a case could be made for it in principle. But of course in practice Zig is not stable enough to consider that yet.
Anyhow, this isn't all or nothing: a case could also be made one way or the other about how much 'unsafe' to allow in Rust in the kernel. It's a question of tradeoffs.
Rust definitely has its opinions and its chosen complexities. I'd point to the way global allocators are used by default (opinionated) and the way trait coherence rules work (opinionated and kind of complicated). That said, I think a lot of the complexity of Rust is actually fallout from a relatively small number of design choices:
- programs ought to be UB-free by default, and overriding those defaults should be rare
- mutation is allowed, including shared mutation across threads
- runtime assumptions similar to C and C++, i.e. no garbage collector, and a useful subset of the lanugage doesn't require heap allocation
Given those requirements (in short, a "safe systems programming language"), I think you very quickly find yourself needing 1) lifetimes, 2) the no-mutable-aliasing rule, and 3) move semantics. Those things contribute a lot of the "feel" of Rust, and probably the majority of the difficult learning curve. But I think it's interesting to consider that "the type system should deal with lifetimes" isn't really a baked-in opinion that Rust has, as much as it is the necessary complexity stemming from the other opinions above.
Yeah I'm not a fan of Rust either. It has some great ideas though. Part of me wants to try writing my own language after I finish my current side project.
You may be interested in Jakt, an experimental language written for SerenityOS. It has some very Rust-like festures for memory safety but no borrow checker or async (at least, not at the moment).
Currently, it compiles into C++ as it's written specifically for an entire operating system written in C++ but I can see native compilation becoming an option down the line.
Huh? It might be slightly wordy, but otherwise very simple and easy for both people and computers to read. Even a primitive editor with primitive highlighting can make the syntax painless.
I never used Zig, but used many other languages, and that looks better than most which don't have line prefix like \\: in languages that don't need it there is confusion about significance of whitespace in the beginning of 2nd+ lines.
Yep, that's precisely what that sintax solves, and also makes it easier to build a grammar for it (think of editor syntax highlighting for example) because the multiline marker is repeated in every line, instead of being a contextual marker. This is the same reason why Zig doesn't have dedicated multiline comment syntax.
Opinionated in the context of programming languages usually means that the language forces it's own abstractions on you, you have to work with and around them.
Zig is not like that, it's pretty much just C, but simpler, ergonomic. It's design is a reasonably simple, portable abstraction of contemporary hardware. It gives you all the power to come up with your own abstractions on top of that.