最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網 會員登陸 & 注冊

Linux驅動程序教程:如何編寫Linux設備驅動程序(趕快學習起來~)

2022-06-08 21:30 作者:補給站Linux內核  | 我要投稿
  • 我們將使用Linux內核版本2.6.32。 我們可以使用更新的版本,但是它們的API可能已被修改,因此可能與我們的示例和構建系統(tǒng)中使用的API不同。 學習本教程后,您將熟悉為Linux操作系統(tǒng)編寫設備驅動程序或內核模塊的過程。

概述

  • Linux有一個單片內核。 因此,為Linux編寫設備驅動程序需要與內核進行組合編譯。 另一種方法是將驅動程序實現(xiàn)為內核模塊,在這種情況下,您無需重新編譯內核即可添加其他驅動程序。 我們將關注第二個選項:內核模塊。

  • 在其基礎上,模塊是專門設計的目標文件。 使用模塊時,Linux會將它們加載到其內核空間,從而將它們鏈接到內核。 Linux內核是使用C編程語言和Assembler開發(fā)的。 C實現(xiàn)了內核的主要部分,而Assembler實現(xiàn)了依賴于體系結構的部分。 不幸的是,這些是我們用于編寫Linux設備驅動程序的唯一兩種語言。 我們不能使用用于Microsoft Windows操作系統(tǒng)內核的C ++,因為Linux內核源代碼的某些部分 - 特定的頭文件 - 可能包含來自C ++的關鍵字(例如, delete或new ),而在Assembler中我們可能會遇到諸如’ : : ‘詞匯。

  • 我們在內核上下文中運行模塊代碼。 這需要開發(fā)人員非常專注,因為它需要承擔額外的責任:如果開發(fā)人員在實現(xiàn)用戶級應用程序時出錯,在大多數(shù)情況下這不會導致用戶應用程序之外的問題; 但是如果開發(fā)人員在實現(xiàn)內核模塊時出錯,后果將是系統(tǒng)級別的問題。 幸運的是,Linux內核有一個很好的功能,可以抵御模塊代碼中的錯誤。 當內核遇到非嚴重錯誤(例如,空指針解除引用)時,您將看到oops消息(Linux操作期間無意義的故障稱為oops ),之后將卸載故障模塊,允許內核和其他模塊像往常一樣工作。 此外,您還可以在內核日志中找到準確描述此錯誤的記錄。 但請注意,不建議在oops消息之后繼續(xù)工作,因為這樣做可能會導致不穩(wěn)定和內核恐慌。

  • 內核及其模塊本質上代表一個程序模塊 - 因此請記住,單個程序模塊使用單個全局命名空間。 為了最小化它,您必須觀察模塊導出的內容:導出的全局字符必須唯一命名(常用的解決方法是簡單地使用將字符導出為前綴的模塊的名稱)并且必須剪切到最低限度。

裝載和卸載模塊

  • 要創(chuàng)建一個簡單的示例模塊,我們不需要做太多工作。 這里有一些代碼可以證明這一點:

這個模塊唯一做的兩件事就是加載和卸載自己。 要加載Linux驅動程序,我們調用my_init函數(shù),并卸載它,我們調用my_exit函數(shù)。 module_init和module_exit宏通知內核有關驅動程序的加載和卸載。 my_init和my_exit函數(shù)必須具有相同的簽名,這些簽名必須完全如下:

  • 如果模塊需要某個內核版本并且必須包含有關版本的信息,我們需要鏈接linux / module.h頭文件。 嘗試加載為另一個內核版本構建的模塊將導致Linux操作系統(tǒng)禁止其加載。 出現(xiàn)這種情況的原因是:內核API的更新經常被釋放,當您調用其簽名已更改的模塊函數(shù)時,會對整個堆棧造成損害。 module_init和module_exit宏在linux / init.h頭文件中聲明。

【文章福利】小編推薦自己的Linux內核技術交流群:【891587639】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦!??!前100名進群領取,額外贈送一份價值699的內核資料包(含視頻教程、電子書、實戰(zhàn)項目及代碼)? ?

注冊角色設備

  • 上面的示例模塊非常簡單; 現(xiàn)在我們將開始處理更復雜的事情。 然而,這個簡短的Linux內核驅動程序教程的目的之一是展示如何使用登錄內核以及如何與設備文件交互。 這些工具可能很簡單,但它們可以為任何驅動程序派上用場,并且在某種程度上,它們使內核模式開發(fā)過程更加豐富。

  • 首先,這里有一些有關設備文件的有用信息。 通常,您可以在/ dev文件夾中找到設備文件。 它們促進了用戶和內核代碼之間的交互。 如果內核必須接收任何內容,您只需將其寫入設備文件即可將其傳遞給提供此文件的模塊; 從設備文件中讀取的任何內容都來自提供此文件的模塊。 我們可以將設備文件分為兩組:字符文件和塊文件。 字符文件是非緩沖的,而塊文件是緩沖的。 正如其名稱所暗示的那樣,字符文件允許您逐個字符地讀取和寫入數(shù)據(jù),而塊文件允許您只寫入整個數(shù)據(jù)塊。 我們將討論塊文件超出本文的范圍,并將直接獲得字符文件。

  • Linux系統(tǒng)有一種通過主設備號識別設備文件的方法, 主設備號識別服務設備文件或一組設備的模塊,以及次要設備號 ,用于識別主設備號指定的一組設備中的特定設備。 在驅動程序代碼中,我們可以將這些數(shù)字定義為常量,也可以動態(tài)分配它們。 如果已經使用了定義為常量的數(shù)字,系統(tǒng)將返回錯誤。 當動態(tài)分配一個數(shù)字時,該函數(shù)保留該數(shù)字以禁止其他任何數(shù)字使用它。

指定設備的名稱

  • 下面引用的函數(shù)用于注冊字符設備:

在這里,我們指定要注冊它的設備的名稱和主要編號,之后將鏈接設備和file_operations結構。 如果我們?yōu)橹鲄?shù)指定零,該函數(shù)將自己分配一個主設備號(即它返回的值)。 如果返回的值為零,則表示成功,而負數(shù)表示錯誤。 兩個設備編號均在0-255范圍內指定。

  • 我們將設備名稱作為name參數(shù)的字符串值傳遞(如果模塊注冊單個設備,則此字符串也可以傳遞模塊的名稱)。 然后,我們使用此字符串來標識/ sys / devices文件中的設備。 讀取,寫入和保存等設備文件操作由存儲在file_operations結構中的函數(shù)指針處理。 這些函數(shù)由模塊實現(xiàn),并且指向標識該模塊的module結構的指針也存儲在file_operations結構中。 在這里你可以看到2.6.32內核版本結構:

file_operations結構

  • 如果file_operations結構包含一些不需要的函數(shù),您仍然可以使用該文件而不實現(xiàn)它們。 指向未實現(xiàn)函數(shù)的指針可以簡單地設置為零。 之后,系統(tǒng)將負責該功能的實現(xiàn)并使其正常運行。 在我們的例子中,我們將實現(xiàn)read函數(shù)。

  • 由于我們要確保只使用我們的Linux驅動程序操作單一類型的設備,因此我們的file_operations結構將是全局和靜態(tài)的。 相應地,在它創(chuàng)建之后,我們需要靜態(tài)填充它。 在這里你可以看到這是如何完成的:

THIS_MODULE宏的聲明包含在linux / module.h頭文件中。 我們將宏轉換為指向所需模塊的模塊結構的指針。 稍后,我們將用原型編寫函數(shù)體,但是現(xiàn)在我們只有指向它的指針,即device_file_read 。

file_operations結構允許我們編寫幾個函數(shù)來執(zhí)行和撤銷設備文件的注冊。

  • device_file_major _number是一個包含主設備號的全局變量。 當驅動程序的生命周期到期時,此全局變量將撤消設備文件的注冊。

printk Fucntion

  • 我們已經列出并提到了幾乎所有的功能,最后一個是printk功能。 這個函數(shù)的聲明包含在linux / kernel.h文件中,它的任務很簡單:記錄內核消息。 毫無疑問,請注意KERN_NOTICE和KERN_WARNING前綴,這些前綴出現(xiàn)在printk的所有列出的格式字符串中。 您可能已經猜到, NOTICE和WARNING表示消息的優(yōu)先級。 級別從最無關緊要的KERN_DEBUG到關鍵的KERN_EMERG ,提醒內核不穩(wěn)定。 這是printk函數(shù)和printf庫函數(shù)之間的唯一區(qū)別。

  • printk函數(shù)形成一個字符串,我們將其寫入循環(huán)緩沖區(qū), klog守護程序將其讀取并將其發(fā)送到系統(tǒng)日志。 printk函數(shù)的實現(xiàn)允許從內核中的任何地方調用它。 最糟糕的情況是循環(huán)緩沖區(qū)溢出,這意味著最舊的消息不會記錄在日志中。

  • 下一步是編寫一個函數(shù)來恢復設備文件的注冊。 如果成功注冊了設備文件,則device_file_major_number的值將不為零。 這允許我們使用nregister_chrdev function撤銷文件的注冊,我們在linux / fs.h文件中聲明了該nregister_chrdev function 。 主設備號是此函數(shù)的第一個參數(shù),后跟包含設備名稱的字符串。 register_chrdev和unresister_chrdev函數(shù)以類似的方式起作用。

  • 要注冊設備,我們使用以下代碼:

使用在用戶模式下分配的內存

  • 我們要編寫的函數(shù)將從設備中讀取字符。 此函數(shù)的簽名必須適合file_operations結構中的簽名:

  • 讓我們看看第一個參數(shù),即指向file結構的指針。 此file結構允許我們獲取有關我們正在使用的文件的必要信息,有關此當前文件的私有數(shù)據(jù)的詳細信息,等等。 已讀取的數(shù)據(jù)使用第二個參數(shù)(即緩沖區(qū))分配給用戶空間。 讀取的字節(jié)數(shù)在第三個參數(shù)中定義,我們從第四個參數(shù)中定義的某個偏移量開始讀取字節(jié)。 執(zhí)行該函數(shù)后,必須返回已成功讀取的字節(jié)數(shù),之后必須刷新偏移量。

  • 用戶在用戶模式地址空間中分配特殊緩沖區(qū)。 而read函數(shù)必須執(zhí)行的另一個操作是將信息復制到此緩沖區(qū)。 來自該空間的指針指向的地址和內核地址空間中的地址可以具有不同的值。 這就是我們不能簡單地取消引用指針的原因。 使用這些指針時,我們有一組特定的宏和函數(shù),我們在asm / uaccess.h文件中聲明。 在我們的例子中,最合適的函數(shù)是copy_to_user() 。 它的名字不言而喻:它只是將特定數(shù)據(jù)從內核緩沖區(qū)復制到用戶空間中分配的緩沖區(qū)。 此外,它還驗證指針是否有效以及緩沖區(qū)大小是否足夠大。 因此,可以相對容易地處理驅動器中的錯誤。 這是copy_to_user原型的代碼:

  • 首先,該函數(shù)必須接收三個指針作為參數(shù):指向緩沖區(qū)的指針,指向數(shù)據(jù)源的指針,以及指向復制的字節(jié)數(shù)的指針。 正如我們所提到的,錯誤返回的值不是零,并且在成功執(zhí)行的情況下,該值將為零。 該函數(shù)包含_user宏,其任務是執(zhí)行文檔處理。 它有另一個有用的應用程序,它允許我們分析代碼是否正確使用地址空間中的指針; 這是使用稀疏分析器完成的,稀疏分析器執(zhí)行靜態(tài)代碼分析。 確保始終將用戶地址空間指針標記為_user 。

  • 本教程僅包含沒有實際設備的Linux驅動程序編程示例。 如果在讀取設備文件后不需要返回除文本字符串以外的任何內容,那么這就足夠了。

  • 這是實現(xiàn)read功能的代碼:

構建內核模塊的系統(tǒng)

  • 在我們?yōu)轵寗映绦蚓帉懘a之后,是時候構建它并查看它是否像我們期望的那樣工作。 在早期的內核版本(例如2.4)中,構建模塊需要來自開發(fā)人員的更多動作:編譯環(huán)境需要單獨準備,編譯本身需要GCC編譯器。 只有在那之后,開發(fā)人員才會收到一個* .o文件 - 一個可以加載到內核的模塊。 幸運的是,這些時間早已過去,現(xiàn)在這個過程要簡單得多。 今天,大部分工作都是由makefile完成的:它啟動內核構建系統(tǒng),并為內核提供有關構建模塊所需組件的信息。 從單個源文件構建的模塊需要makefile中的單個字符串。 創(chuàng)建此文件后,您只需要啟動內核構建系統(tǒng):

  • 如您所見,這里我們已將源文件名分配給模塊,該文件將是* .ko文件。

  • 相應地,如果有多個源文件,則只需要兩個字符串

  • make命令初始化內核構建系統(tǒng):

  • 要構建模塊:

要清理構建文件夾:

模塊構建系統(tǒng)通常位于 /lib/modules/uname -r/build中。 現(xiàn)在是時候準備模塊構建系統(tǒng)了。 要構建第一個模塊,請從構建系統(tǒng)所在的文件夾中執(zhí)行以下命令:

最后,我們將我們學到的所有內容組合到一個makefile中:

  • oad目標加載構建模塊, unload目標將其從內核中刪除。

  • 在我們的教程中,我們使用了main.c和device_file.c中的代碼來編譯驅動程序。 生成的驅動程序名為simple-module.ko。

加載和使用模塊

  • 從源文件文件夾執(zhí)行以下命令允許我們加載構建的模塊:

  • 執(zhí)行此命令后,驅動程序的名稱將添加到/proc/modules文件中,而模塊注冊的設備將添加到/proc/devices文件中。 添加的記錄如下所示:

Character devices: 1 mem 4 tty 4 ttyS … 239 Simple-driver …

前三個記錄包含添加的設備的名稱以及與之關聯(lián)的主設備號。 次編號范圍(0-255)允許在/ dev虛擬文件系統(tǒng)中創(chuàng)建設備文件。

在我們創(chuàng)建設備文件之后,我們需要執(zhí)行最終驗證以確保我們所做的工作按預期工作。 要驗證,我們可以使用cat命令顯示內容:

尾文

  • device_file.c有一點編譯問題,這樣修正。

查看內核日志


Linux驅動程序教程:如何編寫Linux設備驅動程序(趕快學習起來~)的評論 (共 條)

分享到微博請遵守國家法律
天全县| 安顺市| 罗源县| 遂平县| 隆回县| 隆安县| 绥宁县| 治多县| 虎林市| 汨罗市| 磴口县| 调兵山市| 襄垣县| 德江县| 兴海县| 巧家县| 阳原县| 黎城县| 佛山市| 丰县| 山丹县| 鹤岗市| 永昌县| 金昌市| 成武县| 增城市| 乌兰浩特市| 磴口县| 海兴县| 都兰县| 虞城县| 涪陵区| 法库县| 平南县| 绥芬河市| 湘西| 都安| 新龙县| 光泽县| 舒城县| 水城县|