linux下的裝置驅動程式被組織為一組完成不同任務的函式的集合,通過這些函式使得windows的裝置操作猶如檔案一般。在應用程式看來,硬體裝置只是乙個裝置檔案,應用程式可以象操作普通檔案一樣對硬體裝置進行操作,如open ()、close ()、read ()、write () 等。
linux主要將裝置分為二類:字元裝置和塊裝置。字元裝置是指裝置傳送和接收資料以字元的形式進行;而塊裝置則以整個資料緩衝區的形式進行。字元裝置的驅動相對比較簡單。
下面我們來假設乙個非常簡單的虛擬字元裝置:這個裝置中只有乙個4個位元組的全域性變數int global_var,而這個裝置的名字叫做"gobalvar"。對"gobalvar"裝置的讀寫等操作即是對其中全域性變數global_var的操作。
驅動程式是核心的一部分,因此我們需要給其新增模組初始化函式,該函式用來完成對所控裝置的初始化工作,並呼叫register_chrdev() 函式註冊字元裝置:
static int __init gobalvar_init(void)
else
}其中,register_chrdev函式中的引數major_num為主裝置號,"gobalvar"為裝置名,gobalvar_fops為包含基本函式入口點的結構體,型別為file_operations。當gobalvar模組被載入時,gobalvar_init被執行,它將呼叫核心函式register_chrdev,把驅動程式的基本入口點指標存放在核心的字元裝置位址表中,在使用者程序對該裝置執行系統呼叫時提供入口位址。
與模組初始化函式對應的就是模組解除安裝函式,需要呼叫register_chrdev()的"反函式" unregister_chrdev():
static void __exit gobalvar_exit(void)
else
}隨著核心不斷增加新的功能,file_operations結構體已逐漸變得越來越大,但是大多數的驅動程式只是利用了其中的一部分。對於字元裝置來說,要提供的主要入口有:open ()、release ()、read ()、write ()、ioctl ()、llseek()、poll()等。
open()函式 對裝置特殊檔案進行open()系統呼叫時,將呼叫驅動程式的open () 函式:
int (*open)(struct inode * ,struct file *);
其中引數inode為裝置特殊檔案的inode (索引結點) 結構的指標,引數file是指向這一裝置的檔案結構的指標。open()的主要任務是確定硬體處在就緒狀態、驗證次裝置號的合法性(次裝置號可以用minor(inode-> i - rdev) 取得)、控制使用裝置的程序數、根據執**況返回狀態碼(0表示成功,負數表示存在錯誤) 等;
release()函式 當最後乙個開啟裝置的使用者程序執行close ()系統呼叫時,核心將呼叫驅動程式的release () 函式:
void (*release) (struct inode * ,struct file *) ;
release 函式的主要任務是清理未結束的輸入/輸出操作、釋放資源、使用者自定義排他標誌的復位等。
read()函式 當對裝置特殊檔案進行read() 系統呼叫時,將呼叫驅動程式read() 函式:
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
用來從裝置中讀取資料。當該函式指標被賦為null 值時,將導致read 系統呼叫出錯並返回-einval("invalid argument,非法引數")。函式返回非負值表示成功讀取的位元組數(返回值為"signed size"資料型別,通常就是目標平台上的固有整數型別)。
globalvar_read函式中核心空間與使用者空間的記憶體互動需要借助第2節所介紹的函式:
static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
write( ) 函式 當裝置特殊檔案進行write () 系統呼叫時,將呼叫驅動程式的write () 函式:
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
向裝置傳送資料。如果沒有這個函式,write 系統呼叫會向呼叫程式返回乙個-einval。如果返回值非負,則表示成功寫入的位元組數。
globalvar_write函式中核心空間與使用者空間的記憶體互動需要借助第2節所介紹的函式:
static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
ioctl() 函式 該函式是特殊的控制函式,可以通過它向裝置傳遞控制資訊或從裝置取得狀態資訊,函式原型為:
int (*ioctl) (struct inode * ,struct file * ,unsigned int ,unsigned long);
unsigned int引數為裝置驅動程式要執行的命令的**,由使用者自定義,unsigned long引數為相應的命令提供引數,型別可以是整型、指標等。如果裝置不提供ioctl 入口點,則對於任何核心未預先定義的請求,ioctl 系統呼叫將返回錯誤(-enotty,"no such ioctl fordevice,該裝置無此ioctl 命令")。如果該裝置方法返回乙個非負值,那麼該值會被返回給呼叫程式以表示呼叫成功。
llseek()函式 該函式用來修改檔案的當前讀寫位置,並將新位置作為(正的)返回值返回,原型為:
loff_t (*llseek) (struct file *, loff_t, int);
poll()函式 poll 方法是poll 和select 這兩個系統呼叫的後端實現,用來查詢裝置是否可讀或可寫,或是否處於某種特殊狀態,原型為:
unsigned int (*poll) (struct file *, struct poll_table_struct *);
裝置"gobalvar"的驅動程式的這些函式應分別命名為gobalvar_open、gobalvar_ release、gobalvar_read、gobalvar_write、gobalvar_ioctl,因此裝置"gobalvar"的基本入口點結構變數gobalvar_fops 賦值如下:
struct file_operations gobalvar_fops = ;
上述**中對gobalvar_fops的初始化方法並不是標準c所支援的,屬於gnu擴充套件語法。
完整的globalvar.c檔案源**如下:
#include
#include
#include
#include
module_license("gpl");
#define major_num 254 //主裝置號
static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);
//初始化字元裝置驅動的file_operations結構體
struct file_operations globalvar_fops =
;static int global_var = 0; //"globalvar"裝置的全域性變數
static int __init globalvar_init(void)
else
return ret;
}static void __exit globalvar_exit(void)
else
}static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
return sizeof(int);
}static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
return sizeof(int);
}module_init(globalvar_init);
module_exit(globalvar_exit);
執行:gcc -d__kernel__ -dmodule -dlinux -i /usr/local/src/linux2.4/include -c -o globalvar.o globalvar.c
編譯**,執行:
inmod globalvar.o
載入globalvar模組,再執行:
cat /proc/devices
發現其中多出了"254 globalvar"一行,如下圖:
接著我們可以執行:
mknod /dev/globalvar c 254 0
建立裝置節點,使用者程序通過/dev/globalvar這個路徑就可以訪問到這個全域性變數虛擬裝置了。我們寫乙個使用者態的程式globalvartest.c來驗證上述裝置:
#include
#include
#include
#include
main()
else
}編譯上述檔案:
gcc -o globalvartest.o globalvartest.c
執行./globalvartest.o
可以發現"globalvar"裝置可以正確的讀寫。
驅動 linux裝置驅動 字元裝置驅動開發
preface 前面對linux裝置驅動的相應知識點進行了總結,現在進入實踐階段!linux 裝置驅動入門篇 linux 裝置驅動掃盲篇 fedora下的字元裝置驅動開發 開發乙個基本的字元裝置驅動 在linux核心驅動中,字元裝置是最基本的裝置驅動。字元裝置包括了裝置最基本的操作,如開啟裝置 關閉...
Linux裝置驅動之《字元裝置驅動》
linux裝置中最大的特點就是裝置操作猶如檔案操作一般,在應用層看來,硬體裝置只是乙個裝置檔案。應用程式可以像操作檔案一樣對硬體裝置進行操作,如open close read write 等。下面是乙個字元裝置驅動程式的簡單實現test.c 模組分析 1.初始化裝置驅動的結構體 struct fil...
Linux裝置驅動之字元裝置驅動
一 linux裝置的分類 linux系統將裝置分成三種基本型別,每個模組通常實現為其中某一類 字元模組 塊模組或網路模組。這三種型別有 字元裝置 字元裝置是個能夠像位元組流 類似檔案 一樣被訪問的裝置,由字元裝置驅動程式來實現這種特性。字元裝置可以通過檔案系統節點來訪問,比如 dev tty1等。這...