linux系統裝置的3種型別:字元裝置驅動、塊裝置驅動和網路裝置驅動
字元裝置:只能乙個乙個位元組讀寫資料的裝置,不能隨機讀取裝置記憶體中的某一資料
塊裝置:可以從裝置的任意位置讀取一定長度資料的裝置
在 /dev 目錄中 執行 ls-l 時
第乙個字母為c表示該裝置為字元裝置,為b表示塊裝置
乙個字元裝置或者塊裝置都有乙個主裝置號和乙個此裝置號,統稱為裝置號。主裝置號用來表示乙個特定的驅動程式,此裝置號用來表示使用該驅動程式的各裝置。
1.主裝置號和次裝置號的表示
linux核心中 dev_t型別用來表示裝置號,在linux 2.6.34.14中
typedef u_long dev_tu_long 在32位機中是4個位元組,64位機中是8位元組,32位中,高12位為主裝置號,低20位為次裝置號。
2.主裝置號和次裝置號的獲取
為了保障可移植性,應該使用巨集major和minor獲取主裝置號和次裝置號,mkdev巨集生成裝置號,巨集的定義如下:
#define minorbits 20 //次裝置號的位數
#define minormask ((1u << minorbits)-1) //次裝置號掩碼 有minorbits個1
#define major(dev) ((unsigned int) ((dev) >> minorbits))
//dev右移20位得到主裝置號
#define minor(dev) ((unsigned int) ((dev) &minormask))
//與次裝置號掩碼相與得到次裝置號
#define mkdev(ma,mi) (((ma)<
3.分配裝置號
驅動開發者靜態指定乙個裝置號,核心開發者已經為常用裝置分配了裝置號,在核心原始碼/documentation/devices.txt 中可找到,此方法很可能造成裝置號衝突
建議使用方法,動態分配裝置號函式為 alloc_chrdev_region()
int register_chrdev_region(dev_t form,unsigned count,const char *name);form :要分配的裝置號範圍的起始值,一般只提供form的主裝置號,次裝置號通常設定為0
count:需要申請的連續裝置號的個數
name:和該範圍編號關聯的裝置名稱,不能超過64位元組
成功返回0
int alloc_chr_dev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name);dev : 輸出引數,函式成功後儲存分配到的裝置號或者連續裝置號的第乙個
baseminor : 要申請的第乙個次裝置號 通常設為0
count : 要申請的連續裝置號個數 與register_chrdev_region()函式引數相同
name : 裝置名字 與register_chrdev_region()函式引數相同
void unregister_chardev_region(dev_t form ,unsigned count);form:要釋放的裝置號
count:從form開始要釋放的裝置號個數
4.檢視裝置號
當靜態分配裝置號時,需要檢視檔案系統中已經存在的裝置號,從而決定使用哪個裝置號,方法為讀取/proc/devices檔案,如下所示
can@ubuntu:~/c/hehe$ cat /proc/devices當申請字元裝置的裝置號之後,需要將字元裝置註冊到系統中,才能使用字元裝置。character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttys
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyprintk
6 lp
7 vcs
10 misc
13 input
14 sound/midi
14 sound/dmmidi
21 sg
29 fb
block devices:
1 ramdisk
259 blkext
7 loop
8 sd
9 md
11 sr
65 sd
66 sd
67 sd
68 sd
69 sd
linux核心中使用cdev結構體描述字元裝置,其包含了大量字元裝置所共有的特性。cdev結構體定義:
struct cdev
list 結構是乙個雙向鍊錶,用於將其他結構體連線成乙個雙向鍊錶。該結構在linux核心中廣泛使用
struct list_head
cdev結構體的list成員連線到了inode結構體i_devices成員。其中i_devices也是乙個list_head結構。cdev結構與inode結點組成了乙個雙向鍊錶。
每乙個字元裝置在/dev目錄下都有乙個裝置檔案,開啟裝置檔案就相當於開啟相應字元裝置。例如應用程式開啟裝置檔案a,那麼系統會產生乙個inode結點,通過inode結點的i_cdev欄位找到cdev結構體,通過cdev的ops指標,就能找到裝置的操作函式。
在 linux/fs.h 中定義,對裝置進行操作的抽象結構體,常用的函式有open(),read(),write(),close(),ioctl()等
某個版本linux核心中的file_operation結構體定義
struct file_operations ;
重要成員:
owner:不是乙個函式,是乙個指向這個結構模組的指標,用來維持引用計數,當模組還在使用時,不能使用rmmod解除安裝模組,幾乎所有時刻被簡單初始化為 this_module 乙個在linux/module.h中的巨集定義
llseek():改變檔案中的當前讀寫位置,將新的位置返回 loff_t型別為 long long型別
read() : 從裝置獲取資料,成功返回讀取位元組數,失敗返回負的錯誤編碼
write() : 用來邪道裝置中,成功返回寫入位元組數,失敗返回負錯誤碼
ioctl() : 執行裝置特定命令的方法
open() : 開啟乙個裝置,如果被複製為null,那麼裝置永遠開啟成功,並不會對裝置產生影響
release(): 釋放被open()函式中申請的資源,將在檔案引用計數為0時被系統呼叫,對應應用程式的close()方法,但僅當對裝置檔案的所有開啟都被釋放後才會被呼叫
常見的裝置結構體、載入函式和解除安裝函式如下:
struct ***_dev;
static
int __init ***_init(void)
//初始化cdev結構,傳遞file_operation結構指標
cdev_init(&***_dev.cdev,&***_fops);
dev->cdev.owner = this_module; //指定所屬模組
err = cdev_add(&***_dev.cdev,***_devno,1); //註冊裝置
}static
void __exit ***_exit(void)
大多數字元裝置驅動都會實現read(),write()和ioctl()函式,常見寫法如下**所示
//檔案操作結構體
static const struct file_operations ***_fops=
//讀函式
static ssize_t ***_read(struct file *filp,char _user *buf, size_t size,loff_t *ppos)
//寫函式
static ssize_t ***_write(struct file *filp,const char _user *buf,size_t size,loff_t *oppos)
//ioctl 裝置控制函式
static long ***_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
return
0;}
unsigned
long copy_to_user(void _user *to,const
void *from,unsigned
long n);
unsigned
long copy_from_user(void *to,const _user *from,unsigned
long n);
put_user(local,user);
get_user(local,user);
驅動 linux裝置驅動 字元裝置驅動開發
preface 前面對linux裝置驅動的相應知識點進行了總結,現在進入實踐階段!linux 裝置驅動入門篇 linux 裝置驅動掃盲篇 fedora下的字元裝置驅動開發 開發乙個基本的字元裝置驅動 在linux核心驅動中,字元裝置是最基本的裝置驅動。字元裝置包括了裝置最基本的操作,如開啟裝置 關閉...
Linux裝置驅動之《字元裝置驅動》
linux裝置中最大的特點就是裝置操作猶如檔案操作一般,在應用層看來,硬體裝置只是乙個裝置檔案。應用程式可以像操作檔案一樣對硬體裝置進行操作,如open close read write 等。下面是乙個字元裝置驅動程式的簡單實現test.c 模組分析 1.初始化裝置驅動的結構體 struct fil...
Linux裝置驅動之字元裝置驅動
一 linux裝置的分類 linux系統將裝置分成三種基本型別,每個模組通常實現為其中某一類 字元模組 塊模組或網路模組。這三種型別有 字元裝置 字元裝置是個能夠像位元組流 類似檔案 一樣被訪問的裝置,由字元裝置驅動程式來實現這種特性。字元裝置可以通過檔案系統節點來訪問,比如 dev tty1等。這...