1. Signals are bad. All the bad bits of IRQs, and you're not even working in assembly language, so they're twice as hard. Just say no.
2. The forking model is seductive, but wrong-headed. It's hard to make the file descriptor inheritance behave correctly, and it means the memory requirements are unpredictable (due to the copy-on-write pages)
3. Readiness I/O is not really the right way to do things, because there are obvious race conditions, and the OS can't guarantee one thread woken per pending operation (because it has no idea which threads will do what). Also, the process owns the buffer, which really limits what the OS can do... it needs to be able to own the buffer for the duration of the entire operation for best results, so it can fill it on any ready thread, map the buffer into the driver's address space, etc.
4. Poor multithreading primitives. This really annoyed me... like, you've got a bunch of pthreads stuff, but it doesn't interact with select & co. The Linux people aren't dumb, so they give you eventfd - but there's no promise of single wakeup! NT wakes up one thread per increment, and the wakeup atomically decrements the semaphore; Linux wakes up every thread waiting on the semaphore, and they all fight over it, because there's no other option.
(I'm just ignoring POSIX semaphores entirely, because they don't let you wait on them with select/poll/etc. in the first place.)
(Perhaps this and #3 ought to be the same item, because they are related. And the end result is that you need to use non-blocking IO for everything... but the non-blocking IO is crippled, because it still has to copy out into the caller's buffer. It's just a more inconvenient programming model, for no real benefit.)
I guess it just boils down to what you want: a beautifully polished turd, or a carefully engineered system assembled from turds.