Let me first preface my post with a disclaimer: I'm definitely not the most knowledgable person on this subject (or any subject for that matter).
max wrote:Hey!
Hey!
- Redirecting: the driver user process could register itself as an interrupt handling process for a specific interrupt number and provide the address of a handling function. When the interrupt occurs, the kernel would push the current EIP of the userspace process to the user stack, and then set it to that interrupt handling function. Then when returning to userspace, the interrupt handling function does its work when "return"ing it would take the old EIP from it's stack and continue its work. This could work in theory, but it seems to be a really ugly thing to do..
I don't quite get what you're trying to say here. Aren't these handler things what most people use (at least when starting)? ie. Handler is called from the interrupt gate, interrupt gate restores previous state. What you're describing (push the current RIP etc) seems to describe what the CPU does when it goes into an interrupt gate.
ie.
Code: Select all
CPU executing normally
INT
CPU pushes rflags, rip, ss, cs and some other things
CPU jumps to indicated address in IDT
handler does things including ACK the PIT
handler does iret
CPU pops rflags, rip etc. from the stack
state is restored
CPU executes normally.
GOTO 0
This might be a misunderstanding on my part, but the above sounds like what you're describing and yes, it works in user mode as well.
- Messaging: when an interrupt occurs, a message is sent to the process (or to a port as in my system). The process would then have to continuously check if there is a message waiting, what would waste performance. Same thing with..
This is what I personally use. I'll elaborate below.
- Polling: when an interrupt occurs, the kernel itself memorizes that this interrupt has happened. The process must then also continuously poll the kernel via a syscall if that interrupt has happend.. also bad.
This seems like the worst of the 3 solutions you have proposed... The kernel would have to maintain a list of all pending interrupts, which would likely require looping on somebody's part to check for a particular interrupt... not a good idea.
As for how I do it:
My kernel started out as a large unstable monolithic piece of crap, but has slowly evolved into a somewhat more stable, somewhat more microkernel-ish design.
Once the kernel finishes its job (the kernel really just sets up the system and provides a set of system calls and interfaces to hardware, IMO it doesn't actually 'DO' anything after that), it becomes some sort of dispatch system.
While I realise this is probably quite slow, it enables a somewhat more extensible (ie. lazy on my part) system, where I can run drivers in userspace as processes communicating via IPC (honestly the main reason for going in this direction).
Basically, the kernel installs a set of default INT handlers that don't do anything except send messages to the dispatch thread. The dispatch thread checks a list of processes that are interested in whatever interrupts, then simply forwards the interrupts to the processes.
'Interested' processes send a message to the dispatch thread indicating their intended INT vector, which the dispatcher will put in its list of interested threads for that vector. This also means I don't need to fiddle with the IDT directly afterwards.
The above works for synchronous interrupts, or threads that only do things when an interrupt happens (like a keyboard handler or something). For asynchronous interrupts, you'll likely want your dispatcher to also accept a callback function, and for the thread to register its interest with that callback function pointer.
This lets it do its other things, then process an interrupt when it needs to via the interrupt callback. At least I think this is how most asynchronous systems work, callbacks.
As for the problem of having to constantly check for messages, the simplest way to do that is to just implement a Block() function that waits on an IPC message. The function that sends a message (in the kernel code) should check if its sleeping, and if so put it into the run list. Note that for asynchronous messaging, this is not an issue since you'd be doing something else while waiting for the message anyway, so the callback method works.
While I will say this is by far not the best way, I am just describing how I do it, others are welcome to criticise (the hell out of) this.