Linus likes his C code groomed a particular way, but he doesn't mind if you omit the documentation for that subsystem rewrite. That's why they call it kernel hacking.
There are books, such as The Design and Implementation of the FreeBSD Operating System (by McKusick): http://www.amazon.com/Design-Implementation-FreeBSD-Operatin...
There are papers, such as Jonathan Lemon's "Kqueue: A generic and scalable event notification facility" presented at Usenix 2001: http://people.freebsd.org/~jlemon/papers/kqueue.pdf
There are kernel interface man pages:
There are examples referenced by the man pages:
NetBSD is pretty approachable too. With the rump kernel you can run drivers in userspace, so you can use a normal debugger and so on, and not crash the OS you are running.
I actually started to look into FreeBSD kernel code recently. I've found that FreeBSD's ath driver code is cleaner and more straightforward compared to the ath/ath*k drivers in Linux. Not sure if this is coincidence or due to different philosophy between FreeBSD and Linux communities.
I'm not usually one to trot out that line, but I've found it is the simplest way to figure out what's going on. Most subsystems are pretty straight forward. The only really hairy place is the scheduler. That's after mucking with most of the device model, ARM specific bits, and networking stack.
Although I'm not sure about how they manage the coding of their kernels.