Bottom halfs are a feature of the Linux Kernel (and probably of other kernels too) that mark some code to be executed later, making the system more responsive.

Whenever a hardware device issues a Interrupt, the kernel checks if there is a interrupt handler installed for it, and then, if its the case, it will run it. However, whenever the kernel is running the handler, no other proccess can run. Besides, the interrupt handler is non-preemptible, which means that no other code can take it over.

And whats the problem with that? The problem is that, if the interrupt handler is too big, the kernel will have to wait until it stops. That's pretty bad. (Imagine if, everytime you moved your mouse, the kernel could calculate a new mersenne prime and not let you do anything)

So, to take care of these problems, we have bottom halfs. Whenever a Interrupt is issued, the kernel calls the interrupt handler, that does only the critical job (like telling the hardware that it has acknowledged the interrupt or something like that). Then, it schedules a bottom half to do the dirty job. Whenever the kernel has some time, it runs the bottom half code.

For example, in kernel 2.2.x, we could have a bottom half called:

static void got_char(void *scancode);

And our IRC handler would do:

/* Define the bottom half */
static struct tq_struct task = {NULL, 0, got_char, &scancode};
/* Queue it */
queue_task(&task, &tq_immediate);
/* Mark as a BH for immediate execution (ie, ASAP) */

In the 2.4.x kernel serie, bottom halfs were replaced by softirqs and tasklets.

Disclaimer: I'm still learning kernel programming... the above information can be wrong... if it is, please correct me!