首先我们在注册函数里面调用了register_chrdev(MEM_MAJOR,"mem",&memory_fops),向内核注册了一个字符设备。

第一个参数是主设备号,0代表动态分配,这里的MEM_MAJOR是1。第二个参数是设备的名字,第三个参数是文件操作指针。

每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致,否则用户进程将无法访问到设备驱动程序。

一般说来,PCI卡通常都属于字符设备

完成注册后,在/proc/devices中的第一个字符设备我们就看到了:1 mem。

1.前面提到了注册,那这个字符设备到底注册到哪里去了呢?这是要弄明白的第一个问题。

其实是注册到一个存放字符设备的链表中了:

fs/char_dev.c

  1. static struct char_device_struct {
  2. struct char_device_struct *next;
  3. unsigned int major;
  4. unsigned int baseminor;
  5. int minorct;
  6. char name[64];
  7. struct cdev *cdev;      /* will die */
  8. } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

这里的CHRDEV_MAJOR_HASH_SIZE的大小是255,也就是这里最多能够存放的主设备号最多有255个。

这里还是一点需要注意的是这里分配的是一个指针数组。

  1. cd->next = *cp;
  2. *cp = cd;

首先*cp代表的是当前相同的主设备号中最后面的一个,当然这这里的*cp是指向NULL的,然后把*cp更新为cd。

可以注意到这里好像并没有struct cdev什么事情,下面就对其进行初始化。

2.cdev_add第二个任务,add a char device to the system,make it live immediately

先介绍两个遇到的结构体:

include/linux/cdev.h

  1. struct cdev {
  2. struct kobject kobj;
  3. struct module *owner;
  4. const struct file_operations *ops;
  5. struct list_head list;
  6. dev_t dev;
  7. unsigned int count;
  8. };

可进行的操作有:

  1. void cdev_init(struct cdev *, const struct file_operations *);
  2. struct cdev *cdev_alloc(void);
  3. void cdev_put(struct cdev *p);
  4. int cdev_add(struct cdev *, dev_t, unsigned);
  5. void cdev_del(struct cdev *);
  6. void cd_forget(struct inode *);

在结构体cdev里出现了另外一个极其重要的结构体struct kobject,include/linux/kobject.h

  1. struct kobject {
  2. const char      *name;
  3. struct list_head    entry;
  4. struct kobject      *parent;
  5. struct kset     *kset;
  6. struct kobj_type    *ktype;
  7. struct sysfs_dirent *sd;
  8. struct kref     kref;
  9. unsigned int state_initialized:1;
  10. unsigned int state_in_sysfs:1;
  11. unsigned int state_add_uevent_sent:1;
  12. unsigned int state_remove_uevent_sent:1;
  13. unsigned int uevent_suppress:1;
  14. };

主要的操作有:

  1. extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
  2. extern void kobject_del(struct kobject *kobj);
  3. extern struct kobject *kobject_get(struct kobject *kobj);
  4. extern void kobject_put(struct kobject *kobj);extern void kobject_put(struct kobject *kobj);

cdev_add里面只调用了一个函数:kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);

cdev_map是fs/char_dev.h里定义的一个结构体变量,而kobj_map的作用就是初始化它。

  1. static struct kobj_map *cdev_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:

内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。

  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. p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);
  12. if (p == NULL)
  13. return -ENOMEM;
  14. for (i = 0; i < n; i++, p++) {
  15. p->owner = module;
  16. p->get = probe;
  17. p->lock = lock;
  18. p->dev = dev;
  19. p->range = range;
  20. p->data = data;
  21. }
  22. mutex_lock(domain->lock);
  23. for (i = 0, p -= n; i < n; i++, p++, index++) {
  24. struct probe **s = &domain->probes[index % 255];
  25. while (*s && (*s)->range < range)
  26. s = &(*s)->next;
  27. p->next = *s;
  28. *s = p;
  29. }
  30. mutex_unlock(domain->lock);
  31. return 0;
  32. }

现在完成的仅仅是注册,下面还有一些重要的事情需要完成。

linux驱动---字符设备的注册register_chrdev说起的更多相关文章

  1. liunx驱动之字符设备的注册

    上一篇文章学习了如何编写linux驱动,通过能否正常加载模块进行验证是否成功,有做过liunx应用开发的小伙伴都知道驱动会在'/dev'目录下以文件的形式展现出来,所以只是能加载驱动模块不能算是完成驱 ...

  2. linux kernel 字符设备详解

    有关Linux kernel 字符设备分析: 参考:http://blog.jobbole.com/86531/ 一.linux kernel 将设备分为3大类,字符设备,块设备,网络设备. 字符设备 ...

  3. 【驱动】linux设备驱动·字符设备驱动开发

    Preface 前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段! <linux设备驱动入门篇>:http://infohacker.blog.51cto.com/6751 ...

  4. Linux高级字符设备驱动

    转载:http://www.linuxidc.com/Linux/2012-05/60469p4.htm 1.什么是Poll方法,功能是什么? 2.Select系统调用(功能)      Select ...

  5. linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】

    转自:http://my.oschina.net/u/274829/blog/285014 1,ioctl介绍 ioctl控制设备读写数据以及关闭等. 用户空间函数原型:int ioctl(int f ...

  6. Linux高级字符设备驱动 poll方法(select多路监控原理与实现)

    1.什么是Poll方法,功能是什么? 2.Select系统调用(功能)      Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程.      int selec ...

  7. linux内核cdev_init系列函数(字符设备的注册)

    内核中每个字符设备都对应一个 cdev 结构的变量,下面是它的定义: linux-2.6.22/include/linux/cdev.h struct cdev {    struct kobject ...

  8. linux学习--字符设备驱动

    目录 1.字符设备驱动抽象结构 2.设备号及设备节点 2.1 设备号分配与管理 2.2 设备节点的生成 3.打开设备文件 linux驱动有基本的接口进行注册和卸载,这里不再做详细说明,本文主要关注li ...

  9. Linux LED字符设备驱动

    // 申请IO资源 int gpio_request(unsigned gpio, const char *label); // 释放IO资源 void gpio_free(unsigned gpio ...

随机推荐

  1. linux的8小时差问题解决

    下面是同步时间的解决方法: 1.vi /etc/sysconfig/clock #编辑文件 ZONE="Asia/Shanghai" UTC=false #设置为false,硬件时 ...

  2. Python之算法

    一.什么算法 算法:一个计算过程,解决问题的方法 二.时间复杂度 看代码:                                                               ...

  3. WPF之ViewModel

    写之前我先唠叨两句,现在公司要求用wpf写个客户端,我之前玩过winform,没怎么用wpf写过正经项目,我接手这个wpf项目之后就开始研究这个东西,MVVM,自定义控件,等等.可能是winfrom先 ...

  4. slick对超过22个属性的表进行映射的两种办法

    版权声明:本文为博主原创文章,未经博主允许不得转载 slick是scala的一个FRM(Functional Relational Mapper)框架,即函数式的关系数据库编程工具库.使用slick不 ...

  5. ABP官方文档翻译 8.1 通知系统

    通知系统 介绍 发送模型 通知类型 通知数据 通知严重性 关于通知持久化 订阅通知 发布通知 用户通知管理 实时通知 客户端 通知存储 通知定义 介绍 在系统中通知用来基于特定的事件告知用户.ABP提 ...

  6. JavaBean命名规范

    ———————————————————————————————————————————————————————— 属性名/类型                    |                 ...

  7. 洛谷 [P1578] WC2002 奶牛浴场

    本题是一道用极大化思想求最大子矩阵的经典题目.这个题目很出名,可以在百度搜索王知昆国家队dalao的论文,其中说的非常详细. 先枚举极大子矩形的左边界,然后从左到右依次扫描每一个障碍点,并不断修改可行 ...

  8. 济南清北学堂游记 Day 2.

    在大佬云集的地方被直线碾压是什么样的体验? 大概就是210和1030的差别. 大概就是高质量机械键盘和空气的区别. 回来的路上,我一直在想,我到底是不是一个高三的? 大概也是能找到以前在家和学校训练时 ...

  9. BZOJ 2754: [SCOI2012]喵星球上的点名 [AC自动机+map+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1902  Solved: 837[Submit][St ...

  10. js 中的一些小技巧

    js 数字操作: 1.1 取整: 取整有很多方法如: parseInt(a,10); Math.floor(a); a>>0; ~~a; a|0; 前面2种是经常用到的,后面3中算是比较偏 ...