1/2. Yes there are. Network devices, file systems... most of the things that could be abstracted are done. However, take into account that every device has its special quirks and configurations. Only very generic devices can have generic drivers. But once you get to specific devices, you need specific drivers that can deal with special features and non-standard communications. See for example the number of vendors of ethernet drivers (https://github.com/torvalds/linux/tree/master/drivers/net/et...). And that is just a small part of the code.
3. Interesting? A lot. You get to learn how do low-level parts of your system work, deal with complex structures and interactions between kernel/hardware/userspace and.
But the most interesting thing is the programming ability you need. You have to really understand the code, be able mentally follow the execution paths (which are not simple, believe me) and write as few bugs as possible. Why? Because you don't have many tools. No debuggers, no unit tests... My main debugging tool is a set of debug macros that print to the kernel log. Debugging is a real pain in the ass and crashes can lead to a system restart, so I have to try to get it right as soon as possible.
3. Interesting? A lot. You get to learn how do low-level parts of your system work, deal with complex structures and interactions between kernel/hardware/userspace and.
But the most interesting thing is the programming ability you need. You have to really understand the code, be able mentally follow the execution paths (which are not simple, believe me) and write as few bugs as possible. Why? Because you don't have many tools. No debuggers, no unit tests... My main debugging tool is a set of debug macros that print to the kernel log. Debugging is a real pain in the ass and crashes can lead to a system restart, so I have to try to get it right as soon as possible.
I hope that also answers the "fun" part ;)