Do both traps and interrupts give control of the hardware to the CPU - operating-system

I am very confused whether both traps and interrupts can give control of the hardware to the CPU.
Can someone explain why this won't hold or not?

I think it would be more accurate to say that both traps and interruptions get processed by an interrupt handler (there's a trap handler and interrupt handler but I think it's the same concept).
The interrupt handler then processes the raised interrupt and attempts to resolve it. With a trap it may be something like a division by 0 and with an interrupt it could be something like the disk finished writing a file.
In some cases the trap may be "intentional" - this is useful if your program requires some resources it doesn't have and wants to request them. It raises an exception (trap) and attempts to initiate a context switch to another process while it waits for its resources (no point in hogging the CPU if it's just waiting).
So as you can see, an interrupt can necessitate hardware control but a trap (context switch) may not necessitate hardware use.

I think the best way to view a fault/trap/interrupt is as a function call. The operating system sets up a vector of handlers for the different events. When they occur, the CPU calls the appropriate function.
The only oddity is that an interrupt can occur asychronously. Faults and traps occur as the result of the instruction stream.

Related

Computer Reboots After "sti" Instruction

I am trying to implement interrupts in x86 operating system project. However, after loading interrupt descriptor table with lidt, I issue sti command and this "sti" command reboots the computer. And also, I am in the protected mode. Any idea what might be happening?
Some things cause exceptions. When the CPU can't start the corresponding exception handler it falls back to a generic "double fault" exception, and when the CPU can't start that exception handler the CPU falls back to a "triple fault" condition which mostly means that the computer is reset.
It's likely that there are pending IRQs (that occurred while interrupts were masked with "cli" and have been waiting for CPU to be ready to receive them); so when you do "sti" the interrupt controller sees the CPU is ready to receive an IRQ now and immediately sends one to the CPU; and likely that the interrupt handler for whichever IRQ the CPU receives is causing an exception (that leads to double fault, that leads to triple fault/reset).
The easiest way to figure out what is happening is to run it under an emulator that tells you what happened in its logs. The alternative is to write usable exception handler/s for any exceptions that are involved (most likely, a general protection fault exception handler); so that the exception handler can give you information about what went wrong (e.g. the "error code" provided by the CPU to the general protection fault handler may indicate which IDT entry the CPU tried to use for the IRQ).
Note that during boot the best sequence is to mask all IRQs in the interrupt controller/s, then let firmware handle any pending IRQs (e.g. with interrupts enabled, do some "NOP" instructions). That way there can't be any pending IRQs when you "sti" later (and you can unmask individual IRQ sources when you actually want them unmasked - e.g. when you install a device driver that uses a specific IRQ). Sadly most people (tutorials, GRUB, etc) do everything wrong and just "cli" without masking IRQs in the interrupt controller/s (and then do things like remap the PIC chips, etc; which makes things even more confusing), and then end up having to cope with the consequences of doing everything wrong. ;-)

Is os kernel event-based? Does the kernel multithreaded or multiprocess?

I have read some books about os kernel recently. I knew that when an event (like clock ticks) happens, it will trigger an interruption then the kernel's specified routine response.
So my questions are:
1)When an interruption was triggered and its corresponding kernel routine was still running, then another interruption was triggered for some sort of reason. How will the kernel response? Will it mask the second interruption when it was handling the first interruption? Or the first interruption's corresponding routine was interrupted by the second one? If the second condition was true, how the kernel make sure the routines are "reentrance"?
2)Does the kernel multithreaded or multiprocess? I mean when things go like the first question, the kernel will use CPU's extra cores to handle interruptions? If it did, how can the kernel make sure everything works correctly just like running on a single-core CPU?
1) If an interruption is triggered and its corresponding kernel routine is still running, then another interruption is triggered for some sort of reason; how will the kernel respond? Will it mask the second interruption when it was handling the first interruption? Or the first interruption's corresponding routine was interrupted by the second one?
Yes; different operating systems may either:
mask other IRQs while an IRQ is being handled
allow different IRQs to nest (interrupt each other)
allow all IRQs to nest (including the same IRQ interrupting itself)
mask some IRQs and allow other IRQs to nest
not use more than one IRQs (e.g. only use a timer IRQ, and poll everything else)
If the second condition was true, how does the kernel make sure the routines are "reentrant"?
If the OS designer decided that (some or all) IRQs may interrupt others; then they'll need to figure out how reentrancy will work for whatever cases they allowed. This can be "do nothing that causes a problem" (e.g. maybe IRQ handler just sends a notification to a task that does the real work later), and could be further restrictions (e.g. temporarily acquire a lock that prevents further IRQs for pieces of the IRQ handler that might cause a reentrancy problem but not other pieces that don't).
2) Does the kernel multithreaded or multiprocess? I mean when things go like the first question, the kernel will use CPU's extra cores to handle interruptions?
Yes; different operating systems may either use multi-threading or multi-processing (or both or neither); and may or may not use other cores to handle interrupts.
If it did, how can the kernel make sure everything works correctly just like running on a single-core CPU?
If a kernel does use other cores to handle interrupts; it will also do something to ensure everything works correctly. "Something" could be a system of locks, or transaction memory, or lock-free/block-free algorithms, or a "shared nothing" approach, or a combination of these things.

How does the computer implement callbacks?

I already know the general usage of callback. First,I register a "callback function",when some event occur,this function will be triggered(be executed).
What confuses me is how do I know if the event is occur? The solution I can get is polling.Is there a better way to check whether the event occur in less than the O(n) time ?
All right,Maybe the above question is too abstract.A more realistic description is does epoll_wait avoid using O(n) time to check whether the ready file descriptor?
If so, how did it do it?
Is there a callback mechanism that is different from polling essentially?
Usually, but not exclusively, callbacks get called after some peripheral I/O device signals an operation completion by raising a hardware interrupt. A long chain of stuff involving things like driver interrupt handlers, semaphores, protection ring changes, thread and process context changes, message assembly/enqueueing/requiring/handling/dispatching etc etc then cause your callback to be called, maybe by some system thread, or from a message-handling or signal-handling thread of your own that has to conform to a specific structure or constraint.
So no, polling is generally unnecessary, and unwanted.

Interrupts disabled during Interrupt handling

Why are interrupts disabled when the kernel is currently handling an interrupt ?
What if an interrupt carrying an important message is missed ?
This prevents "stacked interrupts" that can overflow the kernel stack. It also can also prevent deadlocks and/or "pinning".
Most hardware doesn't "lose" an interrupt. During an interrupt, the CPU's "interrupt flag" is cleared, but the interrupt controller [a separate beast] is still available/enabled to note new interrupts. If the CPU is processing an interrupt for hardware_A (in "interrupt service routine" ISR_A), the interrupt for hardware_B can still be asserted. It will be remembered [by the interrupt controller], it just won't interrupt the CPU at that time. When ISR_A returns, the interrupt flag is reenableed on exit, and now, immediately, ISR_B will be entered (and its call stack frame will start at the same exact memory location as for ISR_A).
While interrupts won't be missed/dropped, ISRs should be short [execute quickly] to minimize latency. In other words, ISR_A should not take so long that hardware_B will overflow some internal state/buffer [as it continues to accumulate data while waiting for ISR service].
Minimizing latency is a part of careful kernel design and ISR design. In Linux, ISRs can be broken down into the ISR part and a "bottom half" or "tasklet" part. The ISR [with interrupts disabled] does the minimum needed to service/quiesce the device (e.g. clear a bit in the device to prevent it from reasserting the interrupt immediately).
It then enables its corresponding tasklet [which runs with interrupts enabled] to do the more laborous operations that may take longer. Tasklets, despite the name, aren't like full blown tasks/processes that show up in "ps". They're a [very] lightweight/efficient way of splitting up the work the ISR must do, to minimize latency.
Let's take each question.
Why are interrupts disabled when the kernel is currently handling an interrupt ?
Though there are many types of interrupts, such as I/Os, timers, watchdogs, serial ports,peripherals and DMA, let's take example of I/Os. We'll talk a raw case and extend it into kernel.
Imagine a fire-alarm/sensor bit 0/1 connected to your CPU's particular interrupt pin. 0 is normal state and 1 is fire! Then one would configure that input's interrupt as "level triggered". The moment the sensor fires 1, the ISR has to execute a relevant code to siren or automatically dial fire department. An interrupt should be generally cleared the moment you enter ISR. If it's not cleared, the hardware keeps interrupting the CPU and the safety code inside ISR will never execute.
Also the CPU needs to maintain a stack of its current execution state. A recurring interrupt makes it a complex situation.
Second example with a "edge triggered" or "transition triggered" Imagine a series of bits are coming on an input line/pin (NRZ coded). If the ISR's job is assembling those bits into words (8,16,32 whatever length), we need to clear the interrupt, assemble the bit into buffer, and enable the interrupt again, in cycles. Not clearing the interrupt would cause a glitch-y transition to mistake just 1 bit as 2 bits.
So the practice is setup and enable the interrupts, if interrupted, clear it, execute the ISR code and enable the interrupts back wherever relevant.
Kernel
Kernel itself is a critical piece of code and scheduler (also OS timer, OS Clock) depend on a hardware timers interrupt. The hardware part of the CPU that contains interrupt logic maintains state-transition. It also has hardwired logic for enabling, disabling, masking and setting interrupt priorities.
If a kernel module or diver module should execute code safely, a deterministic behavior can only be obtained by disabling the interrupt before executing the handler.
What if an interrupt carrying an important message is missed ?
The interrupt handling shouldn't be too long (before enabling back the interrupts). One should design code properly depending on the frequency of interrupts and complexity of handler.

Interrupt masking: why?

I was reading up on interrupts. It is possible to suspend non-critical interrupts via a special interrupt mask. This is called interrupt masking. What i dont know is when/why you might want to or need to temporarily suspend interrupts? Possibly Semaphores, or programming in a multi-processor environment?
The OS does that when it prepares to run its own "let's orchestrate the world" code.
For example, at some point the OS thread scheduler has control. It prepares the processor registers and everything else that needs to be done before it lets a thread run so that the environment for that process and thread is set up. Then, before letting that thread run, it sets a timer interrupt to be raised after the time it intends to let the thread have on the CPU elapses.
After that time period (quantum) has elapsed, the interrupt is raised and the OS scheduler takes control again. It has to figure out what needs to be done next. To do that, it needs to save the state of the CPU registers so that it knows how to undo the side effects of the code it executes. If another interrupt is raised for any reason (e.g. some async I/O completes) while state is being saved, this would leave the OS in a situation where its world is not in a valid state (in effect, saving the state needs to be an atomic operation).
To avoid being caught in that situation, the OS kernel therefore disables interrupts while any such operations that need to be atomic are performed. After it has done whatever needs doing and the system is in a known state again, it reenables interrupts.
I used to program on an ARM board that had about 10 interrupts that could occur. Each particular program that I wrote was never interested in more than 4 of them. For instance there were 2 timers on the board, but my programs only used 1. I would mask the 2nd timer's interrupt. If I didn't mask that timer, it might have been enabled and continued making interrupts which would slow down my code.
Another example was that I would use the UART receive REGISTER full interrupt and so would never need the UART receive BUFFER full interrupt to occur.
I hope this gives you some insight as to why you might want to disable interrupts.
In addition to answers already given, there's an element of priority to it. There are some interrupts you need or want to be able to respond to as quickly as possible and others you'd like to know about but only when you're not so busy. The most obvious example might be refilling the write buffer on a DVD writer (where, if you don't do so in time, some hardware will simply write the DVD incorrectly) versus processing a new packet from the network. You'd disable the interrupt for the latter upon receiving the interrupt for the former, and keep it disabled for the duration of filling the buffer.
In practise, quite a lot of CPUs have interrupt priority built directly into the hardware. When an interrupt occurs, the disabled flags are set for lesser interrupts and, often, that interrupt at the same time as reading the interrupt vector and jumping to the relevant address. Dictating that receipt of an interrupt also implicitly masks that interrupt until the end of the interrupt handler has the nice side effect of loosening restrictions on interrupting hardware. E.g. you can simply say that signal high triggers the interrupt and leave the external hardware to decide how long it wants to hold the line high for without worrying about inadvertently triggering multiple interrupts.
In many antiquated systems (including the z80 and 6502) there tends to be only two levels of interrupt — maskable and non-maskable, which I think is where the language of enabling or disabling interrupts comes from. But even as far back as the original 68000 you've got eight levels of interrupt and a current priority level in the CPU that dictates which levels of incoming interrupt will actually be allowed to take effect.
Imagine your CPU is in "int3" handler now and at that time "int2" happens and the newly happened "int2" has a lower priority compared with "int3". How would we handle with this situation?
A way is when handling "int3", we are masking out other lower priority interrupters. That is we see the "int2" is signaling to CPU but the CPU would not be interrupted by it. After we finishing handling the "int3", we make a return from "int3" and unmasking the lower priority interrupters.
The place we returned to can be:
Another process(in a preemptive system)
The process that was interrupted by "int3"(in a non-preemptive system or preemptive system)
An int handler that is interrupted by "int3", say int1's handler.
In cases 1 and 2, because we unmasked the lower priority interrupters and "int2" is still signaling the CPU: "hi, there is a something for you to handle immediately", then the CPU would be interrupted again, when it is executing instructions from a process, to handle "int2"
In case 3, if the priority of “int2” is higher than "int1", then the CPU would be interrupted again, when it is executing instructions from "int1"'s handler, to handle "int2".
Otherwise, "int1"'s handler is executed without interrupting (because we are also masking out the interrupters with priority lower then "int1" ) and the CPU would return to a process after handling the “int1” and unmask. At that time "int2" would be handled.