The hardware knows which ISR to call for each interrupt, because the interrupts are numbered, and there's always an interrupt vector, which is basically an array of pointers to ISRs, one for each numbered interrupt. The interrupt vector is at a known place in memory, so the processor just looks up the interrupt number in the array and calls the routine.
This is totally hardware specific: processor specific and often platform
specific, but typically contains entries for hardware devices, a system
clock tick and error conditions, like illegal instructions. If the hardware
supports memory management, there will be interrupts which fire when a
process attempts to illegally access memory it does not own.
We talked about ISRs in the context of task switching; the interrupt handler must save the machine state, do some processing, then call the process scheduler and dispatcher.
When an interrupt occurs
An important goal of the OS is to hide interrupts from the user---and from user-level processes.
(Diagram above shows interrupt vector for an imaginary computer. It is near the bottom of memory, which is typical.)
Having said that interrupts should be hidden, UNIX provides signals for user processes, which are rather like interrupts, but higher level. Instead of being associated with hardware devices, signals are fired to processes on events like the user pressing Ctrl-C, or on a timer alarm previously set by a process, or a signal sent by another process. A process may register a kind of 'software ISR' with the operating system by calling the 'signal' system call, passing it a pointer to the routine to be called when the signal is fired. The routine, called a (signal) handler, is called just like a normal procedure or function, so a process dealing with signals has a much easier time of it than does the OS dealing with interrupts.