WEBVTT

00:00.000 --> 00:13.000
It doesn't mean I don't have any working experience though.

00:13.000 --> 00:18.000
I've been working as a part-time software engineer at Hadron Security since 2022.

00:18.000 --> 00:21.000
If anyone likes cybersecurity, it can also come talk to me.

00:21.000 --> 00:25.000
I've been part of the Madagherne Project since 2019.

00:25.000 --> 00:27.000
I've been there for a long time.

00:27.000 --> 00:30.000
I want my GitHub, it's on the screen.

00:30.000 --> 00:33.000
Okay, so what are we going to cover today?

00:33.000 --> 00:36.000
We're going to be covering the basics of post-exignals.

00:36.000 --> 00:37.000
Not everything.

00:37.000 --> 00:39.000
There's a lot that can be said about post-exignals.

00:39.000 --> 00:44.000
But in particular, we're interested in how they interact with blocking ciscals.

00:44.000 --> 00:48.000
After that, we start looking at the kernel side of post-exignals

00:48.000 --> 00:52.000
and a little bit into how you would normally implement them in monolithic kernels.

00:52.000 --> 00:56.000
Then talk about the challenges you might face in micro kernels.

00:56.000 --> 00:59.000
And then finally, we talk about Madagherne's solution,

00:59.000 --> 01:02.000
which we've designed over the past two years now.

01:02.000 --> 01:06.000
And then finally, we conclude with some lessons learned,

01:06.000 --> 01:09.000
yeah, take home messages that you might be able to apply

01:09.000 --> 01:13.000
to your own microchannel projects or in general asynchronous projects.

01:13.000 --> 01:17.000
All right, first, so what is Madagherne for those of you don't know?

01:17.000 --> 01:21.000
It's a microchannel-based, fully asynchronous hobbyist operating system.

01:22.000 --> 01:25.000
It was founded by Alexander from the Grinton.

01:25.000 --> 01:29.000
In 2016, he's the other organizer for this devroom,

01:29.000 --> 01:32.000
who unfortunately can be here today.

01:32.000 --> 01:35.000
And yeah, we put a lot of effort into it.

01:35.000 --> 01:38.000
We run a lot of programs, some of them are shown on the screen.

01:38.000 --> 01:40.000
Like X-clock and G-Lex gears.

01:40.000 --> 01:44.000
We have a browser as well that's not really my part,

01:44.000 --> 01:48.000
but yeah, some amazing people from the Madagherne team have worked on.

01:49.000 --> 01:53.000
Okay, so, post-exignals, really briefly.

01:53.000 --> 01:57.000
There are sort of asynchronous intrups for user space.

01:57.000 --> 02:01.000
Yeah, there's a huge standard with a bunch of different functions

02:01.000 --> 02:06.000
and behaviors that basically amount to asynchronous intrups for user space.

02:06.000 --> 02:11.000
I'm not going to cover all of post-exignals because we can say a lot about them,

02:11.000 --> 02:14.000
but frankly, we'd be here until tomorrow.

02:15.000 --> 02:20.000
So, right now, we're just going to focus on the part that blocking ciscals can be interrupted by signals.

02:20.000 --> 02:25.000
And I'll explain more about what I mean with blocking signals and intruption.

02:25.000 --> 02:29.000
So, suppose we're a user space program and we enter a read.

02:29.000 --> 02:31.000
We'd like to read a file.

02:31.000 --> 02:32.000
A read is blocking.

02:32.000 --> 02:36.000
That means that once we go into this read call,

02:36.000 --> 02:39.000
the kernel or whatever abstraction we want to have for the kernel

02:40.000 --> 02:44.000
doesn't return control flow to us until it's done reading.

02:44.000 --> 02:47.000
Which for some file descriptors can take a long time.

02:47.000 --> 02:52.000
For example, for example, if we have cat open,

02:52.000 --> 02:55.000
it's going to read is going to wait until someone input something into the terminal,

02:55.000 --> 02:57.000
which can take a very, very long time.

02:57.000 --> 03:02.000
So, what we can do is we can send a signal to the process.

03:02.000 --> 03:04.000
So, the signal needs to be delivered by the kernel.

03:04.000 --> 03:09.000
Some other program needs to send a signal or the kernel itself sends the program a signal.

03:09.000 --> 03:17.000
Like, for example, if you press control C, you'll get a signal sent to the program.

03:17.000 --> 03:21.000
And then something's going to happen that's going to interrupt the read call.

03:21.000 --> 03:25.000
And the return value depends on when the ciscals actually interrupted.

03:25.000 --> 03:27.000
So, they're actually two cases.

03:27.000 --> 03:31.000
Either read was interrupted before the kernel could complete any meaningful work,

03:31.000 --> 03:33.000
so before it could read anything.

03:33.000 --> 03:36.000
And it's going to return, return value of minus one,

03:36.000 --> 03:40.000
and set the error number to this e-interrupt error,

03:40.000 --> 03:44.000
which basically signals us to get like, hey, we got interrupted.

03:44.000 --> 03:46.000
We should probably try again.

03:46.000 --> 03:50.000
Or what happens is that read finished successfully,

03:50.000 --> 03:54.000
or basically, or it's just going to return a positive number,

03:54.000 --> 03:56.000
which either means read finished successfully,

03:56.000 --> 03:59.000
or it was interrupted but completed partial work,

03:59.000 --> 04:01.000
as everything is in kernel development.

04:01.000 --> 04:05.000
But basically, this is sort of the framework that you would use.

04:05.000 --> 04:09.000
Now, this doesn't really work in managarm specifically,

04:09.000 --> 04:12.000
but probably also other micro kernels.

04:12.000 --> 04:18.000
To understand why we need to know a little bit about the managarm system architecture.

04:18.000 --> 04:20.000
So, managarm has a single post-exerver.

04:20.000 --> 04:22.000
This is for pragmatic reasons,

04:22.000 --> 04:25.000
and we'll also see that later on this is quite useful.

04:25.000 --> 04:32.000
But post-exerver basically means that it emulates the entirety of the post-ex...

04:32.000 --> 04:34.000
Yeah, specification basically.

04:34.000 --> 04:36.000
So, handles processes, and threads.

04:36.000 --> 04:42.000
The kernel only has an abstract view or abstract sort of implementation of threads.

04:42.000 --> 04:45.000
Doesn't have anything like post-ex processes.

04:45.000 --> 04:47.000
But it is a micro kernel.

04:47.000 --> 04:50.000
So, this post-exerver can delegate to external servers.

04:50.000 --> 04:53.000
So, for example, we have an external neural net server,

04:53.000 --> 04:54.000
some graphics servers, et cetera.

04:54.000 --> 04:59.000
And so, basically, to avoid having the case where a user-space program

04:59.000 --> 05:01.000
needs to talk to the post-exerver,

05:01.000 --> 05:04.000
which then deliates to an external server.

05:04.000 --> 05:08.000
We'd like to have it that the user-space program can talk to the external server directly

05:08.000 --> 05:11.000
once we go through the first layer of interaction.

05:11.000 --> 05:13.000
So, that's illustrated in this diagram,

05:13.000 --> 05:18.000
where basically a user-space program would call open on some file on the file system,

05:18.000 --> 05:21.000
for example, then post-ex will go and find, okay,

05:21.000 --> 05:23.000
who owns this file.

05:23.000 --> 05:25.000
And it might find that it's owned by an external server,

05:25.000 --> 05:28.000
in which case it'll send an open request to this server.

05:28.000 --> 05:33.000
And the server is going to send back to post-ex an IPC lane.

05:33.000 --> 05:39.000
And this IPC lane is going to be tunneled back to the user-space program.

05:39.000 --> 05:42.000
And so, the next time we want to interact with this file, we've opened.

05:42.000 --> 05:45.000
Instead of telling post-ex, hey, we want to read from this file,

05:45.000 --> 05:48.000
and then post-ex goes to external server and says, hey,

05:48.000 --> 05:50.000
the user-space wants to read from this file.

05:50.000 --> 05:54.000
The user can actually just send a read request to the external server directly.

05:54.000 --> 05:58.000
This is really nice because we've now,

05:58.000 --> 06:07.000
we've now done an optim- we basically have our IPC overhead for reads or a lot of things actually.

06:07.000 --> 06:11.000
The issue is that this scatters bookkeeping kind of,

06:11.000 --> 06:15.000
because now this interruptable call read is handled by the external server,

06:15.000 --> 06:18.000
but the external server doesn't have any notion of processes,

06:19.000 --> 06:21.000
or threads, or signals, or anything.

06:21.000 --> 06:24.000
It only knows, hey, I've received the read call,

06:24.000 --> 06:26.000
and I need to complete this request,

06:26.000 --> 06:28.000
and I need to send it back.

06:28.000 --> 06:32.000
So, this means that we can't simply put in cancellation points,

06:32.000 --> 06:35.000
for example, the external server, because what is it going to check?

06:35.000 --> 06:37.000
It doesn't know if a signal's been delivered.

06:37.000 --> 06:40.000
We might ask post-ex, hey, is there a signal,

06:40.000 --> 06:43.000
but that is a little bit obtuse.

06:43.000 --> 06:47.000
So, yeah, that's one part that makes this difficult.

06:47.000 --> 06:50.000
The second part that makes this difficult is that

06:50.000 --> 06:53.000
Managarm is fully asynchronous from the bottom up.

06:53.000 --> 06:57.000
So, this means that when our external or every server is as well.

06:57.000 --> 07:00.000
So, that means that when the user snippets have request,

07:00.000 --> 07:02.000
it just gets added to some working queue,

07:02.000 --> 07:06.000
and what I really want to highlight is that the co-routine,

07:06.000 --> 07:08.000
we use co-routine, C++ co-routine,

07:08.000 --> 07:11.000
to implement all of our asynchronous,

07:11.000 --> 07:14.000
and this, the co-routine lifetime,

07:14.000 --> 07:16.000
is independent of a process lifetime,

07:16.000 --> 07:18.000
which means that if the process goes away,

07:18.000 --> 07:22.000
dies for whatever reason, or gets interrupted,

07:22.000 --> 07:25.000
then it's not like the co-routine gets notified

07:25.000 --> 07:27.000
and actually gets canceled.

07:27.000 --> 07:31.000
It's just going to continue doing its thing.

07:31.000 --> 07:35.000
So, at this, oh, yeah,

07:35.000 --> 07:37.000
is pretty decently thought out,

07:37.000 --> 07:42.000
and we have some nice interfaces that we can use

07:43.000 --> 07:46.000
to design an elegant solution.

07:46.000 --> 07:50.000
So, firstly, the kernel provides a one-shot event primitive.

07:50.000 --> 07:53.000
So, basically, this is just, you get a kernel handle,

07:53.000 --> 07:56.000
you can call raise event, that's a Cisco,

07:56.000 --> 07:58.000
on this one-shot event,

07:58.000 --> 08:00.000
and then whatever is listening,

08:00.000 --> 08:01.000
whatever subscriber to the event,

08:01.000 --> 08:03.000
we'll get an notification like,

08:03.000 --> 08:07.000
hey, this event has been called.

08:07.000 --> 08:10.000
And the nice part is that it can be easily passed across

08:11.000 --> 08:14.000
as boundaries using mannergarms IPC.

08:14.000 --> 08:17.000
So, here we see this helix thing again,

08:17.000 --> 08:20.000
and what I want you to look at is this helix push descriptor,

08:20.000 --> 08:23.000
cancel event, basically means we can make an event,

08:23.000 --> 08:25.000
and we can just shove it over IPC,

08:25.000 --> 08:28.000
and the kernel will handle all of the difficulties

08:28.000 --> 08:32.000
with subscription and making sure it's valid

08:32.000 --> 08:36.000
in the peers' address space, et cetera.

08:37.000 --> 08:41.000
And we can also take advantage of a consolidated post-exerver,

08:41.000 --> 08:45.000
and I'm highlighting this because it's sort of a non-microchernal way,

08:45.000 --> 08:49.000
as in it's quite a large, monolithic thing, the post-exerver.

08:49.000 --> 08:52.000
But, in mannergarm, it's actually,

08:52.000 --> 08:54.000
we don't call it that anymore,

08:54.000 --> 08:57.000
but we used to call it a pragmatic microchernal-based LS,

08:57.000 --> 09:00.000
and this is part of the pragmatism that we'll make the implementation

09:00.000 --> 09:01.000
of it easier.

09:01.000 --> 09:03.000
And I'll show you why.

09:03.000 --> 09:07.000
So, what will our, basically,

09:07.000 --> 09:11.000
what is our Cisco model going to look like?

09:11.000 --> 09:13.000
Well, what's going to happen is

09:13.000 --> 09:16.000
post-ex, whenever a user space per works,

09:16.000 --> 09:19.000
who's a space process starts,

09:19.000 --> 09:21.000
post-ex is going to load an event,

09:21.000 --> 09:23.000
this one-shot event,

09:23.000 --> 09:25.000
into a shared memory page,

09:25.000 --> 09:27.000
but shared between post-ex and the user space progress,

09:27.000 --> 09:28.000
process.

09:30.000 --> 09:32.000
So, in this diagram,

09:32.000 --> 09:35.000
post-ex has this console event,

09:35.000 --> 09:40.000
and it's going to share it with a user in a shared memory page.

09:40.000 --> 09:44.000
Now, user space attaches this event

09:44.000 --> 09:47.000
to every eintripsis call with eintripsis call,

09:47.000 --> 09:50.000
it just means anything that can block, basically.

09:50.000 --> 09:53.000
So, we see here, the user space,

09:53.000 --> 09:55.000
the user, it's going to send a request,

09:55.000 --> 09:57.000
it's going to send a reader request to the external server,

09:57.000 --> 10:00.000
and it's going to attach this console event to it.

10:00.000 --> 10:02.000
The diagram is going to make sure that this console event

10:02.000 --> 10:06.000
is also valid in the address space of the external server.

10:06.000 --> 10:09.000
And then, if post-ex delivers a signal,

10:09.000 --> 10:12.000
it knows when it delivers a signal,

10:12.000 --> 10:15.000
then it's also going to raise the event.

10:15.000 --> 10:18.000
So, it's going to happen here,

10:18.000 --> 10:21.000
is post-ex sends a signal to the user,

10:21.000 --> 10:25.000
someone press control C, someone wanted to notify them of something,

10:25.000 --> 10:28.000
and at the same time, it's going to call raise event

10:28.000 --> 10:30.000
on this console event.

10:30.000 --> 10:33.000
And the external server in the read handler

10:33.000 --> 10:38.000
actually listens or basically waits for this console event to get triggered.

10:38.000 --> 10:45.000
And if it does, it knows, okay, now this request has been interrupted,

10:45.000 --> 10:47.000
and we need to do something.

10:47.000 --> 10:50.000
We can either cancel the console decoratine,

10:50.000 --> 10:53.000
send back what we already have sort of implementation details,

10:53.000 --> 10:55.000
but this is how it work,

10:55.000 --> 10:57.000
and then, yeah, you see the external server,

10:57.000 --> 10:59.000
sends back an e-interrupt call,

10:59.000 --> 11:03.000
e-interrupt results back to the user.

11:03.000 --> 11:08.000
Yeah, so this is sort of our solution for

11:08.000 --> 11:10.000
how to implement accounts.

11:10.000 --> 11:12.000
I open it, and then I press control C,

11:12.000 --> 11:14.000
and we actually return back to the terminal,

11:14.000 --> 11:17.000
and I can typecat again,

11:17.000 --> 11:24.000
typecat again, without it eating my first C character,

11:24.000 --> 11:27.000
which happened quite a bit.

11:27.000 --> 11:32.000
So yeah, these are some lessons that I learned while implementing this.

11:32.000 --> 11:36.000
As you can just programming across the process boundaries,

11:36.000 --> 11:39.000
make for really confusing lifetimes.

11:39.000 --> 11:45.000
It's probably a solve problem with some like RPC frameworks,

11:45.000 --> 11:47.000
but with a managarm, this isn't a solved problem,

11:47.000 --> 11:51.000
because we sort of detect the lifetime of the co-reteam

11:51.000 --> 11:56.000
from the actual process that makes the request.

11:56.000 --> 12:00.000
What I also, is that abstracting cancellation using cancel tokens,

12:00.000 --> 12:05.000
which is a nice asynchronous sort of primitive

12:05.000 --> 12:08.000
and events make for really readable code.

12:08.000 --> 12:11.000
So, like I said, we didn't have to pull posts,

12:11.000 --> 12:16.000
or ask posts, as a signal been sent into the process or anything.

12:16.000 --> 12:19.000
Using these events and cancel tokens,

12:20.000 --> 12:23.000
yeah, we can implement this in a really elegant way.

12:23.000 --> 12:26.000
And the pragmatic approach of the post-exerver

12:26.000 --> 12:28.000
makes a burden on user space smaller.

12:28.000 --> 12:31.000
Yeah, I want to highlight this for microcriminal devroom.

12:31.000 --> 12:33.000
It was easier to do this.

12:33.000 --> 12:35.000
If posts didn't know, for example,

12:35.000 --> 12:37.000
if you had a separate service sending signals,

12:37.000 --> 12:40.000
then the burden is on user space

12:40.000 --> 12:44.000
to raise this cancel event.

12:45.000 --> 12:48.000
Basically, instead of signal getting sent and posts

12:48.000 --> 12:51.000
sending, calling raise event at the same time,

12:51.000 --> 12:52.000
we'd have signal get sent,

12:52.000 --> 12:55.000
and then user having to call raise event.

12:55.000 --> 12:57.000
And then sometimes, because posts exist,

12:57.000 --> 12:59.000
does know that the process dies,

12:59.000 --> 13:00.000
it'll call raise event,

13:00.000 --> 13:03.000
because the use space can't call raise event

13:03.000 --> 13:06.000
if it's died in the process of a signal.

13:06.000 --> 13:11.000
So, having everything consolidated in this big post-exerver,

13:12.000 --> 13:14.000
post-exconglomerate,

13:14.000 --> 13:17.000
yeah, it's really helpful when implementing this.

13:17.000 --> 13:20.000
So, that was my talk.

13:20.000 --> 13:22.000
Yeah, you can check out the project on our GitHub.

13:22.000 --> 13:24.000
We have a blog as well,

13:24.000 --> 13:27.000
which we irregularly update sometimes.

13:27.000 --> 13:29.000
We have a YouTube channel as well.

13:29.000 --> 13:32.000
I stream sometimes my development on Twitch,

13:32.000 --> 13:34.000
so you can check that out as well if you want.

13:34.000 --> 13:37.000
And you can also join our Discord for discussions.

13:37.000 --> 13:39.000
And yeah, I want to thank all the contributors

13:39.000 --> 13:41.000
to the Manningon project,

13:41.000 --> 13:43.000
because yeah, they make it happen.

13:43.000 --> 13:45.000
I'm just a small column as well.

13:45.000 --> 13:47.000
So, thank you for listening.

13:53.000 --> 13:55.000
Also, we have Manningon stickers.

13:55.000 --> 13:56.000
We have about two minutes.

13:56.000 --> 13:58.000
We have lots of questions.

13:58.000 --> 13:59.000
Uh, nothing.

13:59.000 --> 14:02.000
We have so signals from now to turn,

14:02.000 --> 14:03.000
not as far as programs,

14:03.000 --> 14:05.000
that's all the kind of ones, but.

14:05.000 --> 14:06.000
Yes.

14:06.000 --> 14:19.000
So, yeah, that's a good question.

14:19.000 --> 14:21.000
Oh, can you repeat the question?

14:21.000 --> 14:22.000
No, can you repeat the question?

14:22.000 --> 14:24.000
Oh, can I repeat the question?

14:24.000 --> 14:27.000
Oh, is that what we're doing?

