linux裝置驅動的安全連接埠分配

來源:互聯網
上載者:User
摘要: 編寫裝置驅動是一個具有挑戰性和冒險性的工作。當裝置通過init_mo

dule函數登記時,裝置的資源應當被分 配。一個主要的裝置資源是I/O連接埠。作為動態串連的驅動程式,開發人員應當小心將未被使用的I/O連接埠分配 給這些裝置。 首先驅動程式應偵測這些連接埠是否被使用或釋放。然後再為裝置申請擷取連接埠。當驅動模組被 從核心中移出時,連接埠應該被釋放。這篇文章討論了Linux裝置驅動的安全連接埠分配的複雜性。 介紹裝置驅動開發人員一個主要關心的問題是裝置的資源分派。這些資源套件括I/O連接埠,記憶體和中斷。這篇文章試圖解釋I/O子系統的基本原理和資源分派的重要性,主要是I/O連接埠的資源處理。同時還將闡明如何偵測,申請和釋放裝置的連接埠地址。 基本的硬體元素,如連接埠,匯流排和裝置控制器,構成了大量的不同的I/O裝置。裝置驅動向I/O子系統提供了 一個通用的裝置存取介面,這非常類似於系統調用(systmem call)在應用程式和作業系統之間提供的標準界 面。現在有很多種類型的裝置附屬在電腦上,舉例說來有: 存放裝置,如磁碟,磁帶,光碟機和軟碟機; 人機交 互裝置,如鍵盤,滑鼠和螢幕; 傳輸裝置,如網卡和數據機。不論這些不同裝置的數目巨大,我們只需要理解一些基本的概念,即裝置如何載入以及軟體如何控制硬體。  基本概念裝置由兩部分組成,一個是被稱裝置為控制器的電器部分,另一個是機械部分。控制器通過系統匯流排載入到 電腦上。典型的方式是,一組互不衝突的寄存器組被賦予到各個控制器。I/O連接埠包含4組寄存器,即狀態寄 存器,控制寄存器,資料輸入寄存器,資料輸出寄存器。狀態寄存器擁有可以被主機讀取的(狀態)位,用來 指示當前命令是否執行完畢,或者位元組是否可以被讀出或寫入,以及任何錯誤提示。控制寄存器則被主機寫操作以啟動一條命令或者改變裝置的(工作)模式。資料輸入寄存器用於擷取輸入而資料輸出寄存器則向主機發送結果。 所以,處理器和裝置之間的基本介面是控制和狀態寄存器。當處理器執行程式並且遇到與裝置相關的指令 時,它通過向相應的裝置發送一條命令來執行該指令。控制器執行所要求的動作並設定狀態寄存器的特定位,然後進入等待。處理器有責任檢查裝置的狀態直到發現操作完成。例如並口驅動程式(印表機使用的)一般會 輪詢印表機以知道印表機是否準備好。如果印表機沒有準備好,驅動程式會睡眠一段時間(處理器此時會做其他有用的工作),該過程將重複直到印表機準備好。這種輪詢的機制能夠改進系統的效能。另外一種方式則是 系統進行不必要的"死等"(unnecessarily waiting)而不做任何有用的工作。 寄存器擁有在I/O空間明確定義的位址範圍。通常這些地址在啟動時被分配,使用一組在設定檔中定義的參數。各個裝置的位址範圍可能被預分配,如果裝置是靜態載入的。這意味核心包含了已存在裝置的驅動 程式,以分配的I/O連接埠能被存放在Proc目錄下。你可以在系統使用這些裝置時,通過運行“cat /proc/ioports” 命令同步的檢查其所使用的位址範圍。第一列輸出顯示了連接埠的範圍而第二列則是擁用這些連接埠的裝置。一 些作業系統具備在運行時動態載入裝置驅動模組的特性。所以任何新的裝置都能通過動態載入模組在系統運行時載入到系統中,並且能夠被控制和訪問。 裝置驅動的概念是非常抽象的並且處於一台計算上所運行軟體的最低層。由於直接到裝置的硬體特性的限 制。每個裝置驅動都只管理一種單一類型的裝置。這些類型可能是字元型,快裝置型或網路型。如果一個應用程式向裝置提出(操作)要求。核心會聯絡到對應的裝置驅動,裝置驅動接著向特定的裝置發出命令。裝置驅 動是一個函數集合:包含了許多調用入口,類似於open,close,read,write,ioctl,llseek 等。當你插入你的模組時,init_module ( ) 函數會被調用,而模組被移出時,cleanup_module ( ) 函數會被調用。裝置是在 裝置驅動的init_module ( ) 常式中被登記的。 當裝置在 init_module ( ) 中登記時,裝置的資源如I/O連接埠,記憶體和中斷號也在這個函數被分配,這也 是驅動程式能夠正確操作裝置的需要。如果你分配了任何錯誤的記憶體位址,系統會顯示錯誤資訊segmentation fault。 而對於I/O連接埠,系統不會給出任何類似wrong I/O port的資訊,但是指派任何現有裝置已使用的端 口將會造成系統崩潰。當你移出模組時,裝置應當被登出,更確切的說,主(裝置)號和資源將在cleanup_module ( ) 函 數中被釋放 裝置驅動最頻繁的工作時讀寫IO連接埠。所以你的驅動應當是確信完美的,被裝置使用的連接埠地址是獨佔的。任何其他裝置都不會使用這段位址範圍。為了確認這點,首先驅動應當查明這段地址是否在使用,當驅動發現 這段地址未被使用時,可以申請核心為裝置分配這段地址。 安全連接埠分配現在我們來看看如何通過系統函數來完成資源分派和資源釋放。下面的執行個體 是在linux 2。4核心上進行實驗的,以下的所有實現僅適用於Linux作業系統和某些擴充的Unix變種。 首先偵測可用的連接埠(地址)範圍,通過下面的函數

int check_region (unsigned long start, unsigned long len);
函數返回0表示連接埠地址可用,返回小於零或負的錯誤編碼( -EBUSY or -EINVAL) 表示已在使用中。函數接受2個參數: start 是 連續地區(或I/O連接埠範圍)的起始值,而len是地區內的連接埠數目。 當連接埠可用時,應該將它分配給裝置,通過request_region 函數。
struct resource *request_region (unsigned long start, unsigned long len, char *name);
頭兩個參數和我們前面看到的一樣,字元指標變數name是要分配連接埠地址的裝置名稱。函數返回指向resource結構的指標。Resource結構用來描述資源的範圍,定義於<linux/ioport。h>。結構的格式定義如下  
struct resource {        const char *name;        unsigned long start, end;        unsigned long flags;        struct resource *parent, *sibiling, *child;}; 
當模組從核心移出時,連接埠應當被釋放以便為其它裝置使用,為此我們在 cleanup_module ( )中 使用release_region ( ) 函數。 函數的文法如下
void release_region ( unsigned long start, unsigned long len); 
兩個參數的解釋和前面一致。 以上的3個函數實際上是宏定義,定義於<linux/ioport。h>。   裝置連接埠分配的驅動代碼例子下面的程式說明了動態載入裝置的連接埠分配與回收
#include <linux/fs。h。>#include <linux/ioport。h。> struct file_operations fops;unsigned long start, len; int init_module (void){ int status; start = 0xff90; len   = 0x90;  register_chrdev(254,"your_device",&fops);  status = check_region (start, len); if (status == 0) {     printk ("The ports are available in that range。/n");     request_region(start,len,"your_device"); } else {     printk ("The ports are already in use。 Try other range。/n");     return (status); } return 0;} void cleanup_module (void){ release_region(start, len); printk (" ports are freed successfully/n"); unregister_chrdev(254,"your_device");} printk (" your device is unregistered/n");} 

為了避免混淆,例子代碼中去掉了錯誤檢查和和主(裝置)號的動態分配。 當連接埠分配成功時,我們可以在proc目錄中檢查:
$cat /proc/ioports   驅動程式的核心I/O連接埠函數選擇Linux支援不同位寬的連接埠函數,用於I/O連接埠的讀寫。連接埠可以是8位,16位或32位。Linux的內 核標頭檔<asm/io。h>定義了訪問I/O連接埠的內聯(inline)函數,用於讀取(inx)或寫入(outx) 8位,16位以及32位連接埠。這些函數是  
__u8 inb (unsigned int port);
void outb (__u8 data, unsigned int port);

__u16 inw (unsigned int port);
void outw(__u16 data, unsigned int port);

__u32 inl (unsigned int prot);
void outl (__u32 data, unsigned int port);

 這些函數的串版本(string versions)能讓你在單位時間內更有效傳輸一個以上的資料,通過以下函數  
void insb(unsigned int port, void *addr, unsigned long count);
void outsb(unsigned int port, void *addr, unsigned long count);
addr 是被傳入或傳出的記憶體單元地址,count是被傳輸單元的數量。 Data 則是被讀取或 寫入到"port"連接埠的資料。

void insw(unsigned int port, void *addr, unsigned long count);
void outsw(unsigned int port, void *addr, unsigned long count);
向16位連接埠讀寫16位元據  
void insl(unsigned int port, void *addr, unsigned long count);
void outsl(unsigned int port, void *addr, unsigned long count);
向32位連接埠讀寫32位元據  

上一篇:《分析Linux和windows動態庫》相關文章:《linux 裝置驅動編程》
下一篇:《【linux 編程】BSD Socket 簡易入門手冊》

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.