- 相關(guān)推薦
Linux系統(tǒng)字符設(shè)備驅(qū)動框架筆記
不積跬步,何以至千里。掌握知識都是從很小的點開始的。下面是小編整理的Linux系統(tǒng)字符設(shè)備驅(qū)動框架筆記,歡迎閱讀!
字符設(shè)備是Linux三大設(shè)備之一(另外兩種是塊設(shè)備,網(wǎng)絡(luò)設(shè)備),字符設(shè)備就是字節(jié)流形式通訊的I/O設(shè)備,絕大部分設(shè)備都是字符設(shè)備,常見的字符設(shè)備包括鼠標、鍵盤、顯示器、串口等等,當我們執(zhí)行 ls -l /dev 的時候,就能看到大量的設(shè)備文件, c 就是字符設(shè)備, b 就是塊設(shè)備,網(wǎng)絡(luò)設(shè)備沒有對應(yīng)的設(shè)備文件。編寫一個外部模塊的字符設(shè)備驅(qū)動,除了要實現(xiàn)編寫一個模塊所需要的代碼之外,還需要編寫作為一個字符設(shè)備的代碼。
驅(qū)動模型
Linux一切皆文件,那么作為一個設(shè)備文件,它的操作方法接口封裝在 struct file_operations ,當我們寫一個驅(qū)動的時候,一定要實現(xiàn)相應(yīng)的接口,這樣才能使這個驅(qū)動可用,Linux的內(nèi)核中大量使用"注冊+回調(diào)"機制進行驅(qū)動程序的編寫,所謂注冊回調(diào),簡單的理解,就是當我們open一個設(shè)備文件的時候,其實是通過VFS找到相應(yīng)的inode,并執(zhí)行此前創(chuàng)建這個設(shè)備文件時注冊在inode中的open函數(shù),其他函數(shù)也是如此,所以,為了讓我們寫的驅(qū)動能夠正常的被應(yīng)用程序操作,首先要做的就是實現(xiàn)相應(yīng)的方法,然后再創(chuàng)建相應(yīng)的設(shè)備文件。
#include //for struct cdev
#include //for struct file
#include //for copy_to_user
#include //for error number
/* 準備操作方法集 */
/*
struct file_operations {
struct module *owner; //THIS_MODULE
//讀設(shè)備
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//寫設(shè)備
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//映射內(nèi)核空間到用戶空間
int (*mmap) (struct file *, struct vm_area_struct *);
//讀寫設(shè)備參數(shù)、讀設(shè)備狀態(tài)、控制設(shè)備
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//打開設(shè)備
int (*open) (struct inode *, struct file *);
//關(guān)閉設(shè)備
int (*release) (struct inode *, struct file *);
//刷新設(shè)備
int (*flush) (struct file *, fl_owner_t id);
//文件定位
loff_t (*llseek) (struct file *, loff_t, int);
//異步通知
int (*fasync) (int, struct file *, int);
//POLL機制
unsigned int (*poll) (struct file *, struct poll_table_struct *);
。。。
};
*/
ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)
{
return 0;
}
struct file fops = {
.owner = THIS_MODULE,
.read = myread,
...
};
/* 字符設(shè)備對象類型 */
struct cdev {
//public
struct module *owner; //模塊所有者(THIS_MODULE),用于模塊計數(shù)
const struct file_operations *ops; //操作方法集(分工:打開、關(guān)閉、讀/寫、...)
dev_t dev; //設(shè)備號(第一個)
unsigned int count; //設(shè)備數(shù)量
//private
...
};
static int __init chrdev_init(void)
{
...
/* 構(gòu)造cdev設(shè)備對象 */
struct cdev *cdev_alloc(void);
/* 初始化cdev設(shè)備對象 */
void cdev_init(struct cdev*, const struct file_opeartions*);
/* 為字符設(shè)備靜態(tài)申請設(shè)備號 */
int register_chedev_region(dev_t from, unsigned count, const char* name);
/* 為字符設(shè)備動態(tài)申請主設(shè)備號 */
int alloc_chedev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name);
MKDEV(ma,mi) //將主設(shè)備號和次設(shè)備號組合成設(shè)備號
MAJOR(dev) //從dev_t數(shù)據(jù)中得到主設(shè)備號
MINOR(dev) //從dev_t數(shù)據(jù)中得到次設(shè)備號
/* 注冊字符設(shè)備對象cdev到內(nèi)核 */
int cdev_add(struct cdev* , dev_t, unsigned);
...
}
static void __exit chrdev_exit(void)
{
...
/* 從內(nèi)核注銷cdev設(shè)備對象 */
void cdev_del(struct cdev* );
/* 從內(nèi)核注銷cdev設(shè)備對象 */
void cdev_put(stuct cdev *);
/* 回收設(shè)備號 */
void unregister_chrdev_region(dev_t from, unsigned count);
...
}
實現(xiàn)read,write
Linux下各個進程都有自己獨立的進程空間,即使是將內(nèi)核的數(shù)據(jù)映射到用戶進程,該數(shù)據(jù)的PID也會自動轉(zhuǎn)變?yōu)樵撚脩暨M程的PID,由于這種機制的存在,我們不能直接將數(shù)據(jù)從內(nèi)核空間和用戶空間進行拷貝,而需要專門的拷貝數(shù)據(jù)函數(shù)/宏:
long copy_from_user(void *to, const void __user * from, unsigned long n)
long copy_to_user(void __user *to, const void *from, unsigned long n)
這兩個函數(shù)可以將內(nèi)核空間的數(shù)據(jù)拷貝到回調(diào)該函數(shù)的用戶進程的用戶進程空間,有了這兩個函數(shù),內(nèi)核中的read,write就可以實現(xiàn)內(nèi)核空間和用戶空間的數(shù)據(jù)拷貝。
ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)
{
long ret = 0;
size = size > MAX_KBUF?MAX_KBUF:size;
if(copy_to_user(user_buf, kbuf,size)
return -EAGAIN;
}
return 0;
}
【Linux系統(tǒng)字符設(shè)備驅(qū)動框架筆記】相關(guān)文章:
Linux系統(tǒng)調(diào)用設(shè)備的ioctl函數(shù)10-20
Linux系統(tǒng)中怎么掛載外界設(shè)備06-14
linux系統(tǒng)命令11-23
linux系統(tǒng)命令(經(jīng)典)01-25
Linux操作系統(tǒng)學習筆記權(quán)限管理09-26
C語言簡單的字符驅(qū)動程序介紹09-22
linux的文件系統(tǒng)05-05
LINUX操作系統(tǒng)01-22