linux系統(tǒng)中misc子系統(tǒng)
來(lái)源:程序員人生 發(fā)布時(shí)間:2015-01-23 08:59:14 閱讀次數(shù):8204次
misc子系統(tǒng)
轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/wang_zheng_kai
光源器件與系統(tǒng)研究所
個(gè)人學(xué)習(xí)總結(jié)
1、在linux系統(tǒng)中甚么是misc?
在研究攝像頭驅(qū)動(dòng)的時(shí)候,發(fā)現(xiàn)攝像頭驅(qū)動(dòng)的路徑為:/driver/misc/jz_cim/文件目錄下,經(jīng)過(guò)查找結(jié)果以下:
雜項(xiàng)裝備(misc device)
雜項(xiàng)裝備也是嵌入式系統(tǒng)中用得比較多的1種裝備驅(qū)動(dòng)。在 Linux 內(nèi)核的include/linux目錄下有miscdevice.h文件,要把自己定義的misc device從裝備定義在這里。實(shí)際上是由于這些字符裝備不符合預(yù)先肯定的字符裝備范疇,所有這些裝備采取主編號(hào)10 ,1起歸于misc device,其實(shí)misc_register就是用主標(biāo)號(hào)10調(diào)用register_chrdev()的,只不過(guò)misc是將1些字符裝備寄存在misc類中。換句話說(shuō),misc裝備其實(shí)也就是特殊的字符裝備。
2、linux內(nèi)核雜項(xiàng)裝備驅(qū)動(dòng)源碼分析
在Linux驅(qū)動(dòng)中把沒(méi)法歸類的5花8門的裝備定義為混雜裝備(用miscdevice結(jié)構(gòu)體表述)。miscdevice同享1個(gè)主裝備號(hào)MISC_MAJOR(即10),但次裝備號(hào)不同。所有的miscdevice裝備構(gòu)成了1個(gè)鏈表,對(duì)裝備訪問(wèn)時(shí)內(nèi)核根據(jù)次裝備號(hào)查找對(duì)應(yīng)的miscdevice裝備,然后調(diào)用其file_operations結(jié)構(gòu)中注冊(cè)的文件操作接口進(jìn)行操作。在內(nèi)核中用struct miscdevice表示miscdevice裝備,然后調(diào)用其file_operations結(jié)構(gòu)中注冊(cè)的文件操作接口進(jìn)行操作。miscdevice的API實(shí)現(xiàn)在drivers/char/misc.c中,misc裝備的初始化,注冊(cè),注銷都在這個(gè)文件中。在內(nèi)核中,misc雜項(xiàng)裝備驅(qū)動(dòng)接口是對(duì)1些字符裝備的簡(jiǎn)單封裝,他們同享1個(gè)主裝備號(hào),有不同的次裝備號(hào),同享1個(gè)open調(diào)用,其他的操作函數(shù)在打開后應(yīng)用linux驅(qū)動(dòng)程序的方法重載進(jìn)行裝載。
我們首先先來(lái)看misc裝備的結(jié)構(gòu)體的描寫:
代碼位于:android⑷.1/kernel/include/linux/miscdevice.h,該文件中還有所有misc裝備的次裝備號(hào)的宏定義。
struct miscdevice {
intminor; //次裝備號(hào)
const char*name; //裝備的名稱
const structfile_operations *fops; //文件操作
structlist_head list; //misc_list的鏈表頭
struct device*parent; //父裝備(Linux裝備模型中的東東了,哈哈)
struct device*this_device; //當(dāng)前裝備,是device_create的返回值,下邊會(huì)看到
constchar *nodename;
mode_tmode;
};
這個(gè)結(jié)構(gòu)體是misc裝備基本的結(jié)構(gòu)體,在注冊(cè)misc裝備的時(shí)候必須要聲明并初始化1個(gè)這樣的結(jié)構(gòu)體,但其中1般只需填充name minor fops字段就能夠了。下面就是led驅(qū)動(dòng)程序中初始化miscdevice的代碼:
static struct miscdevice misc = {
.minor =MISC_DYNAMIC_MINOR,
.name =DEVICE_NAME,
.fops =&dev_fops,
};
1般的時(shí)候在fops不用實(shí)現(xiàn)open方法,由于最初的方法misc_ops包括了open方法。其中minor如果填充MISC_DYNAMIC_MINOR,則是動(dòng)態(tài)次裝備號(hào),次裝備號(hào)由misc_register動(dòng)態(tài)分配的。
然后來(lái)看看misc子系統(tǒng)的初始化函數(shù):
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS
proc_create("misc", 0, NULL,&misc_proc_fops); /*創(chuàng)建1個(gè)proc入口項(xiàng)*/
#endif
misc_class =class_create(THIS_MODULE, "misc"); /*在/sys/class/目錄下創(chuàng)建1個(gè)名為misc的類*/
err = PTR_ERR(misc_class);
if(IS_ERR(misc_class))
gotofail_remove;
err =-EIO;
/*注冊(cè)裝備,其中裝備的主裝備號(hào)為MISC_MAJOR,為10。裝備名為misc,misc_fops是操作函數(shù)的集合*/
if(register_chrdev(MISC_MAJOR,"misc",&misc_fops))
gotofail_printk;
misc_class->devnode= misc_devnode;
return0;
fail_printk:
printk("unable to get major %d for misc devices/n",MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
returnerr;
}
subsys_initcall(misc_init); /*misc作為1個(gè)子系統(tǒng)被注冊(cè)到linux內(nèi)核中*/
可以看出,這個(gè)初始化函數(shù),最主要的功能就是注冊(cè)字符裝備 ,所用的注冊(cè)接口是2.4內(nèi)核的register_chrdev。它注冊(cè)了主裝備號(hào)為MISC_MAJOR,次裝備號(hào)為0⑵55的256個(gè)裝備。并且創(chuàng)建了1個(gè)misc類。
下邊是register_chrdev函數(shù)的實(shí)現(xiàn):
int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
{
structchar_device_struct *cd;
struct cdev*cdev;
char *s;
int err =-ENOMEM;
/*主裝備號(hào)是10,次裝備號(hào)為從0開始,分配256個(gè)裝備*/
cd =__register_chrdev_region(major, 0, 256, name);
if(IS_ERR(cd))
returnPTR_ERR(cd);
/*分配字符裝備*/
cdev =cdev_alloc();
if(!cdev)
gotoout2;
cdev->owner = fops->owner;
cdev->ops= fops;
/*Linux裝備模型中的,設(shè)置kobject的名字*/
kobject_set_name(&cdev->kobj, "%s", name);
for (s =strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
*s ='!';
/*把這個(gè)字符裝備注冊(cè)到系統(tǒng)中*/
err =cdev_add(cdev, MKDEV(cd->major, 0), 256);
if (err)
gotoout;
cd->cdev =cdev;
return major? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, 0, 256));
returnerr;
}
來(lái)看看這個(gè)裝備的操作函數(shù)的集合:
static const struct file_operations misc_fops = {
.owner =THIS_MODULE,
.open = misc_open,
};
可以看到這里只有1個(gè)打開函數(shù),用戶打開miscdevice裝備是通過(guò)主裝備號(hào)對(duì)應(yīng)的打開函數(shù),在這個(gè)函數(shù)中找到次裝備號(hào)對(duì)應(yīng)的相應(yīng)的具體裝備的open函數(shù)。它的實(shí)現(xiàn)以下:
static int misc_open(struct inode * inode,struct file * file)
{
intminor = iminor(inode);
structmiscdevice *c;
interr = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL;
lock_kernel();
mutex_lock(&misc_mtx);
/*找到次裝備號(hào)對(duì)應(yīng)的操作函數(shù)集合,讓new_fops指向這個(gè)具體裝備的操作函數(shù)集合*/
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops);
break;
}
}
if(!new_fops) {
mutex_unlock(&misc_mtx);
/*如果沒(méi)有找到,則要求加載這個(gè)次裝備號(hào)對(duì)應(yīng)的模塊*/
request_module("char-major-%d-%d", MISC_MAJOR, minor);
mutex_lock(&misc_mtx);
/*重新遍歷misc_list鏈表,如果沒(méi)有找到就退出,否則讓new_fops指向這個(gè)具體裝備的操作函數(shù)集合*/
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops =fops_get(c->fops);
break;
}
}
if (!new_fops)
goto fail;
}
err= 0;
/*保存舊打開函數(shù)的地址*/
old_fops = file->f_op;
/*讓主裝備號(hào)的操作函數(shù)集合指針指向具體裝備的操作函數(shù)集合*/
file->f_op = new_fops;
if(file->f_op->open) {
/*使用具體裝備的打開函數(shù)打開裝備*/
err=file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
}
fops_put(old_fops);
fail:
mutex_unlock(&misc_mtx);
unlock_kernel();
return err;
}
再來(lái)看看misc子系統(tǒng)對(duì)外提供的兩個(gè)重要的API,misc_register,misc_deregister:
misc_register()函數(shù)在misc.c中,最主要的功能是基于misc_class構(gòu)造1個(gè)裝備,將miscdevice結(jié)構(gòu)掛載到misc_list列表上,并初始化與linux裝備模型相干的結(jié)構(gòu),它的參數(shù)是miscdevice結(jié)構(gòu)體。
int misc_register(struct miscdevice *misc)
{
struct miscdevice *c;
dev_t dev;
interr = 0;
INIT_LIST_HEAD(&misc->list); //鏈表項(xiàng)使用時(shí)必須初始化
mutex_lock(&misc_mtx);
/*遍歷misc_list鏈表,看這個(gè)次裝備號(hào)之前有無(wú)被用過(guò),如果次裝備號(hào)已被占有則退出*/
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
}
/*看是不是是需要?jiǎng)討B(tài)分配次裝備號(hào)*/
if(misc->minor == MISC_DYNAMIC_MINOR) {
/*
*#define DYNAMIC_MINORS 64 /* like dynamic majors */
*static unsigned char misc_minors[DYNAMIC_MINORS / 8];
*這里存在1個(gè)次裝備號(hào)的位圖,1共64位。下邊是遍歷每位,
*如果這位為0,表示沒(méi)有被占有,可使用,為1表示被占用。
*/
int i = DYNAMIC_MINORS;
while (--i >= 0)
if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
break;
if (i<0) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
/*得到這個(gè)次裝備號(hào)*/
misc->minor = i;
}
/*設(shè)置位圖中相應(yīng)位為1*/
if(misc->minor < DYNAMIC_MINORS)
misc_minors[misc->minor >> 3] |= 1 << (misc->minor& 7);
/*計(jì)算出裝備號(hào)*/
dev= MKDEV(MISC_MAJOR, misc->minor);
/*在/dev下創(chuàng)建裝備節(jié)點(diǎn),這就是有些驅(qū)動(dòng)程序沒(méi)有顯式調(diào)用device_create,卻出現(xiàn)了裝備節(jié)點(diǎn)的緣由*/
misc->this_device = device_create(misc_class, misc->parent, dev,NULL,
"%s",misc->name);
if(IS_ERR(misc->this_device)) {
err = PTR_ERR(misc->this_device);
goto out;
}
/*
*Add it to the front, so that later devices can "override"
*earlier defaults
*/
/*將這個(gè)miscdevice添加到misc_list鏈表中*/
list_add(&misc->list, &misc_list);
out:
mutex_unlock(&misc_mtx);
return err;
}
可以看出,這個(gè)函數(shù)首先遍歷misc_list鏈表,查找所用的次裝備號(hào)是不是已被注冊(cè),避免沖突。如果是動(dòng)態(tài)次裝備號(hào)則分配1個(gè),然后調(diào)用MKDEV生成裝備號(hào),從這里可以看出所有的misc裝備同享1個(gè)主裝備號(hào)MISC_MAJOR,然后調(diào)用device_create,生成裝備文件。最后加入到misc_list鏈表中。
關(guān)于device_create,class_create 作用: class_create函數(shù)在misc.c中的模塊初始化中被調(diào)用,現(xiàn)在1起說(shuō)1下。這兩個(gè)函數(shù)看起來(lái)很陌生,沒(méi)有在ldd3中發(fā)現(xiàn)過(guò),看源代碼的時(shí)候發(fā)現(xiàn)class_create會(huì)調(diào)用底層組件__class_regsiter()是說(shuō)明它是注冊(cè)1個(gè)類。而device_create是創(chuàng)建1個(gè)裝備,他是創(chuàng)建裝備的便捷實(shí)現(xiàn)調(diào)用了device_register函數(shù)。他們都提供給linux裝備模型使用,從linux內(nèi)核2.6的某個(gè)版本以后,devfs不復(fù)存在,udev成為devfs的替換。相比devfs,udev有很多優(yōu)勢(shì)。
struct class *myclass =class_create(THIS_MODULE, “my_device_driver”);
class_device_create(myclass, NULL,MKDEV(major_num, 0), NULL, “my_device”);
這樣就創(chuàng)建了1個(gè)類和裝備,模塊被加載時(shí),udev daemon就會(huì)自動(dòng)在/dev下創(chuàng)建my_device裝備文件節(jié)點(diǎn)。這樣就省去了自己創(chuàng)建裝備文件的麻煩。這樣也有助于動(dòng)態(tài)裝備的管理.
這個(gè)是miscdevice的卸載函數(shù):
int misc_deregister(struct miscdevice*misc)
{
inti = misc->minor;
if(list_empty(&misc->list))
return -EINVAL;
mutex_lock(&misc_mtx);
/*在misc_list鏈表中刪除miscdevice裝備*/
list_del(&misc->list);
/*刪除裝備節(jié)點(diǎn)*/
device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
if(i < DYNAMIC_MINORS && i>0) {
/*釋放位圖相應(yīng)位*/
misc_minors[i>>3] &= ~(1 << (misc->minor &7));
}
mutex_unlock(&misc_mtx);
return 0;
}
總結(jié)1下miscdevice驅(qū)動(dòng)的注冊(cè)和卸載流程:
misc_register:匹配次裝備號(hào)->找到1個(gè)沒(méi)有占用的次裝備號(hào)(如果需要?jiǎng)討B(tài)分配的話)->計(jì)算裝備號(hào)->創(chuàng)建裝備文件->miscdevice結(jié)構(gòu)體添加到misc_list鏈表中。
misc_deregister:從mist_list中刪除miscdevice->刪除裝備文件->位圖位清零。
總結(jié):
雜項(xiàng)裝備作為字符裝備的封裝,為字符裝備提供的簡(jiǎn)單的編程接口,如果編寫新的字符驅(qū)動(dòng),可以斟酌使用雜項(xiàng)裝備接口,方便簡(jiǎn)單,只需要初始化1個(gè)miscdevice的結(jié)構(gòu),調(diào)用misc_register就能夠了。系統(tǒng)最多有255個(gè)雜項(xiàng)裝備,由于雜項(xiàng)裝備模塊自己占用了1個(gè)次裝備號(hào)
生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)