一、重要知識點
1. 主次設(shè)備號
dev_t
dev_t是內(nèi)核中用來表示設(shè)備編號的數(shù)據(jù)類型;
int MAJOR(dev_t dev)
int MINOR(dev_t dev)
這兩個宏抽取主次設(shè)備號。
dev-_t MKDEV(unsigned int major, unsignedint minor)
這個宏由主/次設(shè)備號構(gòu)造一個dev_t結(jié)構(gòu)。
2. 分配和釋放設(shè)備號
int register_chardev_region(dev_t first,unsigned int count, char *name)
靜態(tài)申請設(shè)備號。
Int alloc_chardev_region(dev_t *dev,unsigned int firstminor, unsigned int count, char *name)
動態(tài)申請設(shè)備號,注意第一個參數(shù)是傳地址,而靜態(tài)則是傳值。
3. 幾種重要的數(shù)據(jù)結(jié)構(gòu)
struct file
file結(jié)構(gòu)代表一個打開的文件,它由內(nèi)核在open時創(chuàng)建,并傳遞給該文件上進(jìn)行操作的所有函數(shù),直到最后的close函數(shù)。
file結(jié)構(gòu)private_data是跨系統(tǒng)調(diào)用時保存狀態(tài)信息非常有用的資源。
file結(jié)構(gòu)的f_ops 保存了文件的當(dāng)前讀寫位置。
struct inode
內(nèi)核用inode代表一個磁盤上的文件,它和file結(jié)構(gòu)不同,后者表示打開的文件描述符。對于單個文件,可能會有許多個表示打開文件的文件描述符file結(jié)構(gòu),但他們都指單個inode結(jié)構(gòu)。inode的dev_t i_rdev成員包含了真正的設(shè)備編號,struct cdev *i_cdev包含了指向struct cdev結(jié)構(gòu)的指針。
struct file_operations
file_operations結(jié)構(gòu)保存了字符設(shè)備驅(qū)動程序的方法。
4. 字符設(shè)備的注冊和注銷
struct cdev *cdev_alloc(void);
void cdev_init(struct cdev *dev, structfile_operations *fops);
int cdev_add(struct cdev *dev, dev_t num,unsigned int count);
void cdev_del(struct cdev *dev);
用來管理cdev結(jié)構(gòu)的函數(shù),內(nèi)核中使用該結(jié)構(gòu)表示字符設(shè)備。注意cdev_add函數(shù)的count參數(shù)為次設(shè)備的個數(shù),要想擁有多個次設(shè)備,就必須將該參數(shù)設(shè)為次設(shè)備的個數(shù)。
5. 并發(fā)處理
信號量和自旋鎖的區(qū)別,使用信號量時當(dāng)調(diào)用進(jìn)程試圖獲得一個鎖定了的鎖時會導(dǎo)致進(jìn)程睡眠,而自旋鎖則是一直循法的等待一直到該鎖解鎖了為止。
1)信號量
DECLARE_MUTEX(name);
DECLARE_MUTEX_LOCKED(name);
聲明和初始化用在互斥模式中的信號量的兩個宏
void init_MUTEX(struct semaphore *sem)
void init_MUTEX_LOCKER(struct semaphore*sem);
這兩個函數(shù)可以在運行時初始化信號量
void down(struct semaphore *sem);
int down_interruptible(struct semaphore*sem);
int down_trylock(struct semahpore *sem);
void up(struct semaphore *sem);
鎖定和解鎖信號量。如果必要,down會將調(diào)用進(jìn)程置于不可中斷的休眠狀態(tài);相反,down_interruptible可被信號中斷。down_trylock不會休眠,并且會在信號量不可用時立即返回。鎖定信號量的代碼最后必須使用up解鎖該信號量。
2)自旋鎖
spionlock_t lock = SPIN_LOCK_UNLOCKED;
spin_lock_init(spinlock_t *lock);
初始化自旋鎖的兩種方式。
voidspin_lock(spinlock_t *lock);
鎖定自旋鎖
voidspin_unlock(spinlock_t *lock);
解鎖自旋鎖
二、驅(qū)動代碼
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?MEMDEV_MAJOR?251??
#define?MEMDEV_NUM?2??
#define?MEMDEV_SIZE?1024??
struct?mem_dev??
{??
unsignedint?size;??
char*data;??
structsemaphore?sem;??
};??
static?int?mem_major?=?MEMDEV_MAJOR;??
struct?cdev?mem_cdev;??
struct?mem_dev?*mem_devp;??
static?int?mem_open(struct?inode?*inode,struct?file?*filp)??
{??
structmem_dev?*dev;??
unsignedint?num;??
printk("mem_open.\n");??
num=?MINOR(inode->i_rdev);//獲得次設(shè)備號??
if(num>?(MEMDEV_NUM?-1))??????????//檢查次設(shè)備號有效性??
return-ENODEV;??
dev=?&mem_devp[num];??
filp->private_data=?dev;?//將設(shè)備結(jié)構(gòu)保存為私有數(shù)據(jù)??
return0;??
}??
static?int?mem_release(struct?inode?*inode,struct?file?*filp)??
{??
printk("mem_release.\n");??
return0;??
}??
static?ssize_t?mem_read(struct?file?*filp,char?__user?*buf,?size_t?size,?loff_t?*ppos)??
{??
intret?=?0;??
structmem_dev?*dev;??
unsignedlong?p;??
unsignedlong?count;??
printk("mem_read.\n");??
dev=?filp->private_data;//獲得設(shè)備結(jié)構(gòu)??
count=?size;??
p=?*ppos;??
//檢查偏移量和數(shù)據(jù)大小的有效性??
if(p>?MEMDEV_SIZE)??
return0;??
if(count>?(MEMDEV_SIZE-p))??
count=?MEMDEV_SIZE?-?p;??
if(down_interruptible(&dev->sem))//鎖定互斥信號量??
return?-ERESTARTSYS;??
//讀取數(shù)據(jù)到用戶空間??
if(copy_to_user(buf,dev->data+p,?count)){??
ret=?-EFAULT;??
printk("copyfrom?user?failed\n");??
}??
else{??
*ppos+=?count;??
ret=?count;??
printk("read%d?bytes?from?dev\n",?count);??
}??
up(&dev->sem);//解鎖互斥信號量??
returnret;??
}??
static?ssize_t?mem_write(struct?file?*filp,const?char?__user?*buf,?size_t?size,?loff_t?*ppos)//注意:第二個參數(shù)和read方法不同??
{??
intret?=?0;??
structmem_dev?*dev;??
unsignedlong?p;??
unsignedlong?count;??
printk("mem_write.\n");??
dev=?filp->private_data;??
count=?size;??
p=?*ppos;??
if(p>?MEMDEV_SIZE)??
return0;??
if(count>?(MEMDEV_SIZE-p))??
count=?MEMDEV_SIZE?-?p;??
if(down_interruptible(&dev->sem))//鎖定互斥信號量??
return-ERESTARTSYS;??
if(copy_from_user(dev->data+p,buf,?count)){??
ret=?-EFAULT;??
printk("copyfrom?user?failed\n");??
}??
else{??
*ppos+=?count;??
ret=?count;??
printk("write%d?bytes?to?dev\n",?count);??
}??
up(&dev->sem);//解鎖互斥信號量??
returnret;??
}??
static?loff_t?mem_llseek(struct?file?*filp,loff_t?offset,?int?whence)??
{??
intnewpos;??
printk("mem_llseek.\n");??
switch(whence)??
{??
case0:??
newpos=?offset;??
break;??
case1:??
newpos=?filp->f_pos?+?offset;??
break;??
case2:??
newpos=?MEMDEV_SIZE?-?1?+?offset;??
break;??
default:??
return-EINVAL;??
}??
if((newpos<0)||?(newpos>(MEMDEV_SIZE?-?1)))??
return-EINVAL;??
filp->f_pos=?newpos;??
returnnewpos;??
}??
static?const?struct?file_operationsmem_fops?=?{??
.owner=?THIS_MODULE,??
.open=?mem_open,??
.write=?mem_write,??
.read=?mem_read,??
.release=?mem_release,??
.llseek=?mem_llseek,??
};??
static?int?__init?memdev_init(void)??
{??
intresult;??
interr;??
inti;??
//申請設(shè)備號??
dev_tdevno?=?MKDEV(mem_major,?0);??
if(mem_major)??
result=?register_chrdev_region(devno,?MEMDEV_NUM,?"memdev");//注意靜態(tài)申請的dev_t參數(shù)和動態(tài)dev_t參數(shù)的區(qū)別??
else{??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????//靜態(tài)直接傳變量,動態(tài)傳變量指針??
result=?alloc_chrdev_region(&devno,?0,?MEMDEV_NUM,?"memdev");??
mem_major=?MAJOR(devno);??
}??
if(result0){??
printk("can'tget?major?devno:%d\n",?mem_major);??
returnresult;??
}??
//注冊設(shè)備驅(qū)動??
cdev_init(&mem_cdev,&mem_fops);??
mem_cdev.owner=?THIS_MODULE;??
err=?cdev_add(&mem_cdev,?MKDEV(mem_major,?0),?MEMDEV_NUM);//如果有N個設(shè)備就要添加N個設(shè)備號??
if(err)??
printk("addcdev?faild,err?is?%d\n",?err);??
//分配設(shè)備內(nèi)存??
mem_devp=?kmalloc(MEMDEV_NUM*(sizeof(struct?mem_dev)),?GFP_KERNEL);??
if(!mem_devp){??
result?=?-?ENOMEM;??
goto?fail_malloc;??
}??
memset(mem_devp,0,?MEMDEV_NUM*(sizeof(struct?mem_dev)));??
for(i=0;i
mem_devp[i].size=?MEMDEV_SIZE;??
mem_devp[i].data=?kmalloc(MEMDEV_SIZE,?GFP_KERNEL);??
memset(mem_devp[i].data,0,?MEMDEV_SIZE);??
init_MUTEX(&mem_devp[i].sem);//初始化互斥鎖??
}??
returnresult;??
fail_malloc:??
unregister_chrdev_region(MKDEV(mem_major,0),?MEMDEV_NUM);??
returnresult;??
}??
static?void?memdev_exit(void)??
{??
cdev_del(&mem_cdev);??
unregister_chrdev_region(MKDEV(mem_major,0),?MEMDEV_NUM);//注意釋放的設(shè)備號個數(shù)一定要和申請的設(shè)備號個數(shù)保存一致??
//否則會導(dǎo)致設(shè)備號資源流失??
printk("memdev_exit\n");??
}??
module_init(memdev_init);??
module_exit(memdev_exit);??
MODULE_AUTHOR("Y-Kee");??
MODULE_LICENSE("GPL");??
#include #include #include #include #include #include #include #include #include #include #include #define MEMDEV_MAJOR 251#define MEMDEV_NUM 2#define MEMDEV_SIZE 1024struct mem_dev{unsignedint size;char*data;structsemaphore sem;};static int mem_major = MEMDEV_MAJOR;struct cdev mem_cdev;struct mem_dev *mem_devp;static int mem_open(struct inode *inode,struct file *filp){structmem_dev *dev;unsignedint num;printk("mem_open.\n");num= MINOR(inode->i_rdev);//獲得次設(shè)備號if(num> (MEMDEV_NUM -1)) //檢查次設(shè)備號有效性return-ENODEV;dev= &mem_devp[num];filp->private_data= dev; //將設(shè)備結(jié)構(gòu)保存為私有數(shù)據(jù)return0;}static int mem_release(struct inode *inode,struct file *filp){printk("mem_release.\n");return0;}static ssize_t mem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos){intret = 0;structmem_dev *dev;unsignedlong p;unsignedlong count;printk("mem_read.\n");dev= filp->private_data;//獲得設(shè)備結(jié)構(gòu)count= size;p= *ppos;//檢查偏移量和數(shù)據(jù)大小的有效性if(p> MEMDEV_SIZE)return0;if(count> (MEMDEV_SIZE-p))count= MEMDEV_SIZE - p;if(down_interruptible(&dev->sem))//鎖定互斥信號量return -ERESTARTSYS;//讀取數(shù)據(jù)到用戶空間if(copy_to_user(buf,dev->data+p, count)){ret= -EFAULT;printk("copyfrom user failed\n");}else{*ppos+= count;ret= count;printk("read%d bytes from dev\n", count);}up(&dev->sem);//解鎖互斥信號量returnret;}static ssize_t mem_write(struct file *filp,const char __user *buf, size_t size, loff_t *ppos)//注意:第二個參數(shù)和read方法不同{intret = 0;structmem_dev *dev;unsignedlong p;unsignedlong count;printk("mem_write.\n");dev= filp->private_data;count= size;p= *ppos;if(p> MEMDEV_SIZE)return0;if(count> (MEMDEV_SIZE-p))count= MEMDEV_SIZE - p;if(down_interruptible(&dev->sem))//鎖定互斥信號量return-ERESTARTSYS;if(copy_from_user(dev->data+p,buf, count)){ret= -EFAULT;printk("copyfrom user failed\n");}else{*ppos+= count;ret= count;printk("write%d bytes to dev\n", count);}up(&dev->sem);//解鎖互斥信號量returnret;}static loff_t mem_llseek(struct file *filp,loff_t offset, int whence){intnewpos;printk("mem_llseek.\n");switch(whence){case0:newpos= offset;break;case1:newpos= filp->f_pos + offset;break;case2:newpos= MEMDEV_SIZE - 1 + offset;break;default:return-EINVAL;}if((newpos<0)|| (newpos>(MEMDEV_SIZE - 1)))return-EINVAL;filp->f_pos= newpos;returnnewpos;}static const struct file_operationsmem_fops = {.owner= THIS_MODULE,.open= mem_open,.write= mem_write,.read= mem_read,.release= mem_release,.llseek= mem_llseek,};static int __init memdev_init(void){intresult;interr;inti;//申請設(shè)備號dev_tdevno = MKDEV(mem_major, 0);if(mem_major)result= register_chrdev_region(devno, MEMDEV_NUM, "memdev");//注意靜態(tài)申請的dev_t參數(shù)和動態(tài)dev_t參數(shù)的區(qū)別else{ //靜態(tài)直接傳變量,動態(tài)傳變量指針result= alloc_chrdev_region(&devno, 0, MEMDEV_NUM, "memdev");mem_major= MAJOR(devno);}if(result< 0){printk("can'tget major devno:%d\n", mem_major);returnresult;}//注冊設(shè)備驅(qū)動cdev_init(&mem_cdev,&mem_fops);mem_cdev.owner= THIS_MODULE;err= cdev_add(&mem_cdev, MKDEV(mem_major, 0), MEMDEV_NUM);//如果有N個設(shè)備就要添加N個設(shè)備號if(err)printk("addcdev faild,err is %d\n", err);//分配設(shè)備內(nèi)存mem_devp= kmalloc(MEMDEV_NUM*(sizeof(struct mem_dev)), GFP_KERNEL);if(!mem_devp){result = - ENOMEM;goto fail_malloc;}memset(mem_devp,0, MEMDEV_NUM*(sizeof(struct mem_dev)));for(i=0;i
三、疑點難點
1.
__init
__initdata
__exit
__exitdata
僅用于模塊初始化或清除階段的函數(shù)(__init和__exit)和數(shù)據(jù)(__initdata和__exitdata)標(biāo)記。標(biāo)記為初始化項目會在初始化結(jié)束后丟棄;而退出在內(nèi)核未被配置為可卸載模塊的情況下被簡單的丟棄。被標(biāo)記為__exit的函數(shù)只能在模塊卸載或者系統(tǒng)關(guān)閉時被調(diào)用,其他任何用法都是錯誤的。內(nèi)核通過對應(yīng)的目標(biāo)對象放置在可執(zhí)行文件的特殊ELF段中而讓這些標(biāo)記起作用。
2.static
初始化函數(shù)應(yīng)該被聲明為static,因為這種函數(shù)在特定文件之外沒有其他意義。因為一個模塊函數(shù)要對內(nèi)核其他部分課件,則必須顯示導(dǎo)出,因此這并不是什么強(qiáng)制性規(guī)則。
3. struct module *owner
內(nèi)核使用這個字段以避免在模塊操作正在使用時卸載該模塊。幾乎在所有的情況下,該成員都會被初始化為THIS_MODULE。它是定義在中的一個宏。
4 __user
我們會注意到許多參數(shù)包括含有__user字串,它其實是一種形式的文檔而已,表面指針是一個用戶指針,因此不能被直接用。對通常的編譯來講,__user沒有任何效果,但是可由外部檢查軟件使用,用來尋找對用戶空間地址錯誤使用。
?
評論
查看更多