Амм, не, някои неща могат да се правят в юзърспейс, например файлови системи, защото е възможно и има интерфейс us<->ks, който позволява такива неща.
Но като цяло, не. От една страна е проблем архитектурата - има входно-изходни операции, които не могат да се изпълняват извън ring0, демек кърнълспейс. Друг проблем е paging-a. Значи, много драйвери (например за уебкамери, мрежови карти и т.н), мап-ват паметта на PCI устройството в определен регион от паметта. Лошото е че един потребителски процес не "вижда" паметта линейно, заради пейджинг-а. Тоест, физическият адрес 0х100000 (примерно) може да се мап-не от VM-a и за процеса "bla" да бъде 0х500000. Това става с едни таблици за преобразуване и е доста сложна работа, трошаща при това и процесорни цикли. Алтернативно, това, което процесът вижда на адрес 0х2000000, реално може да се намира на физически адрес 0х7361263.
Значи да речем, паметта на устройството (нека например това е RX ring buffer на една мрежова карта), по някакъв начин се мап-ва в виртуалната памет, за да може процесът-драйвер да я "вижда". Ами ако случайно този регион влезе в swap-a? Значи тогава за всеки получен пакет ще трябва това да се изчита от диска (например, съзнавам, че е подвеждащ и тъп пример).
Отделно, примерно реализираш (отново) мрежов драйвер. Принципно, когато пристигне някакъв фрейм, хардуер-а вдига един interrupt (IRQ). Обработката на тези прекъсвания става само в кърнълспейс, да речем че там по някакъв начин се направи така, че да се прескача на адрес, мап-нат към някакъв процес. Значи правиш си interrupt handler в userspace. Това е възможно най-лошата идея, поради няколко причини:
1) IRQ handler-a трябва да завърши максимално бързо, защото такива прекъсвания се генерират прекалено често. Значи, ако е в userspace, имаш един "бавен" context switch.
2) Докато се изпълнява това, трябва да се забранят последователни прекъсвания, докато handler-a си свърши работата, иначе ще настанат concurrency проблеми. Това става с sti()/cti(), а те могат да се изпълняват само в ring0.
3) никой няма да ти гарантира, че процесът-драйвер няма да крашне или да бъде убит. При това положение, при вдигане на IRQ-то, процесорът ще джъмп-не на адрес, на който може да има всякакви глупости и машината като цяло да краш-не. Разбира се, има операционни системи, където всички драйвери са в юзърспейс, но там нещата са решени по съвсем различен начин.
И накрая, драйверите и разните подсистеми от ядрото си комуникират. Значи например, драйвера за мрежовата карта изчита един фрейм, правят се там разни фрагментации и т.н, полученият skb отива към netfilter подсистемата, там ако нещо трябва да се нат-ва, дроп-ва и т.н., после ако е част от някакъв сокет отива към тази част от ядрото, която се грижи за state-a на сокета (ако е TCP конекция примерно може да се налага да се пращат потвърждения и т.н.) и чак накрая това нещо се предава към юзърспейс-кото мрежово приложение.
Сега значи имаш мрежов драйвер в юзърспейс и netfilter в кърнълспейс. Понеже както казах, адресното пространство на процеса няма общо с физическата памет, както я вижда ядрото, ще трябва да има голям брой прехвърляния на памет от/до юзърспейс-а чрез copy_to/from_user() - а това троши доста процесорно време.
И като заключение, не мисля, че скоро в линукс масово ще имаме юзърспейс драйвери. А и не вярвам, че има смисъл
'>