Linux字元裝置驅動

2021-06-02 02:39:40 字數 4744 閱讀 1497

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等。這...