Fast Single Thread RPC / IPC with sub-calls, how to stack?
Posted: Tue Sep 03, 2019 2:36 am
I'm at the point, where I am implementing my fist application interface in my microkernel. Context switching works. Sample Call (via INT) works, too, incl. result value (one DWORD). Generally, we are talking about small values, that can fit on stack/registers, no big data (they should be transfered via SharedMemory).
The Question I have, how to call depending services? With the first level, it's simple:
UserThread: INT 250 --> Kernel Interrupt, takes tss.ESP's kernel stack, exec the chosen function, returns (IRET), back to user Thread. tss.ESP is free for next Thread.
But, what happens, if Kernel/Interrupt Context wants to call a Microservice-Remote Call? The Original User Thread Stack, and the Kernel Stack needs to be protected. And I want to avoid to "create a new thread" for the sub-call.
One idea I have is the following way:
- UserThread A: INT 250
- Kernel Interrupt, takes tss.ESP's kernel stack
- Before saving register states, move to another ESP, so tss.esp is not exclusivly used anymore.
- the new ESP is "on top of the user stack", but page aligned and will be unmapped from the original user space.
- If we need to call a service, we create a new 4k page below (or "on top") of the stack, and map it for the service address space.
- Call Service B.
- Now, if Service needs to call another IPC-Method C (Sub-Service), it acts like a normal User Thread: Int 250, and so on.
The stack would look like this (all 4k aligned, from stack bottom to top):
UserStack
KernelStack
Serivce Stack
KernelStack
Sub-Service-Stack
...
So, I can cleanly unwinding the stacks, and it will simplify where to get a new stack memory (Current ESP+4096 & 0xFFF + map/unmap for Stack protection, using INVLPG to invalidate single page)
This is pure synchronous design, asynchronous is not planned yet.
I didn't started this implementation yet. I want to ask you if you see any problems - or - how to solve this better.
Goal: Fast IPC in a single thread, synchronous (=blocking) with the ability that a service can call sub services (must have feature) without overhead having additional threads. No big messages, only small values (for example a single DWORD).
The Question I have, how to call depending services? With the first level, it's simple:
UserThread: INT 250 --> Kernel Interrupt, takes tss.ESP's kernel stack, exec the chosen function, returns (IRET), back to user Thread. tss.ESP is free for next Thread.
But, what happens, if Kernel/Interrupt Context wants to call a Microservice-Remote Call? The Original User Thread Stack, and the Kernel Stack needs to be protected. And I want to avoid to "create a new thread" for the sub-call.
One idea I have is the following way:
- UserThread A: INT 250
- Kernel Interrupt, takes tss.ESP's kernel stack
- Before saving register states, move to another ESP, so tss.esp is not exclusivly used anymore.
- the new ESP is "on top of the user stack", but page aligned and will be unmapped from the original user space.
- If we need to call a service, we create a new 4k page below (or "on top") of the stack, and map it for the service address space.
- Call Service B.
- Now, if Service needs to call another IPC-Method C (Sub-Service), it acts like a normal User Thread: Int 250, and so on.
The stack would look like this (all 4k aligned, from stack bottom to top):
UserStack
KernelStack
Serivce Stack
KernelStack
Sub-Service-Stack
...
So, I can cleanly unwinding the stacks, and it will simplify where to get a new stack memory (Current ESP+4096 & 0xFFF + map/unmap for Stack protection, using INVLPG to invalidate single page)
This is pure synchronous design, asynchronous is not planned yet.
I didn't started this implementation yet. I want to ask you if you see any problems - or - how to solve this better.
Goal: Fast IPC in a single thread, synchronous (=blocking) with the ability that a service can call sub services (must have feature) without overhead having additional threads. No big messages, only small values (for example a single DWORD).