當前位置:首頁 » 操作系統 » linux文件驅動

linux文件驅動

發布時間: 2022-11-15 19:47:17

1. 如何玩轉linux驅動



我們很明白Linux 設備驅動的學習是一項浩大的工程,正是由於這個原因,一些人不免望而生畏,其實,只要我們有足夠的積累和全面的知識,玩轉驅動,也是早晚的事。閑話少說,開始來干貨。
對於驅動工程師來說,首先要明白驅動在整個系統中的作用,

大家從上圖中可以看出,linux驅動②在這個構架中起到承上硬體①啟下應用程序③的作用。在程序的編寫中,我們常用高內聚低耦合的標准,因此,驅動的引入顯得意義更加重大:一方面,使嵌入式應用工程師不用考慮過多的硬體差異,另一方面,通過將設備驅動融入內核,面向操作系統內核的介面,這樣的介面由操作系統規定,對一類設備而言結構一致,獨立於具體的設備。同時由於linux操作系統有內存管理和進程管理,因此對於多任務並發的要求時,操作系統和驅動的引入使得任務變得簡單。但是對於不需要多任務調度、文件系統、內存管理等復雜功能時,在一個大while(1)循環中既可以完成相關的任務。
上面分析了驅動的意義,那麼,玩轉linux驅動需要那方面的知識呢,現在羅列下:
 第一、Linux 驅動工程師要有良好的硬體基礎。
這個要求不言而喻,linux驅動工程師的主要任務就是隱藏硬體的差異,給應用工程師一個統一的介面,因此需要能看懂電路圖,理解SRAM、Flash、SDRAM、磁碟等模塊的讀寫方式,知道UART、I2C、USB 等設備的介面以及常規操作,了解輪詢、中斷、DMA 的原理,PCI 匯流排的工作方式以及CPU 的內存管理單元(MMU)等。不過對於這種常見的模塊,linux內核中有相關的配置,因此需要有閱讀linux內核的能力和修改linux內核的能力。
 第二 、Linux驅動工程師具有良好的C 語言基礎。
作為一個面向硬體底層和應用層的關鍵人物,C語言功底是必須要牢固的。在編寫linux的字元設備和塊設備驅動中常用的fopen()、fwrite()、fread()、fclose()以及內存分配中經常使用結構體和指針。因此能靈活地運用C 語言的結構體、指針、函數指針及內存動態申請和釋放顯現的尤為重要。
例如字元設備驅動中的讀函數函數的定義
/* 讀設備*/
ssize_t xxx_read(struct file *filp, char _ _user *buf, size_t count,loff_t*f_pos)
{
...
_to_user(buf, ..., ...);
...
}
從中看出C語言功底的重要性。
第三、 Linux 驅動工程師具有一定的Linux 內核基礎,雖然並不要求工程師對內核各個部分有深入的研究,但至少要了解設備驅動與內核的介面,尤其是對於塊設備、網路設備、Flash設備、串口設備等復雜設備。
現在工作起來,嵌入式驅動工程師的工作量相對會小一點,因為一般常見的硬體設備供應商都會提供相應的linux版本驅動,驅動工程師的任務就是調試這些驅動能正常運行在自己的系統中,同時保證系統的穩定。
 第四、 Linux 驅動工程師具有良好的操作系統知識。
這個要求對於沒有學習過操作系統的人來說唯一的痛苦之處就是對於專有名詞不是很理解,例如上半部,下半部,原子操作等。其實剛開始或許是個痛苦的過程,但是只要認真的分析了一個或者幾個驅動程序後,你就會發現其中的規律。畢竟linux驅動大體分為字元設備驅動、塊設備驅動和網路設備驅動三類,正所謂抓其綱要,舉一反三,便可融會貫通。因此linux中多任務並發控制和同步等基礎很重要,因為在設備驅動中會大量使用自旋鎖、互斥、信號量、等待隊列等並發與同步機制。
第五、動手能力。
紙上得來終覺淺,因此,看再多的書也沒有真正的調試一個驅動來的認識深刻。這時你需要搭建宿主機平台,購買開發板。不要好大喜功,從簡單的小驅動開始一步一步走,以螞蟻啃骨頭的精神進行學習,收獲會很大。
經歷了痛苦的折磨,現在看下嵌入式驅動工程師的甜蜜吧,工作個三五年,你已經是大師了,可以去招聘網站瀏覽下,這方面的待遇都是面議獎金都是大大的,紅色票票也隨心所願了。想到這些,你還不下定決心來經受linux驅動的虐待,相信只要以「驅動虐我千百遍,我待驅動如初戀」的決心,相信你可以玩轉linux驅動。

2. linux如何載入驅動

linux操作系統下,載入驅動的方式有二:

  1. 靜態載入驅動;

  2. 動態載入驅動;

作為前者,靜態載入驅動是通過將驅動程序編譯到內核而進行的一系列配置操作;對於後者而言則是向內核注冊設備信息,從而在kernel啟動後,再通過insmod指令,關聯好主、次設備號,從而以模塊的形式進行載入的;

二者各有優點,所以應用的場合也是不一樣的;

3. Linux設備文件與設備驅動程序之間的關系

設各驅動程序在系統中的位置如圖1所示。由於設各驅動程序是直接與外部設各的寄存器打交道的,並且由於外部設各的多樣性及其快速的發展,設各驅動程序常常是由外部設各供應廠商或者是需要掛接外部設備的計算機開發人員提供的,因此,驅動程序不便與linux內核編制在一起形成一個一體化的結構。於是,linux允許把外部設備以內核模塊的形式來提供設各驅動程序。這樣就可使用戶根據需要'動態地向linux內核插入設各

設各驅動程序在系統中的位置如圖1所示。

由於設各驅動程序是直接與外部設各的寄存器打交道的,並且由於外部設各的多樣性及其快速的發展,設各驅動程序常常是由外部設各供應廠商或者是需要掛接外部設備的計算機開發人員提供的,因此,驅動程序不便與linux內核編制在一起形成一個一體化的結構。於是,linux允許把外部設備以內核模塊的形式來提供設各驅動程序。這樣就可使用戶根據需要'動態地向linux內核插入設各驅動模塊,從而大大提高了內核的靈活性。設備驅動程序與文件系統及應用程序的關系如圖2所示。

4. linux驅動有哪些

1、將驅動程序文件bcm5700src.rpm復制到一個臨時目錄中,並在此目錄中運行以下命令;

2、運行以下命令切換到驅動目錄中;

3、此目錄中會生成一個名字為bcm5700.spec的文件,運行以下命令對驅動程序進行編譯;

4、運行以下命令切換到RPM目錄中;

5、運行以下命令安裝驅動程序;

6、運行以下命令載入驅動模塊;

7、運行kudzu命令,系統會自動搜索到硬體,進行配置即可。

linux是文件型系統,在linux中,一切皆文件,所有硬體都會在對應的目錄(/dev)下面用相應的文件表示。 文件系統的linux下面,都有對於文件與這些設備關聯的,訪問這些文件就可以訪問實際硬體。 通過訪問文件去操作硬體設備,一切都會簡單很多,不需要再調用各種復雜的介面。 直接讀文件,寫文件就可以向設備發送、接收數據。 按照讀寫存儲數據方式,我們可以把設備分為以下幾種:字元設備(character device)、塊設備(Block device)和網路設備( network interface)。

字元設備(character device):指應用程序採用字元流方式訪問的設備。這些設備節點通常為傳真、虛擬終端和串口數據機、鍵盤之類設備提供流通信服務, 它通常只支持順序訪問。字元設備在實現時,大多不使用緩存器。系統直接從設備讀取/寫入每一個字元。

塊設備(Block device):通常支持隨機存取和定址,並使用緩存器,支持mount文件系統。典型的塊設備有硬碟、SD卡、快閃記憶體等,但此類設備一般不需要自己開發,linux對此提過了大部分的驅動。

網路設備(network interface):是一種特殊設備,它並不存在於/dev下面,主要用於網路數據的收發。網路驅動同塊驅動最大的不同在於網路驅動非同步接受外界數據,而塊驅動只對內核的請求作出響應。

上述設備中,字元設備驅動程序適合於大多數簡單的硬體設備,算是各類驅動程序中最簡單的一類,一般也是從這類驅動開始學習,然後再開始學習採用IIC、SPI等通訊介面的一些設備驅動。可以基於此類驅動調試LKT和LCS系列加密晶元。注意7位IIC地址是0x28。

5. linux怎樣載入文件過濾驅動

文件系統過濾驅動是一種可選的,為文件系統提供具有附加值功能的驅動程序。文件系統過濾驅動是一種核心模式組件,它作為Windows NT執行體的一部分運行。
文件系統過濾驅動可以過濾一個或多個文件系統或文件系統卷的I/O操作。按不同的種類劃分,文件系統過濾驅動可以分成日誌記錄、系統監測、數據修改或事件預防幾類。通常,以文件系統過濾驅動為核心的應用程序有防毒軟體、加密程序、分級存儲管理系統等。
二、文件系統過濾驅動並不是設備驅動
設備驅動是用來控制特定硬體I/O設備的軟體組件。例如:DVD存儲設備驅動是一個DVD驅動。
相反,文件系統過濾驅動與一個或多個文件系統協同工作來處理文件I/O操作。這些操作包括:創建、打開、關閉、枚舉文件和目錄;獲取和設置文件、目錄、卷的相關信息;向文件中讀取或寫入數據。另外,文件系統過濾驅動必須支持文件系統特定的功能,例如緩存、鎖定、稀疏文件、磁碟配額、壓縮、安全、可恢復性、還原點和卷裝載等。
下面兩部分詳細的闡述了文件系統過濾驅動和設備驅動之間的相似點與不同點。

三、安裝文件系統過濾驅動
對於Windows XP和後續操作系統來說,可以通過INI文件或安裝應用程序來安裝文件系統過濾驅動(對於Windows 2000和更早的操作系統,過濾驅動通常通過服務控制管理器Service Control Manager來進行安裝)。
四、初始化文件系統過濾驅動
與設備驅動類似,文件系統過濾驅動也使用DriverEntry常式進行初始化工作。在驅動程序載入後,載入驅動相同的組件將通過調用驅動程序的 DriverEntry常式來對驅動程序進行初始化工作。對於文件系統過濾驅動來說,載入和初始化過濾驅動的系統組件為I/O管理器。
DriverEntry常式運行於系統線程上下文中,其IRQL = PASSIVE_LEVEL。本常式可分頁,詳細信息參見MmLockPagableCodeSection。
DriverEntry常式定義如下:
NTSTATUS
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
本常式有兩個輸入參數。第一個參數,DriverObject為系統在文件系統過濾驅動載入時所創建的驅動對象;第二個參數,RegistryPath為包含驅動程序注冊鍵路徑的Unicode字元串。
文件系統過濾驅動按如下順序執行DriverEntry常式:

01、創建控制設備對象:

文件系統過濾驅動的DriverEntry常式通常以創建控制設備對象作為該常式的起始。創建控制設備對象的目的在於允許應用程序即使在過濾驅動載入到文件系統或卷設備對象之前也能夠直接與過濾驅動進行通信。
注意:文件系統也會創建控制設備對象。當文件系統過濾驅動將其自身附加到文件系統之上時(而不是附加到某一特定文件系統卷),過濾驅動同樣將其自身附加到文件系統的控制設備對象之上。

在FileSpy驅動範例中,控制設備對象按如下方式創建:

RtlInitUnicodeString(&nameString, FILESPY_FULLDEVICE_NAME);
status = IoCreateDevice(
DriverObject, //DriverObject
0, //DeviceExtensionSize
&nameString, //DeviceName
FILE_DEVICE_DISK_FILE_SYSTEM, //DeviceType
FILE_DEVICE_SECURE_OPEN, //DeviceCharacteristics
FALSE, //Exclusive
&gControlDeviceObject); //DeviceObject

RtlInitUnicodeString(&linkString, FILESPY_DOSDEVICE_NAME);
status = IoCreateSymbolicLink(&linkString, &nameString);

與文件系統不同,文件系統過濾驅動並不是一定要為其控制設備對象命名。如果傳遞給DeviceName參數一個非空(Non-NULL)值,該值將作為控制設備對象的名稱。接下來,在前面的代碼範例中DriverEntry可以調用IoCreateSymbolicLink常式來將該對象的核心模式名稱與應用程序可見的用戶模式名稱關聯到一起(同樣可以通過調用IoRegisterDeviceInterface來使設備對象對應用程序可見)。
注意:由於控制設備對象是唯一不會附加到設備堆棧中的設備對象,因此控制設備對象是唯一的可安全命名的設備對象。由此,是否為文件系統過濾驅動的控制設備對象是否命名是可選的。
注意:文件系統的控制設備對象必須命名。過濾設備對象從不命名。

6. linux驅動程序如何調用

1、進入到Ubuntu桌面後,打開終端,快捷鍵為ctrl+alt+T。

注意事項:

在很多企業網路中,為了追求速度和安全,Linux操作系統不僅僅是被網路運維人員當作伺服器使用,Linux既可以當作伺服器,又可以當作網路防火牆是Linux的 一大亮點。

7. 如何編寫Linux 驅動程序

以裝載和卸載模塊為例:

1、首先輸入代碼

#include <linux/init.h>

#include <linux/mole.h>

8. linux中驅動放在哪個目錄下

這些文件在正常操作中不會被改變的。這個目錄也包含你的Linux發行版本的主要的應用程序,譬如,Netscape。 /var 目錄包含在正常操作中被改變的文件:假離線文件、記錄文件、加鎖文件、臨時文件和頁格式化文件等。 /home 目錄包含用戶的文件:參數設置文件、個性化文件、文檔、數據、EMAIL、緩存數據等。這個目錄在系統省級時應該保留。 /proc 目錄整個包含虛幻的文件。它們實際上並不存在磁碟上,也不佔用任何空間。(用ls –l 可以顯示它們的大小)當查看這些文件時,實際上是在訪問存在內存中的信息,這些信息用於訪問系統 /bin 系統啟動時需要的執行文件(二進制),這些文件可以被普通用戶使用。 /sbin 系統執行文件(二進制),這些文件不打算被普通用戶使用。(普通用戶仍然可以使用它們,但要指定目錄。) /etc 操作系統的配置文件目錄。 /root 系統管理員(也叫超級用戶或根用戶)的Home目錄。 /dev 設備文件目錄。LINUX下設備被當成文件,這樣一來硬體被抽象化,便於讀寫、網路共享以及需要臨時裝載到文件系統中。正常情況下,設備會有一個獨立的子目 錄。這些設備的內容會出現在獨立的子目錄下。LINUX沒有所謂的驅動符。 /lib 根文件系統目錄下程序和核心模塊的共享庫。 /boot 用於自舉載入程序(LILO或GRUB)的文件。當計算機啟動時(如果有多個操作系統,有可能允許你選擇啟動哪一個操作系統),這些文件首先被裝載。這個目錄也會包含LINUX核(壓縮文件vmlinuz),但LINUX核也可以存在別處,只要配置LILO並且LILO知道LINUX核在哪兒。 /opt 可選的應用程序,譬如,REDHAT 5.2下的KDE (REDHAT 6.0下,KDE放在其它的XWINDOWS應用程序中,主執行程序在/usr/bin目錄下) /tmp 臨時文件。該目錄會被自動清理干凈。 /lost+found 在文件系統修復時恢復的文件 「/usr」目錄下比較重要的部分有: /usr/X11R6 X-WINDOWS系統(version 11, release 6) /usr/X11 同/usr/X11R6 (/usr/X11R6的符號連接) /usr/X11R6/bin 大量的小X-WINDOWS應用程序(也可能是一些在其它子目錄下大執行文件的符號連接)。 /usr/doc LINUX的文檔資料(在更新的系統中,這個目錄移到/usr/share/doc)。 /usr/share 獨立與你計算機結構的數據,譬如,字典中的詞。 /usr/bin和/usr/sbin 類似與「/」根目錄下對應的目錄(/bin和/sbin),但不用於基本的啟動(譬如,在緊急維護中)。大多數命令在這個目錄下。 /usr/local 本地管理員安裝的應用程序(也可能每個應用程序有單獨的子目錄)。在「main」安裝後,這個目錄可能是空的。這個目錄下的內容在重安裝或升級操作系統後應該存在。 /usr/local/bin 可能是用戶安裝的小的應用程序,和一些在/usr/local目錄下大應用程序的符號連接。 /proc目錄的內容: /proc/cpuinfo 關於處理器的信息,如類型、廠家、型號和性能等。 /proc/devices 當前運行內核所配置的所有設備清單。 /proc/dma 當前正在使用的DMA通道。/proc/filesystems 當前運行內核所配置的文件系統。 /proc/interrupts 正在使用的中斷,和曾經有多少個中斷。 /proc/ioports 當前正在使用的I/O埠。 舉例,使用下面的命令能讀出系統的CPU信息。 cat /proc/cpuinfo /bin bin是binary的縮寫。這個目錄沿襲了UNIX系統的結構,存放著使用者最經常使用的命令。例如cp、ls、cat,等等。 /boot 這里存放的是啟動Linux時使用的一些核心文件。 /dev dev是device(設備)的縮寫。這個目錄下是所有Linux的外部設備,其功能類似DOS下的.sys和Win下的.vxd。在Linux中設備和文件是用同種方法訪問的。例如:/dev/hda代表第一個物理IDE硬碟。 /etc 這個目錄用來存放系統管理所需要的配置文件和子目錄。 /home 用戶的主目錄,比如說有個用戶叫wang,那他的主目錄就是/home/wang也可以用~wang表示。 /lib 這個目錄里存放著系統最基本的動態鏈接共享庫,其作用類似於Windows里的.dll文件。幾乎所有的應用程序都須要用到這些共享庫。 /lost+found 這個目錄平時是空的,當系統不正常關機後,這里就成了一些無家可歸的文件的避難所。對了,有點類似於DOS下的.chk文件。 /mnt 這個目錄是空的,系統提供這個目錄是讓用戶臨時掛載別的文件系統。 /proc 這個目錄是一個虛擬的目錄,它是系統內存的映射,我們可以通過直接訪問這個目錄來獲取系統信息。也就是說,這個目錄的內容不在硬碟上而是在內存里。 /root 系統管理員(也叫超級用戶)的主目錄。作為系統的擁有者,總要有些特權啊!比如單獨擁有一個目錄。 /sbin s就是Super User的意思,也就是說這里存放的是系統管理員使用的管理程序。 /tmp 這個目錄不用說,一定是用來存放一些臨時文件的地方了。 /usr 這是最龐大的目錄,我們要用到的應用程序和文件幾乎都存放在這個目錄下。其中包含以下子目錄; /usr/X11R6 存放X-Window的目錄; /usr/bin 存放著許多應用程序; /usr/sbin 給超級用戶使用的一些管理程序就放在這里; /usr/doc 這是Linux文檔的大本營; /usr/include Linux下開發和編譯應用程序需要的頭文件,在這里查找; /usr/lib 存放一些常用的動態鏈接共享庫和靜態檔案庫; /usr/local 這是提供給一般用戶的/usr目錄,在這里安裝軟體最適合; /usr/man man在Linux中是幫助的同義詞,這里就是幫助文檔的存放目錄; /usr/src Linux開放的源代碼就存在這個目錄,愛好者們別放過哦! /var 這個目錄中存放著那些不斷在擴充著的東西,為了保持/usr的相對穩定,那些經常被修改的目錄可以放在這個目錄下,實際上許多系統管理員都是這樣乾的。順帶說一下系統的日誌文件就在/var/log目錄中。 總結來說: · 用戶應該將文件存在/home/user_login_name目錄下(及其子目錄下)。 · 本地管理員大多數情況下將額外的軟體安裝在/usr/local目錄下並符號連接在/usr/local/bin下的主執行程序。 · 系統的所有設置在/etc目錄下。 · 不要修改根目錄(「/」)或/usr目錄下的任何內容,除非真的清楚要做什麼。這些目錄最好和LINUX發布時保持一致。 · 大多數工具和應用程序安裝在目錄:/bin, /usr/sbin, /sbin, /usr/x11/bin,/usr/local/bin。 · 所有的文件在單一的目錄樹下。沒有所謂的「驅動符」

9. LINUX設備驅動程序如何與硬體通信

LINUX設備驅動程序是怎麼樣和硬體通信的?下面將由我帶大家來解答這個疑問吧,希望對大家有所收獲!

LINUX設備驅動程序與硬體設備之間的通信

設備驅動程序是軟體概念和硬體電路之間的一個抽象層,因此兩方面都要討論。到目前為止,我們已經討論詳細討論了軟體概念上的一些細節,現在討論另一方面,介紹驅動程序在Linux上如何在保持可移植性的前提下訪問I/O埠和I/O內存。

我們在需要示例的場合會使用簡單的數字I/O埠來講解I/O指令,並使用普通的幀緩沖區顯存來講解內存映射I/O。

I/O埠和I/O內存

計算機對每種外設都是通過讀寫它的寄存器進行控制的。大部分外設都有幾個寄存器,不管是在內存地址空間還是在I/O地址空間,這些寄存器的訪問地址都是連續的。

I/O埠就是I/O埠,設備會把寄存器映射到I/O埠,不管處理器是否具有獨立的I/O埠地址空間。即使沒有在訪問外設時也要模擬成讀寫I/O埠。

I/O內存是設備把寄存器映射到某個內存地址區段(如PCI設備)。這種I/O內存通常是首先方案,它不需要特殊的處理器指令,而且CPU核心訪問內存更有效率。

I/O寄存器和常規內存

盡管硬體寄存器和內存非常相似,但程序員在訪問I/O寄存器的時候必須注意避免由於CPU或編譯器不恰當的優化而改變預期的I/O動作。

I/O寄存器和RAM最主要的區別就是I/O操作具有邊際效應,而內存操作則沒有:由於內存沒有邊際效應,所以可以用多種 方法 進行優化,如使用高速緩存保存數值、重新排序讀/寫指令等。

編譯器能夠將數值緩存在CPU寄存器中而不寫入內存,即使儲存數據,讀寫操作也都能在高速緩存中進行而不用訪問物理RAM。無論是在編譯器一級或是硬體一級,指令的重新排序都有可能發生:一個指令序列如果以不同於程序文本中的次序運行常常能執行得更快。

在對常規內存進行這些優化的時候,優化過程是透明的,而且效果良好,但是對I/O操作來說這些優化很可能造成致命的錯誤,這是因為受到邊際效應的干擾,而這卻是驅動程序訪問I/O寄存器的主要目的。處理器無法預料某些 其它 進程(在另一個處理器上運行,或在在某個I/O控制器中發生的操作)是否會依賴於內存訪問的順序。編譯器或CPU可能會自作聰明地重新排序所要求的操作,結果會發生奇怪的錯誤,並且很難調度。因此,驅動程序必須確保不使用高速緩沖,並且在訪問寄存器時不發生讀或寫指令的重新排序。

由硬體自身引起的問題很解決:只要把底層硬體配置成(可以是自動的或是由Linux初始化代碼完成)在訪問I/O區域(不管是內存還是埠)時禁止硬體緩存即可。

由編譯器優化和硬體重新排序引起的問題的解決辦法是:對硬體(或其他處理器)必須以特定順序的操作之間設置內存屏障(memory barrier)。Linux提供了4個宏來解決所有可能的排序問題:

#include <linux/kernel.h>

void barrier(void)

這個函數通知編譯器插入一個內存屏障,但對硬體沒有影響。編譯後的代碼會把當前CPU寄存器中的所有修改過的數值保存到內存中,需要這些數據的時候再重新讀出來。對barrier的調用可避免在屏障前後的編譯器優化,但硬體完成自己的重新排序。

#include <asm/system.h>

void rmb(void);

void read_barrier_depends(void);

void wmb(void);

void mb(void);

這些函數在已編譯的指令流中插入硬體內存屏障;具體實現方法是平台相關的。rmb(讀內存屏障)保證了屏障之前的讀操作一定會在後來的讀操作之前完成。wmb保證寫操作不會亂序,mb指令保證了兩者都不會。這些函數都是barrier的超集。

void smp_rmb(void);

void smp_read_barrier_depends(void);

void smp_wmb(void);

void smp_mb(void);

上述屏障宏版本也插入硬體屏障,但僅僅在內核針對SMP系統編譯時有效;在單處理器系統上,它們均會被擴展為上面那些簡單的屏障調用。

設備驅動程序中使用內存屏障的典型形式如下:

writel(dev->registers.addr, io_destination_address);

writel(dev->registers.size, io_size);

writel(dev->registers.operation, DEV_READ);

wmb();

writel(dev->registers.control, DEV_GO);

在這個例子中,最重要的是要確保控制某種特定操作的所有設備寄存器一定要在操作開始之前已被正確設置。其中的內存屏障會強制寫操作以要求的順序完成。

因為內存屏障會影響系統性能,所以應該只用於真正需要的地方。不同類型的內存屏障對性能的影響也不盡相同,所以最好盡可能使用最符合需要的特定類型。

值得注意的是,大多數處理同步的內核原語,如自旋鎖和atomic_t操作,也能作為內存屏障使用。同時還需要注意,某些外設匯流排(比如PCI匯流排)存在自身的高速緩存問題,我們將在後面的章節中討論相關問題。

在某些體系架構上,允許把賦值語句和內存屏障進行合並以提高效率。內核提供了幾個執行這種合並的宏,在默認情況下,這些宏的定義如下:

#define set_mb(var, value) do {var = value; mb();} while 0

#define set_wmb(var, value) do {var = value; wmb();} while 0

#define set_rmb(var, value) do {var = value; rmb();} while 0

在適當的地方,<asm/system.h>中定義的這些宏可以利用體系架構特有的指令更快的完成任務。注意只有小部分體系架構定義了set_rmb宏。

使用I/O埠

I/O埠是驅動程序與許多設備之間的通信方式——至少在部分時間是這樣。本節講解了使用I/O埠的不同函數,另外也涉及到一些可移植性問題。

I/O埠分配

下面我們提供了一個注冊的介面,它允允許驅動程序聲明自己需要操作的埠:

#include <linux/ioport.h>

struct resource *request_region(unsigned long first, unsigned long n, const char *name);

它告訴內核,我們要使用起始於first的n個埠。name是設備的名稱。如果分配成功返回非NULL,如果失敗返回NULL。

所有分配的埠可從/proc/ioports中找到。如果我們無法分配到我們要的埠集合,則可以查看這個文件哪個驅動程序已經分配了這些埠。

如果不再使用這些埠,則用下面函數返回這些埠給系統:

void release_region(unsigned long start, unsigned long n);

下面函數允許驅動程序檢查給定的I/O埠是否可用:

int check_region(unsigned long first, unsigned long n);//不可用返回負的錯誤代碼

我們不贊成用這個函數,因為它返回成功並不能確保分配能夠成功,因為檢查和其後的分配並不是原子操作。我們應該始終使用request_region,因為這個函數執行了必要的鎖定,以確保分配過程以安全原子的方式完成。

操作I/O埠

當驅動程序請求了需要使用的I/O埠范圍後,必須讀取和/或寫入這些埠。為此,大多數硬體都會把8位、16位、32位區分開來。它們不能像訪問系統內存那樣混淆使用。

因此,C語言程序必須調用不同的函數訪問大小不同的埠。那些只支持映射的I/O寄存器的計算機體系架構通過把I/O埠地址重新映射到內存地址來偽裝埠I/O,並且為了易於移植,內核對驅動程序隱藏了這些細節。Linux內核頭文件中(在與體系架構相關的頭文件<asm/io.h>中)定義了如下一些訪問I/O埠的內聯函數:

unsigned inb(unsigned port);

void outb(unsigned char byte, unsigned port);

位元組讀寫埠。

unsigned inw(unsigned port);

void outw(unsigned short word, unsigned port);

訪問16位埠

unsigned inl(unsigned port);

void outl(unsigned longword, unsigned port);

訪問32位埠

在用戶空間訪問I/O埠

上面這些函數主要是提供給設備驅動程序使用的,但它們也可以用戶空間使用,至少在PC類計算機上可以使用。GNU的C庫在<sys/io.h>中定義了這些函數。如果要要用戶空間使用inb及相關函數,則必須滿足正下面這些條件:

編譯程序時必須帶有-O選項來強制內聯函數的展開。

必須用ioperm(獲取單個埠的許可權)或iopl(獲取整個I/O空間)系統調用來獲取對埠進行I/O操作的許可權。這兩個函數都是x86平台特有的。

必須以root身份運行該程序才能調用ioperm或iopl。或者進程的祖先進程之一已經以root身份獲取對埠的訪問。

如果宿主平台沒有以上兩個系統調用,則用戶空間程序仍然可以使用/dev/port設備文件訪問I/O埠。不過要注意,該設備文件的含義與平台密切相關,並且除PC平台以處,它幾乎沒有什麼用處。

串操作

以上的I/O操作都是一次傳輸一個數據,作為補充,有些處理器實現了一次傳輸一個數據序列的特殊指令,序列中的數據單位可以是位元組、字、雙字。這些指令稱為串操作指令,它們執行這些任務時比一個C語言編寫的循環語句快得多。下面列出的宏實現了串I/O:

void insb(unsigned port, void *addr, unsigned long count);

void outsb(unsigned port, void *addr, unsigned long count);從內存addr開始連續讀/寫count數目的位元組。只對單一埠port讀取或寫入數據

void insw(unsigned port, void *addr, unsigned long count);

void outsw(unsigned port, void *addr, unsigned long count);對一個16位埠讀寫16位數據

void insl(unsigned port, void *addr, unsigned long count);

void outsl(unsigned port, void *addr, unsigned long count);對一個32位埠讀寫32位數據

在使用串I/O操作函數時,需要銘記的是:它們直接將位元組流從埠中讀取或寫入。因此,當埠和主機系統具有不同的位元組序時,將導致不可預期的結果。使用inw讀取埠將在必要時交換位元組,以便確保讀入的值匹配於主機的位元組序。然而,串函數不會完成這種交換。

暫停式I/O

在處理器試圖從匯流排上快速傳輸數據時,某些平台(特別是i386)就會出現問題。當處理器時鍾比外設時鍾(如ISA)快時就會出現問題,並且在設備板上特別慢時表現出來。為了防止出現丟失數據的情況,可以使用暫停式的I/O函數來取代通常的I/O函數,這些暫停式的I/O函數很像前面介紹的那些I/O函數,不同之處是它們的名字用_p結尾,如inb_p、outb_p等等。在linux支持的大多數平台上都定義了這些函數,不過它們常常擴展為非暫停式I/O同樣的代碼,因為如果不使用過時的外設匯流排就不需要額外的暫停。

平台相關性

I/O指令是與處理器密切相關的。因為它們的工作涉及到處理器移入移出數據的細節,所以隱藏平台間的差異非常困難。因此,大部分與I/O埠相關的源代碼都與平台相關。

回顧前面函數列表可以看到有一處不兼容的地方,即數據類型。函數的參數根據各平台體系架構上的不同要相應地使用不同的數據類型。例如,port參數在x86平台上(處理器只支持64KB的I/O空間)上定義為unsigned short,但在其他平台上定義為unsigned long,在這些平台上,埠是與內存在同一地址空間內的一些特定區域。

感興趣的讀者可以從io.h文件獲得更多信息,除了本章介紹的函數,一些與體系架構相關的函數有時也由該文件定義。

值得注意的是,x86家族之外的處理器都不為埠提供獨立的地址空間。

I/O操作在各個平台上執行的細節在對應平台的編程手冊中有詳細的敘述;也可以從web上下載這些手冊的PDF文件。

I/O埠示例

演示設備驅動程序的埠I/O的示例代碼運行於通用的數字I/O埠上,這種埠在大多數計算機平台上都能找到。

數字I/O埠最常見的一種形式是一個位元組寬度的I/O區域,它或者映射到內存,或者映射到埠。當把數字寫入到輸出區域時,輸出引腳上的電平信號隨著寫入的各位而發生相應變化。從輸入區域讀取到的數據則是輸入引腳各位當前的邏輯電平值。

這類I/O埠的具體實現和軟體介面是因系統而異的。大多數情況下,I/O引腳由兩個I/O區域控制的:一個區域中可以選擇用於輸入和輸出的引腳,另一個區域中可以讀寫實際的邏輯電平。不過有時情況簡單些,每個位不是輸入就是輸出(不過這種情況下就不能稱為“通用I/O"了);在所有個人計算機上都能找到的並口就是這樣的非通用的I/O埠。

並口簡介

並口的最小配置由3個8位埠組成。第一個埠是一個雙向的數據寄存器,它直接連接到物理連接器的2~9號引腳上。第二個埠是一個只讀的狀態寄存器;當並口連接列印機時,該寄存器 報告 列印機狀態,如是否是線、缺紙、正忙等等。第三個埠是一個只用於輸出的控制寄存器,它的作用之一是控制是否啟用中斷。

如下所示:並口的引腳

示例驅動程序

while(count--) {

outb(*(ptr++), port);

wmb();

}

使用I/O內存

除了x86上普遍使的I/O埠之外,和設備通信的另一種主要機制是通過使用映射到內存的寄存器或設備內存,這兩種都稱為I/O內存,因為寄存器和內存的差別對軟體是透明的。

I/O內存僅僅是類似RAM的一個區域,在那裡處理器可以通過匯流排訪問設備。這種內存有很多用途,比如存放視頻數據或乙太網數據包,也可以用來實現類似I/O埠的設備寄存器(也就是說,對它們的讀寫也存在邊際效應)。

根據計算機平台和所使用匯流排的不同,i/o內存可能是,也可能不是通過頁表訪問的。如果訪問是經由頁表進行的,內核必須首先安排物理地址使其對設備驅動程序可見(這通常意味著在進行任何I/O之前必須先調用ioremap)。如果訪問無需頁表,那麼I/O內存區域就非常類似於I/O埠,可以使用適當形式的函數讀取它們。

不管訪問I/O內存是否需要調用ioremap,都不鼓勵直接使用指向I/O內存的指針。相反使用包裝函數訪問I/O內存,這一方面在所有平台上都是安全的,另一方面,在可以直接對指針指向的內存區域執行操作的時候,這些函數是經過優化的。並且直接使用指針會影響程序的可移植性。

I/O內存分配和映射

在使用之前,必須首先分配I/O區域。分配內存區域的介面如下(在<linux/ioport.h>中定義):

struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);

該函數從start開始分配len位元組長的內存區域。如果成功返回非NULL,否則返回NULL值。所有的I/O內存分配情況可從/proc/iomem得到。

不再使用已分配的內存區域時,使用如下介面釋放:

void release_mem_region(unsigned long start, unsigned long len);

下面函數用來檢查給定的I/O內存區域是否可用的老函數:

int check_mem_region(unsigned long start, unsigned long len);//這個函數和check_region一樣不安全,應避免使用

分配內存之後我們還必須確保該I/O內存對內存而言是可訪問的。獲取I/O內存並不意味著可引用對應的指針;在許多系統上,I/O內存根本不能通過這種方式直接訪問。因此,我們必須由ioremap函數建立映射,ioremap專用於為I/O內存區域分配虛擬地址。

我們根據以下定義來調用ioremap函數:

#include <asm/io.h>

void *ioremap(unsigned long phys_addr, unsigned long size);

void *ioremap_nocache(unsigned long phys_addr, unsigned long size);在大多數計算機平台上,該函數和ioremap相同:當所有I/O內存已屬於非緩存地址時,就沒有必要實現ioremap的獨立的,非緩沖版本。

void iounmap(void *addr);

記住,由ioremap返回的地址不應該直接引用,而應該使用內核提供的accessor函數。

訪問I/O內存

在某些平台上我們可以將ioremap的返回值直接當作指針使用。但是,這種使用不具有可移植性,訪問I/O內存的正確方法是通過一組專用於些目的的函數(在<asm/io.h>中定義)。

從I/O內存中讀取,可使用以下函數之一:

unsigned int ioread8(void *addr);

unsigned int ioread16(void *addr);

unsigned int ioread32(void *addr);

其中,addr是從ioremap獲得的地址(可能包含一個整數偏移量);返回值是從給定I/O內存讀取到的值。

寫入I/O內存的函數如下:

void iowrite8(u8 value, void *addr);

void iowrite16(u16 value, void *addr);

void iowrite32(u32 value, void *addr);

如果必須在給定的I/O內存地址處讀/寫一系列值,則可使用上述函數的重復版本:

void ioread8_rep(void *addr, void *buf, unsigned long count);

void ioread16_rep(void *addr, void *buf, unsigned long count);

void ioread32_rep(void *addr, void *buf, unsigned long count);

void iowrite8_rep(void *addr, const void *buf, unsigned long count);

void iowrite16_rep(void *addr, const void *buf, unsigned long count);

void iowrite32_rep(void *addr, const void *buf, unsigned long count);

上述函數從給定的buf向給定的addr讀取或寫入count個值。count以被寫入數據的大小為單位。

上面函數均在給定的addr處執行所有的I/O操作,如果我們要在一塊I/O內存上執行操作,則可以使用下面的函數:

void memset_io(void *addr, u8 value, unsigned int count);

void memcpy_fromio(void *dest, void *source, unsigned int count);

void memcpy_toio(void *dest, void *source, unsigned int count);

上述函數和C函數庫的對應函數功能一致。

像I/O內存一樣使用I/O埠

某些硬體具有一種有趣的特性:某些版本使用I/O埠,而其他版本則使用I/O內存。導出給處理器的寄存器在兩種情況下都是一樣的,但訪問方法卻不同。為了讓處理這類硬體的驅動程序更加易於編寫,也為了最小化I/O埠和I/O內存訪問這間的表面區別,2.6內核引入了ioport_map函數:

void *ioport_map(unsigned long port, unsigned int count);

該函數重新映射count個I/O埠,使其看起來像I/O內存。此後,驅動程序可在該函數返回的地址上使用ioread8及其相關函數,這樣就不必理會I/O埠和I/O內存之間的區別了。

當不需要這種映射時使用下面函數一撤消:

void ioport_unmap(void *addr);

這些函數使得I/O埠看起來像內存。但需要注意的是,在重新映射之前,我們必須通過request_region來分配這些I/O埠。

為I/O內存重用short

前面介紹的short示例模塊訪問的是I/O埠,它也可以訪問I/O內存。為此必須在載入時通知它使用I/O內存,另外還要修改base地址以使其指向I/O區域。

下例是在MIPS開發板上點亮調試用的LED:

mips.root# ./short_load use_mem=1 base = 0xb7ffffc0

mips.root# echo -n 7 > /dev/short0

下面代碼是short寫入內存區域時使用的循環:

while(count--) {

iowrite8(*ptr++, address);

wmb();

}

1MB地址空間之下的ISA內存

最廣為人知的I/O內存區之一就是個人計算機上的ISA內存段。它的內存范圍在64KB(0xA0000)到1MB(0x100000)之間,因此它正好出現在常規系統RAM的中間。這種地址看上去有點奇怪,因為這個設計決策是20世紀80年代早期作出的,在當時看來沒有人會用到640KB以上的內存。

10. linux如何安裝驅動

在Intel網站直接下載的Linux驅動是e1000-5.2.52.tar.gz(版本可能會有改變),這個壓縮包裡面沒有編譯好的.o的文件,需要在Linux系統下編譯之後才能使用,
因為網卡需要編譯,所以要先確認將內核源文件安裝好,下面是關於內核源文件的安裝
● Linux下添加內核源文件
1. 用rpm –qa|grep kernel-source查看是否安裝了這個包;
如果返回結果中有kernel-source-xxx(其中xxx為當前redhat的內核版本,如rhel3為2.4.21-4EL), 即已經 安裝。如無返回結果則需要安裝kernel-source包。到安裝光碟中找到kernel-source-xxx.i386.rpm,用下面命令安裝此rpm包:
2.如果安裝了用rpm -V kernel-source校驗是否有文件丟失,如果沒有輸出,表示文件完整;
3.如果有丟失用rpm -ivh --force kernel-source-xxxx...把包重新安裝一下;
這個kernel-source包,在您的RH安裝光碟中,在Redhat/RPMS中,如果以前沒有安裝過這個包,那麼用rpm -ivh kernel-source-xxxx...來安裝,如果安裝過,需要覆蓋安裝,使用rpm -ivh --force kernel-source-xxxx...這個命令強制安裝。
註:AS 4 開始,沒有kernel-source這個包了,取而代之的是kernel-dev這個包,檢查這個包有沒有安裝的方法同上
● 驅動安裝步驟:
1. 把這個tar文件拷貝到用戶自己定義的目錄中,例如:
/home/username/e1000 or /usr/local/src/e1000
2. 用tar命令解這個壓縮包:
tar zxf e1000-5.2.52.tar.gz
3. 切換到驅動的src目錄下:
cd e1000-5.2.52/src/
4. 編譯這個驅動模塊:
make
然後安裝這個模塊
make install
這個二進制元將被安裝到如下位置:
/lib/moles//kernel/drivers/net/e1000.o
以上的路徑是默認的安裝位置,在某些linux版本中可能是其他位置,具體信息可以查看在驅動的 tar壓縮包中的ldistrib.txt文件.
5. 安裝模塊:
insmod e1000 (2.6以上的版本最好使用全路徑安裝 P insmod /lib/moles//kernel/drivers/net/e1000/e1000.ko)
6. 設定網卡IP地址:
ifconfig ethx <IP_address> x是網卡介面的號
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
若多個網卡的晶元相同可以cp ifcfg-eth0 ifcfg-eth1~~~~~~
修改下裡面的drive名稱就OK
!!!!!!!!!!!!!!!!!!!
在網卡的編譯中很可能不能進行下去~這個原因除了kernel的開發包沒有安裝外還可能是由於開發環境不完全所引起的!
這時就需要你講開發環境安裝完成,最簡單的辦法就是通過 sysconfig-config-packet 安裝gcc
安裝完成後繼續執行 make ;make install

了解更多開源相關,去LUPA社區看看吧

熱點內容
c語言發簡訊 發布:2024-05-18 13:23:08 瀏覽:833
vb資料庫程序 發布:2024-05-18 13:01:57 瀏覽:111
新建文件夾2免費手機 發布:2024-05-18 12:56:13 瀏覽:365
自己在家搭建伺服器有水冷散熱嗎 發布:2024-05-18 12:47:27 瀏覽:649
舊版的安卓手機怎麼使用微信 發布:2024-05-18 12:46:36 瀏覽:467
我的世界伺服器開多久 發布:2024-05-18 12:45:32 瀏覽:593
vba獲取網頁表格資料庫資料庫資料庫 發布:2024-05-18 12:23:24 瀏覽:700
騰訊伺服器為什麼卡頓 發布:2024-05-18 12:02:12 瀏覽:306
如何知道密碼鎖有沒有nfc 發布:2024-05-18 11:58:09 瀏覽:962
單片機c語言模塊化編程 發布:2024-05-18 11:53:16 瀏覽:645