linux內核介面
⑴ linux內核調用哪些介面分配內核地址空間的內存
kmalloc/kfree
類似於標准C中的malloc/free,kmalloc/kfree是內核中的用於常規內存分配的介面。
例如,申請1024位元組的內核地址空間:
char*buff;
buff=kmalloc(1024,GFP_KERNEL);
if(!buff)
return-ENOMEM;
還有什麼問題的話,可以私信我哦
⑵ Linux 內核驅動介面詳解
寫作本文檔的目的,是為了解釋為什麼Linux既沒有二進制內核介面,也沒有穩定 的內核介面。這里所說的內核介面,是指內核里的介面,而不是內核和用戶空間 的介面。內核到用戶空間的介面,是提供給應用程序使用的系統調用,系統調用 在 歷史 上幾乎沒有過變化,將來也不會有變化。我有一些老應用程序是在0.9版本 或者更早版本的內核上編譯的,在使用2.6版本內核的Linux發布上依然用得很好 。用戶和應用程序作者可以將這個介面看成是穩定的。
你也許以為自己想要穩定的內核介面,但是你不清楚你要的實際上不是它。你需 要的其實是穩定的驅動程序,而你只有將驅動程序放到公版內核的源代碼樹里, 才有可能達到這個目的。而且這樣做還有很多其它好處,正是因為這些好處使得 Linux能成為強壯,穩定,成熟的操作系統,這也是你最開始選擇Linux的原因。
只有那些寫驅動程序的「怪人」才會擔心內核介面的改變,對廣大用戶來說,既 看不到內核介面,也不需要去關心它。
既然只談技術問題,我們就有了下面兩個主題:二進制內核介面和穩定的內核源 代碼介面。這兩個問題是互相關聯的,讓我們先解決掉二進制介面的問題。
假如我們有一個穩定的內核源代碼介面,那麼自然而然的,我們就擁有了穩定的 二進制介面,是這樣的嗎?錯。讓我們看看關於Linux內核的幾點事實:
對於一個特定的內核,滿足這些條件並不難,使用同一個C編譯器和同樣的內核配 置選項來編譯驅動程序模塊就可以了。這對於給一個特定Linux發布的特定版本提 供驅動程序,是完全可以滿足需求的。但是如果你要給不同發布的不同版本都發 布一個驅動程序,就需要在每個發布上用不同的內核設置參數都編譯一次內核, 這簡直跟噩夢一樣。而且還要注意到,每個Linux發布還提供不同的Linux內核, 這些內核都針對不同的硬體類型進行了優化(有很多種不同的處理器,還有不同 的內核設置選項)。所以每發布一次驅動程序,都需要提供很多不同版本的內核 模塊。
相信我,如果你真的要採取這種發布方式,一定會慢慢瘋掉,我很久以前就有過 深刻的教訓…
如果有人不將他的內核驅動程序,放入公版內核的源代碼樹,而又想讓驅動程序 一直保持在最新的內核中可用,那麼這個話題將會變得沒完沒了。 內核開發是持續而且快節奏的,從來都不會慢下來。內核開發人員在當前介面中 找到bug,或者找到更好的實現方式。一旦發現這些,他們就很快會去修改當前的 介面。修改介面意味著,函數名可能會改變,結構體可能被擴充或者刪減,函數 的參數也可能發生改變。一旦介面被修改,內核中使用這些介面的地方需要同時 修正,這樣才能保證所有的東西繼續工作。
舉一個例子,內核的USB驅動程序介面在USB子系統的整個生命周期中,至少經歷 了三次重寫。這些重寫解決以下問題:
這和一些封閉源代碼的操作系統形成鮮明的對比,在那些操作系統上,不得不額 外的維護舊的USB介面。這導致了一個可能性,新的開發者依然會不小心使用舊的 介面,以不恰當的方式編寫代碼,進而影響到操作系統的穩定性。 在上面的例子中,所有的開發者都同意這些重要的改動,在這樣的情況下修改代 價很低。如果Linux保持一個穩定的內核源代碼介面,那麼就得創建一個新的介面 ;舊的,有問題的介面必須一直維護,給Linux USB開發者帶來額外的工作。既然 所有的Linux USB驅動的作者都是利用自己的時間工作,那麼要求他們去做毫無意 義的免費額外工作,是不可能的。 安全問題對Linux來說十分重要。一個安全問題被發現,就會在短時間內得到修 正。在很多情況下,這將導致Linux內核中的一些介面被重寫,以從根本上避免安 全問題。一旦介面被重寫,所有使用這些介面的驅動程序,必須同時得到修正, 以確定安全問題已經得到修復並且不可能在未來還有同樣的安全問題。如果內核 內部介面不允許改變,那麼就不可能修復這樣的安全問題,也不可能確認這樣的 安全問題以後不會發生。 開發者一直在清理內核介面。如果一個介面沒有人在使用了,它就會被刪除。這 樣可以確保內核盡可能的小,而且所有潛在的介面都會得到盡可能完整的測試 (沒有人使用的介面是不可能得到良好的測試的)。
如果你寫了一個Linux內核驅動,但是它還不在Linux源代碼樹里,作為一個開發 者,你應該怎麼做?為每個發布的每個版本提供一個二進制驅動,那簡直是一個 噩夢,要跟上永遠處於變化之中的內核介面,也是一件辛苦活。 很簡單,讓你的驅動進入內核源代碼樹(要記得我們在談論的是以GPL許可發行 的驅動,如果你的代碼不符合GPL,那麼祝你好運,你只能自己解決這個問題了, 你這個吸血鬼把Andrew和Linus對吸血鬼的定義鏈接到這里>)。當你的代碼加入 公版內核源代碼樹之後,如果一個內核介面改變,你的驅動會直接被修改介面的 那個人修改。保證你的驅動永遠都可以編譯通過,並且一直工作,你幾乎不需要 做什麼事情。
把驅動放到內核源代碼樹里會有很多的好處:
⑶ Linux操作系統由什麼組成
Linux系統結構一般有3個主要部分:內核kernel、命令解釋層Shell或其他操作環境、實用工具
1.Linux內核
內核是系統的核心,是運行程序和管理磁碟、列印機等硬體設備的核心程序。操作系統向用戶提供一個操作界面,它從用戶那裡接收命令,並且把命令送給內核去執行。
當 Linux安裝完畢之後,一個通用的內核就被安裝到主機中,這個通用內核能滿足絕大部分用戶的需求,但普遍適用性內核對具體的某台主機來說,可能有一些並不需要的內核程序將被安裝。因此,Linux允許用戶根據主機的實際配置定製 Linux的內核,從而有效地簡化 Linux內核,提高系統啟動速度。
2.Linux Shell
Shell是系統的用戶界面,提供了用戶與內核進行交互操作的介面。它接收用戶輸入的命今,並且把它送入內核執行。操作系統在系統內核與用戶之間提供操作界面, Linux存在多種操作環境,分別是基於圖形界面的集成桌面環境和基於Shell命令行環境。
Shell是一個命令解釋器,它解釋由用戶輸入的命令,並且送到內核。Shell編程語言具有普通編程語言的很多特點,如它也有循環結構和分支控制結構等,用這種編程語言編寫的Shell程序與其他應用程序具有同樣的效果。
作為命令行操作界面的替代, Linux還提供了像 Windows那樣的可視化圖形界面X-window的圖形用戶界面。
3.實用工具
標準的 Linux系統都有配套的實用工具程序,如編輯器、瀏覽器、辦公套件及其它系統管理工具等,用戶可以自行編寫需要的應用程序。
⑷ Linux內核gpio
本文基於 RockPI 4A 單板 Linux 4.4 內核介紹 RK3399 Linux GPIO 功能。
GPIO(General Purpose Input/Output Port) :通用輸入輸出埠。
除作為一般的輸入/輸出功能外,還可以配置為中斷和模擬 PWM、I2C 等介面功能。
RK3399 GPIO 屬性如下:
1、一共有5組 GPIO(GPIO0~4) ,每組 GPIO 為一個 Bank ,共32個引腳。每個 Bank 包括4個 Group (GPIOA(0~7) ~ D(0~7)) 。不是所有 Bank 都有 GPIOA~D 的編號, RK3399 共122個 GPIO 引腳。
2、所有 GPIO 都可被配置為 CA55或CA53 的中斷功能,且 GPIO0 和 GPIO1 可用於系統低功耗喚醒模式。
3、在上電復位後,所有 GPIO 默認輸入狀態。
4、軟體可配置 GPIO 驅動強度。
RK3399 引腳在系統中顯示如下:
RK3399 GPIO 引腳號計算方式:
例:
以 ROCKPI 4A 單板 WIFI 模塊電源( GPIO0_B2 )為例,介紹 DTS 中 GPIO 配置。
在系統啟動後,可以查看 GPIO ,命令如下:
註:
如果debugfs沒有掛載,使用下面命令掛載
Linux 內核 GPIO 主要實現文件:
GPIO 子系統有兩套 API :
1、基於描述符(descriptor-based)
前綴為: gpiod_ 。
參考: Documentation/gpio/consumer.txt
2、老版本介面(legacy)
前綴為: gpio_ 。
參考: Documentation/gpio/gpio-legacy.txt
3、常用API
GPIO 還有很多介面,就不一一列舉了。
RK3399 GPIO 控制器驅動實現文件: drivers/pinctrl/pinctrl-rockchip.c ,涉及主要函數:
所有 GPIO 子系統的 API 最終都會調到 SOC 的 GPIO 控制器驅動函數。
ROCKPi 4A 單板有個40個引腳的擴展口,引用 radxa 圖片,見下圖。
1、進入測試目錄
2、導出GPIO
在使用 GPIO2_A7 前,需要導出該引腳。方法:配置 export 後,會出現 gpio71 節點。
測試時,注意不要使用在程序中已經申請過或配置為其它功能的 GPIO 引腳。
3、配置GPIO方向
設置 GPIO2_A7 的輸入/輸出方向。
in :表示輸入。
out :表示輸出。
active_low :用於中斷配置中高電平或低電平有效。
edge :用於中斷配置中上升沿或下降沿有效。
4、配置GPIO輸出值
在 GPIO 為輸入時,通過 value 查詢 GPIO 的輸入電平(高或低電平)。
在 GPIO 為輸出時,通過 value 配置 GPIO 的輸出電平(高或低電平)。
5、查看GPIO
查看已經導出的 GPIO71 。
6、取消導出
使用完 GPIO2_A7 後,需要進行釋放。方法:配置 unexport 後, gpio71 節點會消失。
⑸ linux kernel 文件系統編程介面
進程讀寫文件之前需要 打開文件 ,得到 文件描述符 ,然後 通過文件描述符讀寫文件 .
內核提供了兩個打開文件的系統調用 open 和 openat .
打開文件的主要步驟如下:
(1)需要 在父目錄的數據中查找文件對應的目錄項 , 從目錄項得到索引節點的編號,然後在內存中創建索引節點的副本 .因為各種文件系統類型的物理結構不同,所以需要提供索引節點操作集合的 lookup 方法和文件操作集合的 open 方法.
(2)需要分配文件的一個打開實例-- file 結構體,關聯到文件的索引節點.
(3)在進程的打開文件表中 分配一個文件描述符 , 把文件描述符和打開實例的映射添加到進程的打開文件表 中.
進程可通過使用系統調用 close 關閉文件.
系統調用close的執行流程如下:
(1)解除打開文件表和file實例的關聯.
(2)在close_on_exec點陣圖中清楚文件描述符對應的位.
(3)釋放文件描述符,在文件描述符點陣圖中清除文件描述符對應的位.
(4)調用函數fput釋放file實例:把引用計數減1,如果引用計數是0,那麼把file實例添加到鏈表delayed_fput_list中,然後調用延遲工作項delayed_fput_work.
延遲工作項delayed_fput_work的處理函數是flush_delayed_fput,遍歷鏈表delayed_fput_list,針對每個file實例,調用函數__fput來加以釋放.
創建不同類型的文件,需要使用不同的命令.
(1) 普通文件 :touch FILE ,這條命令本來用來更新文件的訪問時間和修改時間,如果文件不存在,創建文件.
(2) 目錄 :mkdir DIRECTORY .
(3) 符號鏈接(軟鏈接) :ln -s TARGET LINK_NAME 或ln --symbolic TARGET LINK_NAME .
(4) 字元或塊設備文件 :mknod NAME TYPE [MAJOR MINOR] .
(5) 命名管道 :mkpipe NAME .
(6) 硬連接 :命令"ln TARGETLINK_NAME ".給已經存在的文件增加新的名稱,文件的索引節點有一個硬鏈接計數,如果文件有n個名稱,那麼硬鏈接計數是n.
創建文件需要在文件系統中 分配一個索引節點 ,然後 在父目錄的數據中增加一個目錄項來保存文件的名稱和索引節點編號 .
刪除文件的命令如下:
(1)刪除任何類型文件:unlink FILE .
(2)rm FILE ,默認不刪除目錄,如果使用"-r""-R"或"-recursive",可以刪除目錄和目錄的內容.
(3)刪除目錄:rmdir DICTIONARY .
內核提供了unlink,unlinkat用來刪除文件的名稱,如果文件的硬鏈接計數變成0,並且沒有進程打開這個文件,那麼刪除文件.提供了rmdir刪除目錄.
刪除文件需要從父目錄的數據中刪除文件對應的目錄項, 把文件的索引節點的硬鏈接計數減1(一個文件可以有多個名稱,Linux把文件名稱稱為硬鏈接),如果索引節點的硬鏈接計數變成0,那麼釋放索引節點 .因為各種文件系統的物理結構不同,所以需要提供索引節點操作集合的 unlink 方法.
設置文件許可權的命令如下:
(1)chmod [OPTION]... MODE[, MODE]... FILE...
mode : 許可權設定字串,格式[ugoa...][[+-=][rwxX]...][,...]
其中:
(2)chmod [OPTION]... OCTAL-MODE FILE...
參數OCTAL-MODE是八進制數值.
系統調用chmod負責修改文件許可權.
修改文件許可權需要修改文件的索引節點的文件模式欄位,文件模式欄位包含文件類型和訪問許可權.因為各種文件系統類型的索引節點不同,所以需要提供索引節點操作集合的 setattr 方法.
訪問外部存儲設備的速度很慢,為了避免每次讀寫文件時訪問外部存儲設備, 文件系統模塊為每個文件在內存中創建一個緩存 ,因為 緩存的單位是頁 ,所以稱為 頁緩存 .
(1) 索引節點的成員i_mapping 指向地址空間結構體(address_space).進程在打開文件的時候, 文件打開實例(file結構體)的成員f_mapping 也會指向文件的地址空間.
(2)每個文件有一個地址空間結構體 address_space ,成員 page_tree 的類型是結構體radix_tree_root:成員 gfp_mask是分配內存頁的掩碼,成員rnode指向基數樹的根節點 .
(3)使用基數樹管理頁緩存,把文件的頁索引映射到內存頁的頁描述符.
每個文件都有一個地址空間結構體address_space,用來建立數據緩存(在內存中為某種數據創建的緩存)和數據來源(即存儲設備)之間的關聯.結構體address_space如下:
地址空間操作結合address_space_operations的主要成員如下:
頁緩存的常用操作函數如下:
(1)函數find_get_page根據文件的頁索引在頁緩存中查找內存頁.
(2)函數find_or_create_page根據文件的頁索引在頁緩存中查找內存頁,如果沒有找到內存頁,那麼分配一個內存頁,然後添加到頁緩存中.
(3)函數add_to_page_cache_lru把一個內存頁添加到頁緩存和LRU鏈表中.
(4)函數delete_from_page_cache從頁緩存中刪除一個內存頁.
進程讀文件的方式有3種:
(1)調用內核提供的 讀文件的系統調用 .
(2)調用glibc庫封裝的讀文件的 標准I/O流函數 .
(3)創建基於文件的內存映射,把 文件的一個區間映射到進程的虛擬地址空間,然後直接讀內存 .
第2種方式在用戶空間創建了緩沖區,能減少系統調用的次數,提高性能.第3種方式可以避免系統調用,性能最高.
讀文件的主要步驟如下:
(1)調用具體文件系統類型提供的文件操作集合的read和read_iter方法來讀文件.
(2) read或read_iter方法根據頁索引在文件的頁緩存中查找頁,如果沒有找到,那麼調用具體文件系統類型提供的地址空間集合的readpage方法來從存儲設備讀取文件頁到內存中 .
為了提高讀文件的速度,從存儲設備讀取文件頁到內存中的時候,除了讀取請求的文件頁,還會預讀後面的文件頁.如果進程按順序讀文件,預讀文件頁可以提高讀文件的速度;如果進程隨機讀文件,預讀文件頁對提高讀文件的速度幫助不大.
進程寫文件的方式有3種:
(1)調用內核提供的 寫文件的系統調用 .
(2)調用glibc庫封裝的寫文件的 標准I/O流函數 .
(3)創建基於文件的內存映射,把 文件的一個區間映射到進程的虛擬空間,然後直接寫內存 .
第2種方式在用戶空間創建了緩沖區,能夠減少系統調用的次數,提高性能.第3種方式可以避免系統調用,性能最高.
寫文件的主要步驟如下:
(1)調用具體文件系統類型提供的文件操作集合的write或write_iter方法來寫文件.
(2)write或write_iter方法調用文件的地址空間操作集合的 write_begin 方法, 在頁緩存查找頁,如果頁不存在就分配頁;然後把數據從用戶緩沖區復制到頁緩存的頁中 ;最後調用文件的地址空間操作集合的 write_end 方法.
進程寫文件時,內核的文件系統模塊把數據寫到文件的頁緩存,沒有立即寫回到存儲設備.文件系統模塊會定期把臟頁寫回到存儲設備,進程也可以調用系統調用把臟頁強制寫回到存儲設備.
管理員可以執行命令"sync",把內存中所有修改過的文件元數據和文件數據寫回到存儲設備.
內核提供了 sync , syncfs , fsync , fdatasync , sync_file_range 等系統調用用於文件寫回.
把文件寫回到存儲設備的時機如下:
(1)周期回寫.
(2)當臟頁的數量達到限制的時候,強制回寫.
(3)進程調用sync和syncfs等系統調用.
對於類似內存的塊設備,例如NVDIMM設備,不需要把文件從存儲設備復制到頁緩存.DAX繞過頁緩存,直接訪問存儲設備,對於基於文件的內存映射,直接把存儲設備映射到進程的虛擬地址空間.
調用系統調用mmap創建基於文件的內存映射,把文件的一個區間映射到進程的虛擬地址空間,這會調用具體文件系統類型提供的文件操作集合的mmap方法.mmap方法針對設置了標志位S_DAX的索引節點,處理方法如下:
(1)給虛擬內存區域設置標志位VM_MIXEDMAP和VM_HUGEPAGE.
(2)設置虛擬內存操作集合,提供fault,huge_fault,page_mkwrite和pfn_mkwrite方法.
⑹ Linux內核中斷之中斷申請介面
本文基於 RockPI 4A 單板Linux4.4內核介紹中斷申請的常用介面函數。
1、文件
2、定義
說明:
1)、 irq :要申請的中斷號,可通過 platform_get_irq() 獲取,見「Linux內核中斷之獲取中斷號」。
2)、 handler :中斷處理函數,發生中斷時,先處理中斷處理函數,然後返回 IRQ_WAKE_THREAD 喚醒中斷處理線程。中斷處理函數盡可能簡單。
中斷處理函數定義: typedef irqreturn_t (*irq_handler_t)(int, void *);
中斷返回值如下:
3)、 thread_fn :中斷處理線程,該參數可為NULL。類似於中斷處理函數的下半部分。
4)、 irqflags :中斷類型標志。
定義文件: include/linux/interrupt.h ,內容如下:
5)、 devname :中斷名稱,可使用 cat /proc/interrupts 命令查看。
6)、 dev_id :設備ID,該值唯一。
在使用共享中斷時(即設置 IRQF_SHARED ),必須傳入 dev_id ,在中斷處理和釋放函數中都會使用該參數。
註:
1、 request_threaded_irq() 函數可替代 request_irq 加 tasklet 或 workqueue 的方式。
2、對應的中斷釋放函數為: void free_irq(unsigned int, void *) ,需要和中斷申請函數成對出現。
1、文件
2、定義
說明:
1)、 __must_check :指調用函數一定要處理函數的返回值,否則編譯器會給出警告。
2)、 request_irq() 函數本質上是中斷處理線程 thread_fn 為空的 request_threaded_irq() 函數。
注 :
對應的中斷釋放函數為: void free_irq(unsigned int, void *) ,需要和中斷申請函數成對出現。
1、文件
2、定義
說明 :
devm_request_threaded_irq() 本質上還是使用 request_threaded_irq() 函數實現中斷申請。
兩者區別:
1)多了一個 dev 參數;
2)在設備驅動卸載時,中斷會自動釋放;
3)如果想單獨釋放中斷,可使用 void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id) 函數。
1、文件
2、定義
devm_request_irq() 函數本質上是中斷處理線程 thread_fn 為空的 devm_request_threaded_irq() 函數。
1、獲取中斷號
2、申請中斷
3、中斷處理函數
4、中斷處理線程
5、查看中斷