亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
12下一頁(yè)
最近訪問(wèn)板塊 發(fā)新帖
查看: 11932 | 回復(fù): 15
打印 上一主題 下一主題

字符設(shè)備驅(qū)動(dòng)模型淺析 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2010-07-19 18:26 |只看該作者 |倒序?yàn)g覽
本帖最后由 zhiqiang0071 于 2010-07-26 09:17 編輯

本文屬本人原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處。由于個(gè)人的見(jiàn)識(shí)和能力有限,不可能面面俱到,也可能存在謬誤,敬請(qǐng)網(wǎng)友指出,本人的郵箱是yzq.seen@gmail.com,博客是http://zhiqiang0071.cublog.cn。

      在linux系統(tǒng)中,很多驅(qū)動(dòng)是字符型驅(qū)動(dòng),有些是直接編譯集成在內(nèi)核中,另一些是單獨(dú)編譯成“.ko”動(dòng)態(tài)加載的。其實(shí)字符驅(qū)動(dòng)只是個(gè)外殼,用于內(nèi)核與應(yīng)用程序間通信,無(wú)非是調(diào)用open,release,read,write和ioctl等例程。所以根據(jù)應(yīng)用不同,字符驅(qū)動(dòng)能會(huì)調(diào)用其他驅(qū)動(dòng)模塊,如i2c、spi和v4l2等,于是字符驅(qū)動(dòng)還可分WDT驅(qū)動(dòng)、RTC驅(qū)動(dòng)和MTD驅(qū)動(dòng)等。所以在分析其他驅(qū)動(dòng)模塊之前有必要好好分析下字符設(shè)備驅(qū)動(dòng)模型。本篇文章要講的就是字符設(shè)備驅(qū)動(dòng)模型,也就是字符設(shè)備驅(qū)動(dòng)是怎么注冊(cè)和注銷的,怎么生成設(shè)備節(jié)點(diǎn)的,怎么和應(yīng)用程序關(guān)聯(lián)的,例程調(diào)用具體如何實(shí)現(xiàn)的等等。

一、字符設(shè)備驅(qū)動(dòng)的注冊(cè)和注銷
對(duì)于寫過(guò)linux-2.6內(nèi)核(本文采用linux-2.6.18內(nèi)核)字符驅(qū)動(dòng)的程序員來(lái)說(shuō),對(duì)下面這段程序的形式肯定不陌生。
  1. int result;
  2.         /*
  3.          * Register the driver in the kernel
  4.          * Dynmically get the major number for the driver using
  5.          * alloc_chrdev_region function
  6.          */
  7.         result = alloc_chrdev_region(&dev, 0, 1, “testchar”);

  8.         /* if it fails return error */
  9.         if (result < 0) {
  10.                 printk("Error registering test character device\n");
  11.                 return -ENODEV;
  12.         }

  13.         printk(KERN_INFO " test major#: %d, minor# %d\n", MAJOR(dev), MINOR(dev));

  14.         /* initialize cdev with file operations */
  15.         cdev_init(&cdev, & test _fops);

  16.         cdev.owner = THIS_MODULE;
  17.         cdev.ops = &test_fops;

  18.         /* add cdev to the kernel */
  19.         result = cdev_add(&cdev, dev, 1);

  20.         if (result) {
  21.                 unregister_chrdev_region(dev, 1);
  22.                 printk("Error adding test char device: error no:%d\n", result);
  23.                 return -EINVAL;
  24.         }
  25. testchar _class = class_create(THIS_MODULE, "testchar");
  26.         if (!testchar _class) {
  27.                 printk("Error creating test device class\n");
  28.                 unregister_chrdev_region(dev, 1);
  29.                 unregister_chrdev(MAJOR(dev), "testchar");
  30.                 cdev_del(&cdev);
  31.                 return -EINVAL;
  32.         }
  33. class_device_create(testchar _class, NULL, dev, NULL, "testchar");
復(fù)制代碼
通常這段程序會(huì)放在一個(gè)模塊初始化加載函數(shù)里,形式是這樣的,
  1. int __init testchar_init(void)
  2. {
  3. }
  4. module_init(testchar_init);
復(fù)制代碼
既然有注冊(cè)的函數(shù),那必然有注銷的函數(shù),這叫有進(jìn)必有出,有公必有母…,總而言之,這是大自然的神奇被人類所利用。廢話少說(shuō),形式是這樣的,
  1. void __exit testchar _cleanup(void)
  2. {
  3.         /* remove major number allocated to this driver */
  4.         unregister_chrdev_region(dev, 1);

  5.         /* remove simple class device */
  6.         class_device_destroy(testchar_class, dev);

  7.         /* destroy simple class */
  8.         class_destroy(testchar class);

  9.         cdev_del(&cdev);

  10.         /* unregistering the driver from the kernel */
  11.         unregister_chrdev(MAJOR(dev), "testchar");
  12. }
  13. module_exit(testchar_cleanup);
復(fù)制代碼
這些注冊(cè)字符驅(qū)動(dòng)的例程調(diào)用大都集中在文件fs/char_dev.c中。所以先來(lái)看看這個(gè)文件中都有些啥,這叫直搗黃龍。
這有個(gè)初始化函數(shù),在模塊加載過(guò)程中會(huì)被調(diào)用到(動(dòng)態(tài)insmod加載或在內(nèi)核中加載),如下,
  1. void __init chrdev_init(void)
  2. {
  3.         cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
  4. }。
復(fù)制代碼
kobj_map_init()函數(shù)傳進(jìn)去了兩個(gè)參數(shù),base_probe函數(shù)和chrdevs_lock互斥變量,返回一個(gè)struct kobj_map類型的指針。
base_probe調(diào)用了request_module()函數(shù),用于加載與字符驅(qū)動(dòng)相關(guān)的驅(qū)動(dòng)程序,被加載的驅(qū)動(dòng)命名方式是char-major-主設(shè)備號(hào)-次設(shè)備號(hào)。request_module()函數(shù),你可以看看該函數(shù)上頭的英文注釋,它最終會(huì)調(diào)用應(yīng)用層空間的modprobe命令來(lái)加載驅(qū)動(dòng)程序。實(shí)際上,沒(méi)有使用該項(xiàng)功能。
chrdevs_lock是一個(gè)全局的互斥變量,用于整個(gè)設(shè)備驅(qū)動(dòng)模塊的關(guān)鍵區(qū)域保護(hù),后面你會(huì)看到。
返回的是struct kobj_map類型指針,保存到cdev_map中,該結(jié)構(gòu)體干嗎用的呢,顧名思義,用來(lái)做映射連通的,后面會(huì)慢慢說(shuō)明。先來(lái)看看該結(jié)構(gòu)體的定義,
  1. struct kobj_map {
  2.         struct probe {
  3.                 struct probe *next;         /* 被255整除后相同的設(shè)備號(hào)鏈成一個(gè)單向鏈表 */
  4.                 dev_t dev;  /* 字符設(shè)備驅(qū)動(dòng)的設(shè)備號(hào),包含主設(shè)備號(hào)(高12位)和次設(shè)備號(hào)(低20位) */
  5.                 unsigned long range;       /* 次設(shè)備號(hào)范圍 */
  6.                 struct module *owner;     /* 表明模塊的歸屬,是THIS_MODULE */
  7.                 kobj_probe_t *get;         /* 這里可以保存?zhèn)鬟M(jìn)來(lái)的base_probe函數(shù)指針,可回調(diào) */
  8.                 int (*lock)(dev_t, void *);  /* 保存回調(diào)函數(shù),具體是啥,后續(xù)會(huì)說(shuō)到 */
  9.                 void *data;
  10. } *probes[255];  /* 雖然大小只有255,但采用了鏈表的形式,可以支持到4096個(gè)主設(shè) */
  11.         struct mutex *lock;   /* 保存全局互斥鎖,用于關(guān)鍵區(qū)域的保護(hù) */
  12. };
復(fù)制代碼
我們?cè)賮?lái)看看kobj_map_init()函數(shù)里頭做了什么,該函數(shù)是這樣的,
  1. struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
  2. {
  3.         struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
  4.         struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
  5.         int i;

  6.         if ((p == NULL) || (base == NULL)) {
  7.                 kfree(p);
  8.                 kfree(base);
  9.                 return NULL;
  10.         }

  11.         base->dev = 1;
  12.         base->range = ~0;                 /* 初始的范圍很大 */
  13.         base->get = base_probe;        /* 保存函數(shù)指針 */
  14.         for (i = 0; i < 255; i++)
  15.                 p->probes[i] = base;       /* 所有指針都指向同一個(gè)base */
  16.         p->lock = lock;
  17.         return p;
  18. }。
復(fù)制代碼
該函數(shù)只是分配了一個(gè)結(jié)構(gòu)體struct kobj_map,并做了初始化,保存了函數(shù)指針base_probe和全局鎖lock。

下面就按照驅(qū)動(dòng)注冊(cè)流程一個(gè)個(gè)解析這些例程調(diào)用吧。首先是alloc_chrdev_region()函數(shù),解析它之前,先看看結(jié)構(gòu)體(定義了255個(gè)結(jié)構(gòu)體指針),
  1. static struct char_device_struct {
  2.         /*被255整除后相同的設(shè)備號(hào)鏈成一個(gè)單向鏈表*/
  3.         struct char_device_struct *next;  
  4.         unsigned int major;                /* 主設(shè)備號(hào) */
  5.         unsigned int baseminor;          /* 次設(shè)備起始號(hào) */
  6.         int minorct;                /* 次設(shè)備號(hào)范圍 */
  7.         char name[64];        /* 驅(qū)動(dòng)的名字 */
  8.         struct file_operations *fops;   /* 保存文件操作指針,目前沒(méi)有使用 */
  9.         struct cdev *cdev;                /* will die */   /*目前沒(méi)有使用*/
  10. } *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; /* CHRDEV_MAJOR_HASH_SIZE = 255 */
復(fù)制代碼
它的作用僅僅是用于注冊(cè)字符設(shè)備驅(qū)動(dòng),保存已經(jīng)注冊(cè)字符驅(qū)動(dòng)的一些信息,如主次設(shè)備號(hào),次設(shè)備號(hào)的數(shù)量,驅(qū)動(dòng)的名字等,便于字符設(shè)備驅(qū)動(dòng)注冊(cè)時(shí)索引查找。alloc_chrdev_region()函數(shù)很簡(jiǎn)單,通過(guò)調(diào)用__register_chrdev_region()來(lái)實(shí)現(xiàn),通過(guò)英語(yǔ)注釋你也可以明白,這個(gè)函數(shù)有兩個(gè)作用,一是,如果主設(shè)備號(hào)為0,則分配一個(gè)最近的主設(shè)備號(hào),返回給調(diào)用者;二是,如果主設(shè)備號(hào)不為0,則占用好該主設(shè)備號(hào)對(duì)應(yīng)的位置,返回給調(diào)用者。如下,
  1. static struct char_device_struct *
  2. __register_chrdev_region(unsigned int major, unsigned int baseminor,
  3.                            int minorct, const char *name)
  4. {
  5.         struct char_device_struct *cd, **cp;
  6.         int ret = 0;
  7.         int i;

  8.         cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
  9.         if (cd == NULL)
  10.                 return ERR_PTR(-ENOMEM);

  11.         mutex_lock(&chrdevs_lock);  /* 這下看到了吧,加鎖,就允許你一個(gè)人進(jìn)來(lái) */

  12.         /* temporary */
  13.         if (major == 0) {         /* 如果主設(shè)備號(hào)為零,則找一個(gè)最近空閑的號(hào)碼分配 */
  14.                 for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
  15.                         if (chrdevs[i] == NULL)
  16.                                 break;
  17.                 }

  18.                 if (i == 0) {
  19.                         ret = -EBUSY;
  20.                         goto out;
  21.                 }
  22.                 major = i;
  23.                 ret = major;
  24.         }

  25.         /* 這些不用說(shuō)你懂的 */
  26.         cd->major = major;
  27.         cd->baseminor = baseminor;
  28.         cd->minorct = minorct;
  29.         strncpy(cd->name,name, 64);

  30.         i = major_to_index(major);

  31.         /* 如果主設(shè)備號(hào)不為0,則占用好該主設(shè)備號(hào)對(duì)應(yīng)的位置 */
  32.         for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
  33.                 if ((*cp)->major > major ||
  34.                     ((*cp)->major == major && (*cp)->baseminor >= baseminor))
  35.                         break;
  36.         if (*cp && (*cp)->major == major &&
  37.             (*cp)->baseminor < baseminor + minorct) {
  38.                 ret = -EBUSY;
  39.                 goto out;
  40.         }
  41.         cd->next = *cp;
  42.         *cp = cd;
  43.         mutex_unlock(&chrdevs_lock);   /* 開(kāi)鎖,隊(duì)列里的下一個(gè)人可以進(jìn)來(lái)了 */
  44.         return cd;
  45. out:
  46.         mutex_unlock(&chrdevs_lock);
  47.         kfree(cd);
  48.         return ERR_PTR(ret);
  49. }
復(fù)制代碼
接著是cdev_init()函數(shù),先說(shuō)說(shuō)cdev的結(jié)構(gòu)體,
  1. struct cdev {
  2.         struct kobject kobj; /* 不多解釋了,看看鄙人前面寫的文章吧 */
  3.         struct module *owner; /* 模塊鎖定和加載時(shí)用得著 */
  4.         const struct file_operations *ops; /* 保存文件操作例程結(jié)構(gòu)體 */
  5.         struct list_head list; /* open時(shí),會(huì)將其inode加到該鏈表中,方便判別是否空閑 */
  6.         dev_t dev; /* 設(shè)備號(hào) */
  7.         unsigned int count;
  8. };
復(fù)制代碼
cdev結(jié)構(gòu)體把字符設(shè)備驅(qū)動(dòng)和文件系統(tǒng)相關(guān)聯(lián),后面解析字符設(shè)備驅(qū)動(dòng)怎樣運(yùn)行的時(shí)候會(huì)詳談。
cdev_init()函數(shù)如下,
  1. void cdev_init(struct cdev *cdev, const struct file_operations *fops)
  2. {
  3.          memset(cdev, 0, sizeof *cdev);
  4.          INIT_LIST_HEAD(&cdev->list);
  5.          cdev->kobj.ktype = &ktype_cdev_default; /* 卸載驅(qū)動(dòng)時(shí)會(huì)用到,別急,后面詳講 */
  6.          kobject_init(&cdev->kobj);
  7.          cdev->ops = fops; /* 用戶寫的字符設(shè)備驅(qū)動(dòng)fops就保存在這了 */
  8. }。
復(fù)制代碼
你也看到了,該函數(shù)就是對(duì)變量做了初始化,關(guān)于kobject的解析,建議你看看鄙人博客上寫的《Linux設(shè)備模型淺析之設(shè)備篇》和《Linux設(shè)備模型淺析之驅(qū)動(dòng)篇》兩篇文章,這里就不詳談了。
用戶的fops,在本文中是test_fops,一般形式是這樣的,
  1. static const struct file_operations test_fops = {
  2.         .owner  = THIS_MODULE,
  3.         .open   = test_fops_open,
  4.         .release  = test_fops_release,
  5.         .ioctl    = test_fops_ioctl,
  6.         .read   = test_fops_read,
  7.         .write  = test_fops_write,
  8. };
復(fù)制代碼


接著又調(diào)用了函數(shù)cdev_add(),這個(gè)函數(shù)又調(diào)用了kobj_map()函數(shù),其作用就是分配一個(gè)struct probe結(jié)構(gòu)體,填充該結(jié)構(gòu)體中的變量并將其加入到全局的cdev_map中,說(shuō)白了,就是分個(gè)一畝三分田給該字符設(shè)備驅(qū)動(dòng),并做好標(biāo)記,放到主設(shè)備號(hào)對(duì)應(yīng)的地方,等主人下次來(lái)找的時(shí)候能找到(使用kobj_lookup()函數(shù),后面會(huì)講到)。該函數(shù)是這樣的,
  1. int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
  2.              struct module *module, kobj_probe_t *probe,
  3.              int (*lock)(dev_t, void *), void *data)
  4. {
  5.         unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
  6.         unsigned index = MAJOR(dev);
  7.         unsigned i;
  8.         struct probe *p;

  9.         if (n > 255)
  10.                 n = 255;
  11.         /* 分配了一畝三分田 */
  12.         p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);

  13.         if (p == NULL)
  14.                 return -ENOMEM;

  15.         /* 填充些私有的東西 */
  16.         for (i = 0; i < n; i++, p++) {
  17.                 p->owner = module;
  18.                 p->get = probe;  /* 是exact_match ()函數(shù),獲取cdev結(jié)構(gòu)體的kobject指針 */
  19.                 p->lock = lock;   /* 是exact_lock()函數(shù),增加引用*/
  20.                 p->dev = dev;
  21.                 p->range = range;
  22.                 p->data = data;      /* cdev保存到p->data中 */
  23.         }
  24.         mutex_lock(domain->lock);
  25.         /* 將這一畝三分田加到主設(shè)備號(hào)對(duì)應(yīng)的位置上去 */
  26.         for (i = 0, p -= n; i < n; i++, p++, index++) {
  27.                 struct probe **s = &domain->probes[index % 255];
  28.                 while (*s && (*s)->range < range)
  29.                         s = &(*s)->next;
  30.                 p->next = *s;
  31.                 *s = p;
  32.         }
  33.         mutex_unlock(domain->lock);
  34.         return 0;
  35. }
復(fù)制代碼
接下來(lái)有class_create()函數(shù)和class_device_create()函數(shù),前者生成一個(gè)名字為"testchar"的class,后者作用就是在/dev目錄下生成設(shè)備節(jié)點(diǎn),當(dāng)然,需要uevent和UDEVD的支持,具體可見(jiàn)鄙人博客上的文章《Linux設(shè)備模型淺析之uevent篇》。
     順帶說(shuō)下register_chrdev()函數(shù),其也是注冊(cè)字符設(shè)備驅(qū)動(dòng),只不過(guò)是封裝好的,包含了所有前面講的注冊(cè)步驟——分配一個(gè)設(shè)備號(hào),由一個(gè)主設(shè)備號(hào)和255個(gè)次設(shè)備號(hào)組成。如下,
  1. int register_chrdev(unsigned int major, const char *name,
  2.                     const struct file_operations *fops)
  3. {
  4.         struct char_device_struct *cd;
  5.         struct cdev *cdev;
  6.         char *s;
  7.         int err = -ENOMEM;

  8.         cd = __register_chrdev_region(major, 0, 256, name);
  9.         if (IS_ERR(cd))
  10.                 return PTR_ERR(cd);
  11.         
  12.         cdev = cdev_alloc();   /* 這個(gè)有點(diǎn)不一樣,動(dòng)態(tài)分配的,不是調(diào)用者提供 */
  13.         if (!cdev)
  14.                 goto out2;

  15.         cdev->owner = fops->owner;
  16.         cdev->ops = fops;
  17.         kobject_set_name(&cdev->kobj, "%s", name);
  18.         for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
  19.                 *s = '!';
  20.                
  21.         err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
  22.         if (err)
  23.                 goto out;

  24.         cd->cdev = cdev;

  25.         return major ? 0 : cd->major;
  26. out:
  27.         kobject_put(&cdev->kobj);
  28. out2:
  29.         kfree(__unregister_chrdev_region(cd->major, 0, 256));
  30.         return err;
  31. }
復(fù)制代碼

論壇徽章:
0
2 [報(bào)告]
發(fā)表于 2010-07-19 19:15 |只看該作者
本帖最后由 zhiqiang0071 于 2010-07-26 09:18 編輯

二、字符設(shè)備驅(qū)動(dòng)的調(diào)用機(jī)制
都注冊(cè)好了,應(yīng)用程序可以打開(kāi)該設(shè)備驅(qū)動(dòng)文件了。都是通過(guò)它來(lái)實(shí)現(xiàn)的,
  1. const struct file_operations def_chr_fops = {
  2.         .open = chrdev_open,
  3. };
復(fù)制代碼
在char_dev.c文件中并沒(méi)有哪個(gè)例程使用def_chr_fops,在隔壁的fs/inode.c中被init_special_inode()函數(shù)調(diào)用了,如下
  1. void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
  2. {
  3.         inode->i_mode = mode;
  4.         if (S_ISCHR(mode)) {         /* 判斷是不是字符類型的文件 */
  5.                 inode->i_fop = &def_chr_fops;   /* 這就是我們要找的 */
  6.                 inode->i_rdev = rdev;
  7.         } else if (S_ISBLK(mode)) {
  8.                 inode->i_fop = &def_blk_fops;
  9.                 inode->i_rdev = rdev;
  10.         } else if (S_ISFIFO(mode))
  11.                 inode->i_fop = &def_fifo_fops;
  12.         else if (S_ISSOCK(mode))
  13.                 inode->i_fop = &bad_sock_fops;
  14.         else
  15.                 printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o)\n",
  16.                        mode);
  17. }
復(fù)制代碼
init_special_inode()函數(shù)又被特定文件系統(tǒng)調(diào)用,比如cramfs,在fs/cramfs/inode.c的cramfs_iget5_set()函數(shù)中被調(diào)用。所以對(duì)任何文件系統(tǒng)操作時(shí)都能調(diào)用到。要知道linux采用了VFS虛擬文件系統(tǒng)層,也就是可以同時(shí)支持很多不同種類的文件系統(tǒng),比如cramfs, ext2, ext3等,但這些文件系統(tǒng)都要注冊(cè)到VFS層才行,注冊(cè)后,就有機(jī)會(huì)調(diào)用到cramfs_iget5_set()了,具體不詳談了(其實(shí)我知道的也就這么多,呵呵)。很顯然,inode在linux系統(tǒng)中代表一個(gè)文件或目錄,inode->i_fop用來(lái)對(duì)文件或目錄進(jìn)行操作的,所以這里inode->i_fop = &def_chr_fops后,應(yīng)用程序再去調(diào)用open、close、read、write和ioctl等操作時(shí),就映射到了def_chr_fops,但是,def_chr_fops中只有一個(gè).open = chrdev_open,并沒(méi)有read, wirte等的實(shí)現(xiàn),那么現(xiàn)在就來(lái)說(shuō)說(shuō)為什么了。
當(dāng)應(yīng)用程序使用open()打開(kāi)字符設(shè)備驅(qū)動(dòng)文件時(shí),最終調(diào)用到了chrdev_open()函數(shù),我們來(lái)看看其中發(fā)生了什么,
  1. int chrdev_open(struct inode * inode, struct file * filp)
  2. {
  3.         struct cdev *p;
  4.         struct cdev *new = NULL;
  5.         int ret = 0;

  6.         spin_lock(&cdev_lock);
  7.         p = inode->i_cdev;
  8.         if (!p) {        /* 很顯然,第一次打開(kāi)的時(shí)候是NULL */
  9.                 struct kobject *kobj;
  10.                 int idx;
  11.                 spin_unlock(&cdev_lock);
  12.               /* 找到和設(shè)備號(hào)i_rdev對(duì)應(yīng)的kobj,其實(shí)就是cdev了,因?yàn)閏dev中包含kobj;idx保存的是次設(shè)備號(hào),后面會(huì)分析kobj_lookup()函數(shù) */
  13.                 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
  14.                 if (!kobj)
  15.                         return -ENXIO;
  16.                 new = container_of(kobj, struct cdev, kobj);  /* 說(shuō)的沒(méi)錯(cuò)吧,得到cdev了 */
  17.                 spin_lock(&cdev_lock);
  18.                 p = inode->i_cdev;
  19.                 if (!p) {
  20.                         inode->i_cdev = p = new;  /* 把找到的cdev保存到inode的icdev中 */
  21.                         inode->i_cindex = idx;       /* 次設(shè)備號(hào)保存到inode中,用戶驅(qū)動(dòng)可能需要 */
  22.                         list_add(&inode->i_devices, &p->list);  /* inode加入到cdev的鏈表中 */
  23.                         new = NULL;
  24.                 } else if (!cdev_get(p))
  25.                         ret = -ENXIO;
  26.         } else if (!cdev_get(p))
  27.                 ret = -ENXIO;
  28.         spin_unlock(&cdev_lock);
  29.         cdev_put(new);
  30.         if (ret)
  31.                 return ret;
  32. /*
  33. 保存用戶的fops,以后你再調(diào)用read, write, ioctl系統(tǒng)調(diào)用的時(shí)候就直接使用了,你懂的
  34. */
  35.         filp->f_op = fops_get(p->ops);
  36.         if (!filp->f_op) {         /* 如果你沒(méi)有注冊(cè)fops,那你的路還得慢慢走 */
  37.                 cdev_put(p);
  38.                 return -ENXIO;
  39.         }
  40.         /* 判斷open函數(shù)是否存在,沒(méi)有,那么對(duì)不起了,不合格 */
  41.         if (filp->f_op->open) {
  42.                 lock_kernel();
  43.                 /* 調(diào)用用戶的open函數(shù),你寫過(guò)驅(qū)動(dòng),你懂的 */
  44.                 ret = filp->f_op->open(inode,filp);
  45.         }
  46.         if (ret)
  47.                 cdev_put(p);
  48.         return ret;
  49. }
復(fù)制代碼
看到了吧,這個(gè)chrdev_open()函數(shù)起到承上啟下的作用,把用戶注冊(cè)的字符驅(qū)動(dòng)程序的各個(gè)函數(shù)調(diào)用與應(yīng)用程序調(diào)用連接了起來(lái)。
再來(lái)看一下kobj_lookup()函數(shù)吧,
  1. struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
  2. {
  3.         struct kobject *kobj;
  4.         struct probe *p;
  5.         unsigned long best = ~0UL;

  6. retry:
  7.         mutex_lock(domain->lock);
  8.        /* 根據(jù)主設(shè)備號(hào)和設(shè)備號(hào)查找它的一畝三分地。因?yàn)橐С?^12次方也就是4096個(gè)主設(shè)備號(hào),但只使用了前255個(gè)主設(shè)備號(hào)索引,所以這255個(gè)索引對(duì)應(yīng)的probe結(jié)構(gòu)都有一個(gè)單向      鏈表保存著大于255的主設(shè)備號(hào)(被255整除后的索引相等)  */
  9.         for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
  10.                 struct kobject *(*probe)(dev_t, int *, void *);
  11.                 struct module *owner;
  12.                 void *data;
  13.                 /* 比較,看是否真找到了,因?yàn)橛墟湵泶嬖,所以不一定就是哦?*/
  14.                 if (p->dev > dev || p->dev + p->range - 1 < dev)
  15.                         continue;         /* 不是就繼續(xù)找唄 */
  16.                 if (p->range - 1 >= best)  /* 不太可能吧… */
  17.                         break;
  18.                 if (!try_module_get(p->owner))
  19.                         continue;
  20.                 owner = p->owner;
  21.                 data = p->data;  /* 你如果從頭看到現(xiàn)在,那么你了解,data就是cdev */
  22.                 probe = p->get;
  23.                 best = p->range - 1;
  24.                 *index = dev - p->dev;  /* 得到了次設(shè)備號(hào),為什么?你懂的… */
  25.                 /* 調(diào)用的lock就是exact_lock()函數(shù),增加對(duì)該字符設(shè)備驅(qū)動(dòng)的引用,防止被卸載什么的 */
  26.                 if (p->lock && p->lock(dev, data) < 0) {
  27.                         module_put(owner);
  28.                         continue;
  29.                 }
  30.                 mutex_unlock(domain->lock);
  31.                 /*調(diào)用的probe就是exact_match()函數(shù),獲取cdev的kobj指針 */
  32.                 kobj = probe(dev, index, data);
  33.                 /* Currently ->owner protects _only_ ->probe() itself. */
  34.                 module_put(owner);
  35.                 if (kobj)
  36.                         return kobj;
  37.                 goto retry;
  38.         }
  39.         mutex_unlock(domain->lock);
  40.         return NULL;
  41. }。
復(fù)制代碼
open調(diào)用的流程圖如下(ARM平臺(tái)),


三、字符設(shè)備驅(qū)動(dòng)的注銷
當(dāng)你想換個(gè)花樣,不想使用這個(gè)字符設(shè)備驅(qū)動(dòng)的時(shí)候,你可能會(huì)叫“rmmod 帝”幫下忙,要不然也沒(méi)啥別的辦法。rmmod命令使用后,會(huì)調(diào)用到文章開(kāi)頭里說(shuō)的一個(gè)注銷函數(shù)testchar _cleanup(),
  1. void __exit testchar _cleanup(void)
  2. {
  3.         /* remove major number allocated to this driver */
  4.         unregister_chrdev_region(dev, 1);

  5.         /* remove simple class device */
  6.         class_device_destroy(testchar_class, dev);

  7.         /* destroy simple class */
  8.         class_destroy(testchar class);

  9.         cdev_del(&cdev);

  10.         /* unregistering the driver from the kernel */
  11.         unregister_chrdev(MAJOR(dev), "testchar");
  12. }
復(fù)制代碼
具體就不深入去分析了,你有空,可以跟進(jìn)去看看。都很簡(jiǎn)單,概括起來(lái)主要是三個(gè)方面,一是把已分配的一畝三分地(probe和char_device_struct)重置為空閑的地;二是把已經(jīng)分配的內(nèi)存注銷掉;三是kobj清理,切斷與文件系統(tǒng)inode的藕斷絲連。

四、總結(jié)

沒(méi)啥說(shuō)的了,就這樣吧。


字符設(shè)備驅(qū)動(dòng)模型淺析.pdf (197.56 KB, 下載次數(shù): 367)

論壇徽章:
0
3 [報(bào)告]
發(fā)表于 2010-07-19 19:56 |只看該作者
不錯(cuò){:3_185:}

論壇徽章:
1
天蝎座
日期:2013-10-23 21:11:03
4 [報(bào)告]
發(fā)表于 2010-07-19 20:07 |只看該作者
寫得不錯(cuò)

論壇徽章:
0
5 [報(bào)告]
發(fā)表于 2010-07-19 22:43 |只看該作者
不錯(cuò),頂!很適合像我這樣的新手。感謝lz

論壇徽章:
0
6 [報(bào)告]
發(fā)表于 2010-07-20 11:25 |只看該作者
lz其他的博文也很強(qiáng)啊,是不是考慮出本書(shū)了

論壇徽章:
0
7 [報(bào)告]
發(fā)表于 2010-07-20 20:37 |只看該作者
好東西,看看

論壇徽章:
0
8 [報(bào)告]
發(fā)表于 2010-07-22 18:26 |只看該作者
奇怪,dreamice兄,這篇文章咋沒(méi)評(píng)上原創(chuàng)文章呢?

論壇徽章:
0
9 [報(bào)告]
發(fā)表于 2010-07-23 18:36 |只看該作者
好東西`~哥大大的支持你多寫寫原創(chuàng)的。。有什么好的資料就給我發(fā)過(guò)來(lái)啊~{:3_181:}

論壇徽章:
0
10 [報(bào)告]
發(fā)表于 2010-09-03 14:27 |只看該作者
狠狠的MARK
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP