There is nothing mystical about the [tt]OUT[/tt] command, and it is not at all related to the interrupt system in the way you describe. There are many things that can be done with [tt]OUT[/tt] that cannot be done with interrupt calls.
Here's how it works: Inside your computer, there is this thing called a "bus", which consists of a bunch of parallel lines that are hooked up to all devices of a particular type (for instance, all PCI devices are connected to one so-called "PCI bus"

. When you perform an OUT statement, the processor (which is also connected to the bus) announces that it is about to send data. This makes all of the devices on the bus wait and listen. Then, it sends the port number you specify down the "address lines" of the bus, and the data that you specify down the "data lines". Usually, exactly one device on the bus will recognize the port number that is on the address lines and will process the data. Sometimes, a system is misconfigured so that two devices are listening to the same port number. When this happens, communicating with one device interferes with the other one simultaneously. If the devices do not work the same way, this can cause highly undesirable things to happen. Thus, it is a good idea to ensure that no devices share the same port number(s). Plug and play does this automatically, but on older systems, you had to move jumpers to configure the cards, and it was easy to make mistakes that caused the system to malfunction.
As for reading from the bus (the [tt]INP()[/tt] function in QB), the processor announces that it is about to read, and then sends the port number down the address lines. The PCI bus standard specifies that a device responding to this must respond within a certain amount of time, so if any device is going to respond, it does so and puts the information on the data lines. After the time elapses, the processor reads the data lines, which presumably contain the data from that port.
Also note that the processor is not the only device which can initiate communication between devices. In fact, it is possible for two devices on the bus to talk to each other without interfering with the system's operation, though of this advantage is not often taken (wow, that was an awkward gramatical construct ^_^).
As a sidenote, I'll explain what interrupts are all about. Sometimes, a device has information that it wants the system to read, but the system is not aware of this. To take care of this problem, the processor has 16 so-called "interrupt request lines", also known as IRQs, which are wired to lines on the bus. In memory, the first kilobyte of memory is used to store memory addresses of so-called "interrupt handlers". Of the 16 hardware interrupts that are on the bus, only 8 were originally planned for, and the remaining 8 were added on in a rather haphazard fashion which involves interrupt 2 being invoked as a proxy to the upper 8. These lower 8 interrupts that were in the older specification correspond to interrupts 0x8 through 0xF in the processor's array of memory addresses.
When an interrupt line goes high, the processor saves information about what it is currently doing, and then branches off to the routine at the corresponding memory address. If IRQ 0 is invoked, then interrupt 8 is called on the processor. Once the interrupt processing is done, the old processor state is restored, and it resumes processing your code.
There was recently some discussion about accessing the keyboard using port I/O. This is how keyboard I/O really works at a low level: When you press a key, the keyboard controller on the motherboard records the scan code in a register, and then raises IRQ 1, which causes interrupt 9 to be called. Interrupt 9 reads the value from port &H60, which, as I just described, causes the processor to say "I will shortly read port &H60 from the bus", which in turn causes the keyboard controller to recognize that port number and set the data lines equal to the contents of the scan code register. When that short period of time allowed for devices to set the data lines elapses, the processor reads the data lines which contain the scan code from the keyboard, as well as an "activation bit" (which allows the software to determine whether the key was just pressed or just released). This value is returned to the interrupt handler which is still executing, and it processes the key information and adds it to [tt]INKEY$[/tt]'s queue and such. When it is done, it returns to your code, restoring the processor state, and your code has no clue that anything ever happened. The next time you access [tt]INKEY$[/tt], though, it contains the key that was read in from the keyboard.