The device files interface with the kernel through the use of major and minor device numbers. The kernel first looks up the major number to discover which device or device type is being refered to, and the minor numbers determine a sub-selection within that category. For an example, lets look at the serial ports:

legba:/proc# ls -l /dev/ttyS*
crw-r-----    1 root     dialout    4,  64 Dec 26 11:46 /dev/ttyS0
crw-rw----    1 root     dialout    4,  65 Jul  6 03:44 /dev/ttyS1
crw-rw----    1 root     dialout    4,  66 Jul  6 03:44 /dev/ttyS2
crw-rw----    1 root     dialout    4,  67 Jul  6 03:44 /dev/ttyS3
                                    ^   ^                    ^
                                    |   |                    |
Major number -----------------------+   |                    |
                                        |                    |
Minor Number ---------------------------+                    |
                                                             |
Device Name -------------------------------------------------+
(Note that ttyS0 == COM1, ttyS1 == COM2 etc)

Here we can see that all the serial ports have a major number of 4, which indicates a terminal of some type, and minor numbers of 64 to 67, which determine which com port you are trying to access. So a program sending data to a modem connected to the first serial port would write the data to /dev/ttyS0, this would cause the kernel to intercept the data, see it was addressed to 4,64, find the module that controls the serial port, and push the data to the module's input subroutine. The module will then interface with the hardware, and give the serial port controller the data to output over the serial port.