Hi,
kenz wrote:
So I have now implemented a semaphore including wait list and connection to scheduler etc. So I wanted to use this new shiny semaphore.
Kernels also tend to use spinlocks (especially for things like the scheduler's data structures); where if the lock can't be acquired the CPU waits and doesn't do a task switch. In general; most locks within the kernel are only held for a very short amount of time and therefore it's usually faster to wait/spin than it is to do two (relatively expensive) task switches.
kenz wrote:
So I have created a semaphore for the text console. So I have a syscall handler (that can handle calls from userspace) that can print a string. So of course I wait for the semaphore when printing the string and release it afterwards. However, I also have an interrupt handler for the keyboard which prints something everytime there's a keyboard interrupt. This of course also waits for the semaphore and releases it after printing.
Potentially excluding "early debugging hacks"; a keyboard driver shouldn't write anything to any (real or virtual) terminal, display or anything else. Typically keyboard IRQ handler sends something to the rest of the keyboard driver, and the rest of the keyboard driver does some processing and sends something to user-space (e.g. to a terminal emulator, to a GUI, ...).
For "early debugging hacks" typically it's enough to do atomic writes directly to the framebuffer without any need for locks; especially if the OS is using text mode.
kenz wrote:
But now the failing case is this: A process calls the print system call. The print system calls grabs the semaphore. However, while inside the function that actually updates the video memory, a keyboard interrupt occurs. The keyboard interrupt handler also tries to grab the semaphore. But since the semaphore has already been acquired, the process is added to the wait list and put to sleep. However, this means that the code that was interrupted (the actual printing, triggered by the process itself, which holds the lock) will now never finish up and actually release the lock! This is because there's no way we can "switch out" of the interrupt handler and back to the system call.
So how is this usually addressed in more general terms (more general than just saying "don't print from the interrupt handlers!" because I guess this conflict could occur over other resources)?
For these situations typically the kernel has 2 kinds of locks, where one disables IRQs and the other doesn't. If data (that needs a lock) is used by an IRQ handler then that data needs to use an "IRQ disabling lock" (and if the data isn't used by an IRQ handler then it uses a cheaper lock that doesn't disable IRQs). Of course this only works for spinlocks, but IRQ handlers shouldn't be doing anything that takes so long that a mutex or semaphore makes sense.
Cheers,
Brendan