The way I did it was:
Enumerating Services/Applications/Modules
You need some way to turn more common identifiers into numerical values. Some services that are critical to the system could be hard coded; just for example but not actually suggesting.. the VFS(Virtual File System).
So generally you could assign textual names to modules and let them register this with the kernel and associate it with their VID; or as their VID.
Enumerating Supporting Interfaces
You need to query a module once you have resolved the textual name into the VID about what interfaces it has to ensure that you are going to be able to communicate with it using a protocol the client supports.
A way to do this is to generate a globally unique identifier number for each interface created, just like COM on windows. Then you could sub that out with versions. Where the server (service, application, module) can provide one or many versions of a certain specific interface.
--
This should allow you to find X and query X for a protocol (interface) Z and essentially establish dependable and valid communication between client and X over Z.
Message Passing
If it is short messages you could pack them into registers and issue the software interrupt into the kernel and allow it to hand it over to the remote end; either by placing it in a buffer or alerting the remote end that it needs to issue a system call to get the short message.
If the message is long you could use pages or copy buffers from process space to process space. You could even use shared pages and create a ring buffer with locks and check so that reliable concurrent access by two processes simultaneously is possible.
Message Passing Methods
1. short message (passed via registers and read via registers)
This is fast, but you are limited to message size.
2. short message (passed via registers and placed in buffer on remote side)
This is faster, but still limited to message size.
- A structure supporting concurrent access could be used to allow server to check for messages. (faster)
- A system call could be used to check for messages. (slower)
3. long message (passed via copy memory from address space to address space)
This is slower, but supports large messages. But, the time to copy a message increases with every byte added to a message.
- A structure supporting concurrent access could be used to allow server to check for messages. (faster)
- A system call could be used to check for messages. (slower)
4. long message (passed by un-mapping page from client and mapping to server)
This is faster, but will cause the process to re-read the page table. So not sure which might be slower? Interrupt or flushing a table?
- A structure supporting concurrent access could be used to allow server to check for messages. (faster)
- A system call could be used to check for messages. (slower)
You will need a way for the remote end (server) to cleanup used pages after it has read the needed information from them. You will also need to think about a way to prevent DOS(Denial Of Service) attacks by flooding a process with messages.
5. long message (passed by mapping page from client to server)
This is about the same speed as 4.
- A structure supporting concurrent access could be used to allow server to check for messages. (faster)
- A system call could be used to check for messages. (slower)
You will need a way for the remote end (server) and local (client) to cleanup used pages after it has read the needed information from them. You will also need to think about a way to prevent DOS(Denial Of Service) attacks by flooding a process with messages.
6. long message/short message (passed using shared memory and reliable ring buffer)
This is very fast, but more complicated to implement.
- The kernel only maps pages allowing them to be shared. The protocol is completed implemented by the client and server. The kernel knows nothing about IPC between them.
You do not have to worry about cleaning up pages, or preventing a DOS attack -- and it should be really fast.