linux系統中misc子系統

來源:互聯網
上載者:User

標籤:

misc子系統


轉載請註明出處:http://blog.csdn.net/wang_zheng_kai

光源器件與系統研究所

個人學習總結

1、在linux系統中什麼是misc?

         在研究網路攝影機驅動的時候,發現網路攝影機驅動的路徑為:/driver/misc/jz_cim/檔案目錄下,經過尋找結果如下:

         雜項裝置(misc device)

         雜項裝置也是嵌入式系統中用得比較多的一種裝置驅動。在 Linux 核心的include/linux目錄下有miscdevice.h檔案,要把自己定義的misc device從裝置定義在這裡。其實是因為這些字元裝置不符合預先確定的字元裝置範疇,所有這些裝置採用主編號10 ,一起歸於misc device,其實misc_register就是用主標號10調用register_chrdev()的,只不過misc是將一些字元裝置存放在misc類中。換句話說,misc裝置其實也就是特殊的字元裝置。

        

2、linux核心雜項裝置驅動源碼分析

       在Linux驅動中把無法歸類的五花八門的裝置定義為混雜裝置(用miscdevice結構體表述)。miscdevice共用一個主裝置號MISC_MAJOR(即10),但次裝置號不同。所有的miscdevice裝置形成了一個鏈表,對裝置訪問時核心根據次裝置號尋找對應的miscdevice裝置,然後調用其file_operations結構中註冊的檔案操作介面進行操作。在核心中用struct miscdevice表示miscdevice裝置,然後調用其file_operations結構中註冊的檔案操作介面進行操作。miscdevice的API實現在drivers/char/misc.c中,misc裝置的初始化,註冊,登出都在這個檔案中。在核心中,misc雜項裝置驅動介面是對一些字元裝置的簡單封裝,他們共用一個主裝置號,有不同的次裝置號,共用一個open調用,其他的操作函數在開啟後運用linux驅動程式的方法重載進行裝載。

 

我們首先先來看misc裝置的結構體的描述:

         代碼位於:android-4.1/kernel/include/linux/miscdevice.h,該檔案中還有所有misc裝置的次裝置號的宏定義。

struct miscdevice {         intminor;             //次裝置號      const char*name;              //裝置的名稱         const structfile_operations *fops;     //檔案操作       structlist_head list;          //misc_list的鏈表頭     struct device*parent;           //父裝置(Linux裝置模型中的東東了,哈哈)         struct device*this_device;        //當前裝置,是device_create的傳回值,下邊會看到          constchar *nodename;         mode_tmode;}; 

         這個結構體是misc裝置基本的結構體,在註冊misc裝置的時候必須要聲明並初始化一個這樣的結構體,但其中一般只需填充name minor fops欄位就可以了。下面就是led驅動程式中初始化miscdevice的代碼:

static struct miscdevice misc = {     .minor =MISC_DYNAMIC_MINOR,     .name =DEVICE_NAME,     .fops =&dev_fops, }; 

    一般的時候在fops不用實現open方法,因為最初的方法misc_ops包含了open方法。其中minor如果填充MISC_DYNAMIC_MINOR,則是動態次裝置號,次裝置號由misc_register動態分配的。

 

然後來看看misc子系統的初始化函數:

static int __init misc_init(void)  {      int err;   #ifdef CONFIG_PROC_FS         proc_create("misc", 0, NULL,&misc_proc_fops);      /*建立一個proc入口項*/               #endif      misc_class =class_create(THIS_MODULE, "misc");  /*在/sys/class/目錄下建立一個名為misc的類*/     err = PTR_ERR(misc_class);      if(IS_ERR(misc_class))          gotofail_remove;      err =-EIO;     /*註冊裝置,其中裝置的主裝置號為MISC_MAJOR,為10。裝置名稱為misc,misc_fops是操作函數的集合*/      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作為一個子系統被註冊到linux核心中*/ 

         可以看出,這個初始化函數,最主要的功能就是註冊字元裝置 ,所用的註冊介面是2.4核心的register_chrdev。它註冊了主裝置號為MISC_MAJOR,次裝置號為0-255的256個裝置。並且建立了一個misc類。

 

下邊是register_chrdev函數的實現:

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;     /*主裝置號是10,次裝置號為從0開始,分配256個裝置*/     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裝置模型中的,設定kobject的名字*/    kobject_set_name(&cdev->kobj, "%s", name);     for (s =strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))         *s ='!';     /*把這個字元裝置註冊到系統中*/        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; } 來看看這個裝置的操作函數的集合:static const struct file_operations misc_fops = {      .owner      =THIS_MODULE,      .open       = misc_open,  };  

可以看到這裡只有一個開啟函數,使用者開啟miscdevice裝置是通過主裝置號對應的開啟函數,在這個函數中找到次裝置號對應的相應的具體裝置的open函數。它的實現如下:

 

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);      /*找到次裝置號對應的操作函數集合,讓new_fops指向這個具體裝置的操作函數集合*/    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);         /*如果沒有找到,則請求載入這個次裝置號對應的模組*/        request_module("char-major-%d-%d", MISC_MAJOR, minor);         mutex_lock(&misc_mtx);         /*重新遍曆misc_list鏈表,如果沒有找到就退出,否則讓new_fops指向這個具體裝置的操作函數集合*/        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;      /*儲存舊開啟函數的地址*/    old_fops = file->f_op;      /*讓主裝置號的操作函數集合指標指向具體裝置的操作函數集合*/    file->f_op = new_fops;      if(file->f_op->open) {        /*使用具體裝置的開啟函數開啟裝置*/         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;  }  


再來看看misc子系統對外提供的兩個重要的API,misc_register,misc_deregister:

         misc_register()函數在misc.c中,最主要的功能是基於misc_class構造一個裝置,將miscdevice結構掛載到misc_list列表上,並初始化與linux裝置模型相關的結構,它的參數是miscdevice結構體。

int misc_register(struct miscdevice *misc)  {     struct miscdevice *c;     dev_t dev;      interr = 0;     INIT_LIST_HEAD(&misc->list);  //鏈表項使用時必須初始化    mutex_lock(&misc_mtx);      /*遍曆misc_list鏈表,看這個次裝置號以前有沒有被用過,如果次裝置號已被佔有則退出*/    list_for_each_entry(c, &misc_list, list) {         if (c->minor == misc->minor) {             mutex_unlock(&misc_mtx);             return -EBUSY;         }     }      /*看是否是需要動態分配次裝置號*/     if(misc->minor == MISC_DYNAMIC_MINOR) {        /*        *#define DYNAMIC_MINORS 64 /* like dynamic majors */         *static unsigned char misc_minors[DYNAMIC_MINORS / 8];          *這裡存在一個次裝置號的位元影像,一共64位。下邊是遍曆每一位,         *如果這位為0,表示沒有被佔有,可以使用,為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;         }         /*得到這個次裝置號*/        misc->minor = i;                                             }      /*設定位元影像中相應位為1*/     if(misc->minor < DYNAMIC_MINORS)         misc_minors[misc->minor >> 3] |= 1 << (misc->minor& 7);      /*計算出裝置號*/     dev= MKDEV(MISC_MAJOR, misc->minor);      /*在/dev下建立裝置節點,這就是有些驅動程式沒有顯式調用device_create,卻出現了裝置節點的原因*/    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     */      /*將這個miscdevice添加到misc_list鏈表中*/    list_add(&misc->list, &misc_list);   out:     mutex_unlock(&misc_mtx);     return err;  } 


可以看出,這個函數首先遍曆misc_list鏈表,尋找所用的次裝置號是否已經被註冊,防止衝突。如果是動態次裝置號則分配一個,然後調用MKDEV產生裝置號,從這裡可以看出所有的misc裝置共用一個主裝置號MISC_MAJOR,然後調用device_create,產生裝置檔案。最後加入到misc_list鏈表中。

    關於device_create,class_create 作用: class_create函數在misc.c中的模組初始化中被調用,現在一起說一下。這兩個函數看起來很陌生,沒有在ldd3中發現過,看原始碼的時候發現class_create會調用底層組件__class_regsiter()是說明它是註冊一個類。而device_create是建立一個裝置,他是建立裝置的便捷實現調用了device_register函數。他們都提供給linux裝置模型使用,從linux核心2.6的某個版本之後,devfs不複存在,udev成為devfs的替代。相比devfs,udev有很多優勢。

struct class *myclass =class_create(THIS_MODULE, “my_device_driver”); 

class_device_create(myclass, NULL,MKDEV(major_num, 0), NULL, “my_device”); 

   這樣就建立了一個類和裝置,模組被載入時,udev daemon就會自動在/dev下建立my_device裝置檔案節點。這樣就省去了自己建立裝置檔案的麻煩。這樣也有助於動態裝置的管理.

這個是miscdevice的卸載函數:

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);        /*刪除裝置節點*/                              device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));                if(i < DYNAMIC_MINORS && i>0) {        /*釋放位元影像相應位*/         misc_minors[i>>3] &= ~(1 << (misc->minor &7));     }     mutex_unlock(&misc_mtx);     return 0;  }  

總結一下miscdevice驅動的註冊和卸載流程:

misc_register:匹配次裝置號->找到一個沒有佔用的次裝置號(如果需要動態分配的話)->計算裝置號->建立裝置檔案->miscdevice結構體添加到misc_list鏈表中。

misc_deregister:從mist_list中刪除miscdevice->刪除裝置檔案->位元影像位清零。

 

總結:

雜項裝置作為字元裝置的封裝,為字元裝置提供的簡單的編程介面,如果編寫新的字元驅動,可以考慮使用雜項裝置介面,方便簡單,只需要初始化一個miscdevice的結構,調用misc_register就可以了。系統最多有255個雜項裝置,因為雜項裝置模組自己佔用了一個次裝置號

linux系統中misc子系統

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.