《LDD3》这本书中“字符设备驱动程序”一章有这样一段话:

只要cdev_add返回了,我们的设备就“活”了,它的操作就会被内核调用。

这里就研究一下cdev_add究竟如何让设备“活”过来,以及用户空间访问字符设备节点时,内核的处理流程。

从cdev_add开始分析

先从cdev_add()入手:

  1. int cdev_add(struct cdev *p, dev_t dev, unsigned count)
  2. {
  3. int error;
  4. p->dev = dev;
  5. p->count = count;
  6. error = kobj_map(cdev_map, dev, count, NULL,
  7. exact_match, exact_lock, p);
  8. if (error)
  9. return error;
  10. kobject_get(p->kobj.parent); /* 增加parent的引用计数 */
  11. return 0;
  12. }

这个函数除了增加parent的引用计数外,只有一个kobj_map()的函数调用,所以重要的操作应该都通过该函数进行;函数定义再drivers/base/map.c中:

  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);

cdev_map

结合cdev_add()调用该函数时传入的参数,发现一个重要的参数和数据结构,struct kobj_map类型的cdev_map,这是一个定义在fs/char_dev.c中的全局变量;先来看一下struct kobj_map的结构定义:

  1. struct kobj_map {
  2. struct probe {
  3. struct probe *next;
  4. dev_t dev;
  5. unsigned long range;
  6. struct module *owner;
  7. kobj_probe_t *get;
  8. int (*lock)(dev_t, void *);
  9. void *data;
  10. } *probes[255];
  11. struct mutex *lock;
  12. };

kobj_map中有一个指向struct probe结构的数组,长度为255,而struct probe结构中包含了设备号、模块的owner等信息。
cdev_map的初始化操作通过chrdev_init()进行,而该函数又直接调用了kobj_map_init():

  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;
  14. for (i = 0; i < 255; i++)
  15. p->probes[i] = base;
  16. p->lock = lock;
  17. return p;
  18. }

初始化过程非常简单,除了分配内存,还初始化了一个probe结构的base,并将cdev_mapprobes所有元素指向这个base,所以初始化后的cdev_map结构如下图:

字符设备访问流程 - 图1

kobj_map()

分析完cdev_map的初始化,继续回到kobj_map()函数;前面说到cdev_map被作为参数传递给kobj_map()函数,接下来分析一下kobj_map()的实现,注释后的代码如下:

  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) /* 虽然主设备号为12位,但是此处限制设备号范围最多跨255个主设备号 */
  10. n = 255;
  11. p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL); /* 每个主设备号一个probe结构 */
  12. if (p == NULL)
  13. return -ENOMEM;
  14. /* 逐个初始化probe结构 */
  15. for (i = 0; i < n; i++, p++) {
  16. p->owner = module;
  17. p->get = probe;
  18. p->lock = lock;
  19. p->dev = dev;
  20. p->range = range;
  21. p->data = data; /* 传入的data实际上是cdev结构的实例 */
  22. }
  23. mutex_lock(domain->lock);
  24. for (i = 0, p -= n; i < n; i++, p++, index++) {
  25. struct probe **s = &domain->probes[index % 255];
  26. while (*s && (*s)->range < range)
  27. s = &(*s)->next;
  28. p->next = *s;
  29. *s = p;
  30. }
  31. mutex_unlock(domain->lock);
  32. return 0;
  33. }

mutex_lock()之前的部分比较好理解,就是根据传入的参数,创建并设置了probe结构;mutex_lock()mutex_unlock()之间的部分需要单独分析:

  1. ……
  2. for (i = 0, p -= n; i < n; i++, p++, index++) {
  3. struct probe **s = &domain->probes[index % 255];
  4. while (*s && (*s)->range < range)
  5. s = &(*s)->next;
  6. p->next = *s;
  7. *s = p;
  8. }
  9. ……

在这个for循环中,index表示设备的主设备号,p表示一个probe的实例,这段代码主要作用就是将kobj_map结构的probes数组中的元素指向前面创建的probe实例。
循环中第一行,创建一个struct probe的指针指向kobj_map->probes中的某个位置,这个位置并不直接指向第index个位置,而是通过index%255计算得到,因为index是12位的主设备号,取值范围大于255,取模操作可以保证索引不会溢出。
接下来的while循环条件比较复杂,暂时先忽略while循环的内容;for循环中最后两句则是将probe[index%255]这个位置指向pp->next指向原来的值,最终cdev_map会得到这样的结构:

字符设备访问流程 - 图2

接着再看刚刚忽略的while循环;简单来说,这里的while循环作用就是将一条probe链上的probe实例按照range的值从小到大排序。
kobj_map_init()中这样一条语句,将baserange设置为unsigned long的最大值:

  1. base->range = ~0

这里我们以probes数组的第0个元素为例,此时probes[0]结构如下图:

字符设备访问流程 - 图3

假设此时插入一个range1probe实例到probes[0],此时(*s)->range = MAX > range = 1,所以结构会变为这样:

字符设备访问流程 - 图4

这种情况下,再插入一个range为2的probe实例,这时(*s)->range = 1 < range = 2,会进入到while循环中,执行s = &(*s)->next

字符设备访问流程 - 图5

执行完后,s将指向probe_0next域,而(*s)则指向base,此时(*s)->range = MAX > range = 2,离开while循环,最终得到如下结构:

字符设备访问流程 - 图6

所以最终得到的cdev_map中,每条probe链都是按range从小到大排序的,并且每条链的末尾都指向初始化时创建的base

字符设备的访问

前面分析完了cdev_map的初始化流程,到目前为止,cdev结构已经添加到cdev_map中,但是从用户空间访问设备节点时,如何找到对应的file_operations函数呢?

kobj_lookup()

drivers/base/map.c中还有一个重要的函数kobj_lookup,先来从这个函数进行分析:

  1. struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index);

从函数定义来看,这个函数的作用是根据设备号,从kobj_map中找到对应的probe,并从中返回对应driverkobject;看一下这个函数的实现部分:

  1. ……
  2. for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
  3. struct kobject *(*probe)(dev_t, int *, void *);
  4. struct module *owner;
  5. void *data;
  6. /* 设备号与当前probe不匹配,继续寻找 */
  7. if (p->dev > dev || p->dev + p->range - 1 < dev)
  8. continue;
  9. if (p->range - 1 >= best) /* 达到链表尾,退出循环 */
  10. break;
  11. if (!try_module_get(p->owner))
  12. continue;
  13. owner = p->owner;
  14. data = p->data;
  15. probe = p->get;
  16. best = p->range - 1;
  17. *index = dev - p->dev;
  18. if (p->lock && p->lock(dev, data) < 0) {
  19. module_put(owner);
  20. continue;
  21. }
  22. mutex_unlock(domain->lock);
  23. kobj = probe(dev, index, data); /* 使用kobj_map()时注册的probe函数获取kobj */
  24. /* Currently ->owner protects _only_ ->probe() itself. */
  25. module_put(owner);
  26. if (kobj)
  27. return kobj;
  28. goto retry;
  29. }
  30. ……

kobj_lookup()根据主设备号从kobj_map->probes中找到对应的链表进行遍历,并通过计算probe中设备号的范围来匹配正确的probe结构;而kobj_map()时注册的probe()函数则用来从data中获取kobject;对应到cdev_map上,data指向的是cdev结构,而probe()函数指针指向exact_match()函数:

  1. static struct kobject *exact_match(dev_t dev, int *part, void *data)
  2. {
  3. struct cdev *p = data;
  4. return &p->kobj;
  5. }

exact_match()的作用就是从data中获取kobject
既然获取到了kobject,那就可以使用container_of()获取对应的cdev结构,所以char_dev.c中一定有对应的函数会调用kobj_lookup()

chrdev_open()

经过搜索,在char_dev.c中找到了函数chrdev_open()调用kobj_lookup()

  1. /*
  2. * Called every time a character special file is opened
  3. */
  4. static int chrdev_open(struct inode *inode, struct file *filp)

从注释来看,这个函数在用户空间每次访问字符设备时调用;来看一下哪里会注册这个函数:

  1. const struct file_operations def_chr_fops = {
  2. .open = chrdev_open,
  3. .llseek = noop_llseek,
  4. };

chrdev_open()函数注册在def_chr_fops结构体中,继续搜索一下这个结构体会被赋值给谁:

  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. }
  8. ……
  9. }

fs/inode.c中的函数init_special_inode()里,def_chr_fops被赋值给了字符设备的inode结构体,这样在访问字符设备时,就会通过其inode访问到def_chr_fops->chrdev_open(),但是目前为止还没有调用我们自己为设备注册的file_operations,回过头来继续看chrdev_open()的实现:

  1. static int chrdev_open(struct inode *inode, struct file *filp)
  2. {
  3. const struct file_operations *fops;
  4. struct cdev *p;
  5. struct cdev *new = NULL;
  6. int ret = 0;
  7. spin_lock(&cdev_lock);
  8. p = inode->i_cdev;
  9. if (!p) {
  10. ……
  11. kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
  12. ……
  13. new = container_of(kobj, struct cdev, kobj);
  14. spin_lock(&cdev_lock);
  15. ……
  16. if (!p) {
  17. inode->i_cdev = p = new;
  18. list_add(&inode->i_devices, &p->list);
  19. new = NULL;
  20. } else if (!cdev_get(p))
  21. ret = -ENXIO;
  22. } else if (!cdev_get(p))
  23. ret = -ENXIO;
  24. ……
  25. fops = fops_get(p->ops);
  26. ……
  27. replace_fops(filp, fops);
  28. if (filp->f_op->open) {
  29. ret = filp->f_op->open(inode, filp);
  30. ……
  31. }
  32. return 0;
  33. ……
  34. }

函数实现经过精简后,非常容易看出实现过程:第一次访问时,inode->i_cdev为空,使用kobj_lookup()结合container_of()获取cdev,并将cdev赋值给inode->i_cdev,然后使用fops_get()获取我们设置的fops,再使用replace_fops()将我们的fops设置到filp文件指针上,然后调用filp->f_op->open(),至此成功访问到我们自己的open函数;当再次访问该字符设备时,inode->i_cdev已经被赋值,无需再次通过kobj_lookup()查找对应的cdev结构。