Hacker News new | past | comments | ask | show | jobs | submit | phil-opp's comments login

Location: Karlsruhe, Germany

Remote: yes

Willing to relocate: no

Technologies: Rust, systems programming, operating systems, embedded, open-source software

Resume: https://www.linkedin.com/in/phil-opp/

Email: [email protected]

After finishing my computer science master in 2019 and focusing the last few months on open-source software, I'm looking for a job now. I specialized on operating systems, embedded, and systems programming. I'm writing a blog about OS development in Rust at https://os.phil-opp.com/.


Great to hear that! I also found the official documentation a bit short on background information, so I decided to write my own explanation rather than link to something existing. Given that the async/await implementation is still quite young, I'm sure that the official docs will improve over time.


Thanks! I created them using draw.io (https://about.draw.io/).


On the other hand, these strict requirements make sure that the global allocator is thread-safe. Also, I only count two compiler errors mentioned in this post and both have a valid reason (you can't modify values behind an immutable reference; you can't implement traits for types of other crates).


Both of them are invalid. What if you want a single-threaded allocator without the mutex overhead? And forbidding implementing traits for some types is just arbitrary stupidity. Developers don't need a nanny compiler to tell them which types they can or can't extend. Orphan instances are the solution in some cases.


For single threaded programs you can e.g. use a Mutex implementation that only disables interrupts for the critical section (https://docs.rust-embedded.org/book/concurrency/#mutexes). Rust does not forbid you from doing these potentially memory unsafe things, it just requires you to use an `unsafe` block for this.

The reason for forbidding these trait implementations is compatibility. For example, the dependency might add an implementation for the trait at some point, so that there would suddenly be conflicting implementations after a semver-compatible update. You can still implement the trait by creating a wrapper type.


> The overflow scenario should be treated the same as the out-of-memory scenario and also return null.

Good point! I'll prepare an update to fix this.

> It can do better, in `dealloc` you can use `ptr` and `layout` to check if the allocation is at the end of the allocated region. If it is, `bump.next` can be reduced by `layout.size()`. This is optimal for lifo/stack style allocation patterns.

You're right. I already created a PR [1] for this shortly after publishing the article, but it seems like I forgot to merge it. There should be now an additional "Fixing the Test?" section that talks about freeing the last allocation.

[1]: https://github.com/phil-opp/blog_os/pull/722



Thanks so much!

Yes, I'm eagerly awaiting https://github.com/rust-lang/rust/pull/58457 and your follow-up https://github.com/rust-lang/rust/pull/60703. AFAIK, a global allocator is still required with them (so we still have to initialize it), but it allows us to choose a different allocator for some collections, e.g. to avoid deadlocks in interrupt handlers.


An interesting fact about this is that the author is on the Microsoft docs team, which uses a similar system for docs.microsoft.com [1]. So I don't think that GitHub has a problem with this approach.

For an example of Microsoft's system in use, see the "Feedback" section on [2].

[1]: https://github.com/MicrosoftDocs/feedback/issues/396

[2]: https://docs.microsoft.com/en-us/azure/azure-functions/funct...


GitHub is owned by Microsoft though, so there's no guarantee they won't have problems with someone else doing this.


Great to hear that!


Author here. This post is a rewrite of the previous Unit Testing [1] and Integration Tests [2] posts. It creates a custom test framework that runs test functions inside QEMU, so that they run in a realistic environment (compared to running on the host system).

[1]: https://os.phil-opp.com/unit-testing/ [2]: https://os.phil-opp.com/integration-tests/

The new test framework directly works with `cargo xtest`, which brings the project closer to using standard cargo programs for building, testing, and running. The last remaining step is the `std` Aware Cargo RFC [3]. When the RFC is merged and implemented, cargo-xbuild will no longer be needed and the familiar cargo build, cargo run, and cargo test commands will just work, including IDE integration.

[3]: https://github.com/rust-lang/rfcs/pull/2663


Just wanted to say thanks so much for your blog posts! I have been working on a medical device in no_std Rust and your articles have been super helpful in setting up a robust unit test suite. I have been trying to hack at the utest crate and get on-device integration tests running again for quite some time... good to know there is movement on the custom test runner front :)


It's the first time that I hear about Rust on medical devices. Sounds really interesting! I'm glad to hear that my blog was useful to you!

> I have been trying to hack at the utest crate and get on-device integration tests running again for quite some time... good to know there is movement on the custom test runner front :)

The custom test framework feature of Rust was silently introduced in https://github.com/rust-lang/rust/pull/53410 and I didn't know that it even existed a month ago. But it works really well and seems like a good solution for your problem.


How did you deal with lack of allocation errors?

In my mind, what would make rust a fanatic kernel language is tracking allocators with types at the compiler level like it does for Send and Sync.


There is some minimal support for fallible allocation through the try_reserve method on various collections. See https://github.com/rust-lang/rust/issues/48043 for more information.

Other than that, there is ongoing work for custom allocator support in collections: https://github.com/rust-lang/rust/issues/42774. Maybe that's what you meant with tracking the allocators at the type level?

It's of course also possible to create own fallible collection types, for example as a wrapper around the normal liballoc and try_reserve.


But none of the standard libraries expose those failures. Did you end up writing an std_lib?


The try_reserve method is exposed on all collection types, e.g. [1],but it is still unstable.

[1]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.try...

I did not write my own stdlib since I didn't need it yet (the blog has no heap allocation yet). And I think infallible allocation is good enough for the beginning.

But I think it should be possible to create a fallible wrapper library around liballoc quite easily. We only need an appropriate call to try_reserve for every wrapped function. For example, `push` would do a `try_reserve(1)` before calling the push function of liballoc.


It is possible to access other address spaces, it is just a bit more complicated:

- Set the recursive entry of the active level 4 table to the (recursive) level 4 table of the other address space. - Now the recursive addresses point to the other address space, so you can access its tables freely. - The difficult part is to switch back to the active address space again because can no longer access the original level 4 entry. This can be solved with a temporary mapping to the active level 4 table or by using different level 4 entries for the recursive mapping of active and inactive address spaces.

However, I'm no longer sure whether recursive page tables are a good choice for the blog since they're complicated, difficult to learn and not portable. See my reply to DSMan.


Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: