http://www.cnblogs.com/bakari/p/5520860.html

內核從本質上看是一種軟件——控制計算機的硬件資源,並提供上層應用程序運行的環境。
系統調用是操作系統的最小功能單位,這些系統調用根據不同的應用場景可以進行擴展和裁剪,現在各種版本的Unix實現都提供了不同數量的系統調用,如Linux的不同版本提供了240-260個系統調用,FreeBSD大約提供了320個(reference:UNIX環境高級編程)。

Intel的X86架構的CPU提供了0到3四個特權級,數字越小,特權越高,Linux操作系統中主要采用了0和3兩個特權級,分別對應的就是內核態和用戶態。
一些操作需要在內核權限下才能執行,這就涉及到一個從用戶態切換到內核態的過程。比如C函數庫中的內存分配函數malloc(),它具體是使用sbrk()系統調用來分配內存,當malloc調用sbrk()的時候就涉及一次從用戶態到內核態的切換,類似的函數還有printf(),調用的是wirte()系統調用來輸出字符串,等等。
在什么情況下會發生從用戶態到內核態的切換,一般存在以下三種情況:
1)當然就是系統調用:原因如上的分析。
2)異常事件: 當CPU正在執行運行在用戶態的程序時,突然發生某些預先不可知的異常事件,這個時候就會觸發從當前用戶態執行的進程轉向內核態執行相關的異常事件,典型的如缺頁異常。
3)外圍設備的中斷:當外圍設備完成用戶的請求操作后,會像CPU發出中斷信號,此時,CPU就會暫停執行下一條即將要執行的指令,轉而去執行中斷信號對應的處理程序,如果先前執行的指令是在用戶態下,則自然就發生從用戶態到內核態的轉換。
注意:系統調用的本質其實也是中斷,相對於外圍設備的硬中斷,這種中斷稱為軟中斷,這是操作系統為用戶特別開放的一種中斷,如Linux int 80h中斷。所以,從觸發方式和效果上來看,這三種切換方式是完全一樣的,都相當於是執行了一個中斷響應的過程。但是從觸發的對象來看,系統調用是進程主動請求切換的,而異常和硬中斷則是被動的。
(注:補充一下:Linux進程空間,也分成內核空間和用戶空間,執行內核空間中的內容要在內核態)
內核與用戶態的通信
http://www.cnblogs.com/gaiwen/articles/3002774.html
在一台運行 Linux 的計算機中,CPU 在任何時候只會有如下四種狀態:
【1】 在處理一個硬中斷。
【2】 在處理一個軟中斷,如 softirq、tasklet 和 bh。(softirq、tasklet和workqueue,貌似都是bh, bottom-half的機制,tasklet可以說是一種擴展,支持更多的軟中斷)
【3】 運行於內核態,但有進程上下文,即與一個進程相關。
【4】 運行一個用戶態進程。
Unix的兩大分支AT&T Unix和BSD Unix在進程通信實現機制上的各有所不同,前者形成了運行在單個計算機上的System V IPC,后者則實現了基於socket的進程間通信機制。
同時Linux也遵循IEEE制定的Posix IPC標准,在三者的基礎之上實現了以下幾種主要的IPC機制:管道(Pipe)及命名管道(Named Pipe),信號(Signal),消息隊列(Message queue),共享內存(Shared Memory),信號量(Semaphore),套接字(Socket)。
為了完成內核空間與用戶空間通信,Linux提供了基於socket的Netlink通信機制,可以實現內核與用戶空間數據的及時交換。
到目前Linux提供了9種機制完成內核與用戶空間的數據交換,分別是內核啟動參數、模塊參數與 sysfs、sysctl、系統調用、netlink、procfs、seq_file、debugfs和relayfs,
其中模塊參數與sysfs、procfs、debugfs、relayfs是基於文件系統的通信機制,用於內核空間向用戶控件輸出信息;
sysctl、系統調用是由用戶空間發起的通信機制。由此可見,以上均為單工通信機制,在內核空間與用戶空間的雙向互動數據交換上略顯不足。
Netlink是基於socket的通信機制,由於socket本身的雙共性、突發性、不阻塞特點,因此能夠很好的滿足內核與用戶空間小量數據的及時交互,因此在Linux 2.6內核中廣泛使用,例如SELinux,Linux系統的防火牆分為內核態的netfilter和用戶態的iptables,netfilter與iptables的數據交換就是通過Netlink機制完成。
2 各種通信方式
本節說明各種通信方式是否適合內核空間和用戶空間信息交互,以及如何使用。
2.1 信號
在進程中使用函數signal()或sigaction()安裝信號時指定了關聯的函數。在內核空間相進程發送信號,從內核空間返回進程空間時檢查並執行相應的關聯函數。
在進程中可以使用pause()函數進入睡眠,在有信號產生並執行了相應的關聯函數后進程被喚醒,繼續執行。可以使用這種方式實現內核空間和用戶空間的同步。
pause()會使當前進程掛起,直到捕捉到一個信號,對指定為忽略的信號,pause()不會返回。只有執行了一個信號處理函數,並從其返回,pause()才返回-1,並將errno設為EINTR。
2.2 信號量
雖然原理一樣,但內核空間和用戶空間的信號量是完全兩套系統,所以信號量不能用於內核空間和用戶空間信息交互。
2.3 無名管道
無名管道只適用於有關系的進程之間通信。不能用於內核空間和用戶空間信息交互。
2.4 get_user()/put_user()
2.5 copy_from_user()/copy_to_user()
都可以,略
2.6 共享內存(mmap)
使用mmap()函數通常映射一個普通文件實現進程之間內存共享,即多個進程打開同一個文件,將文件映射到各自進程的虛擬空間。這樣各個進程就可以通過共享的內存進行大量的數據交互,當然需要我們自己設計互斥功能。
還可以使用mmap()函數實現內核空間和用戶空間內存共享的功能。網上提到的方法基本都是proc文件+mmap。
大體過程如下
1、在模塊中申請一些內存頁面,作為共享的內存空間。
2、創建可讀的proc文件,在其讀函數中把上面申請的內存空間的物理地址返回給進程空間。
3、在進程空間open /dev/mem文件,並把從proc讀取的物理地址(要共享的內存的物理地址)作為文件/dev/mem的offset,以此offset 把/dev/mem文件的若干空間用mmap映射到進程空間。
注意:
1、/dev/mem 不是一個普通的文件里面的內容是所有物理內存的內容信息。所以,在上面的過程中把共享空間的物理地址作為offset使用。
2、proc文件的作用就是提供一個讀取的函數,把共享內存的地址從內核空間傳遞到用戶空間。也可以用設備的ioctl 把該物理地址數值傳給用戶空間。
經典的內核與用戶空間的通信(使用read和write系統調用)
1. 用戶空間需要將某些數據傳遞給內核,並指定數據的處理函數。
2. 用戶空間需要從內核讀取數據,並指定數據的讀取函數。
后面比較難懂了,略。
