首先我们在注册函数里面调用了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. ArcGIS License启动无响应

    根据对网上的总结以及个人的经验,首先建议关闭电脑的防火墙以及杀毒软件 如果点击启动,依然没有反应,建议在dos下进行启动,命令为: lmgrd -z -c service.txt 如果需要深入研究,可 ...

  2. c#实现Word转换PNG图片

    由于项目需要,经过一些大神的指导以及github,stackOverflow找资料,写了个这么个程序. 主要是因为word文档有特殊字体,特殊字体处理就要用到EnhMetaFileBits,即获取一页 ...

  3. 生成模型(generative model)与判别模型(discriminative model)的区别

    监督学习可以分为生成方法与判别方法,所学到的模型可以分为生成模型与判别模型. 生成模型 生成模型由数据学习联合概率分布\(P(X,Y)\),然后求出条件概率分布\(P(Y|X)\)作为预测的模型,即生 ...

  4. Eclipse远程调试应用程序

    第一步,在应用程序的配置文件run.xml中加入下面的配置项,启动应用程序: <target name="run" depends="checkBuilderFai ...

  5. BZOJ 1856: [Scoi2010]字符串 [Catalan数]

    1856: [Scoi2010]字符串 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1418  Solved: 790[Submit][Status][ ...

  6. 学习H5仿制网站时遇到的问题

    学习H5的第二周,开始仿制网站,在本过程中也发现了自己存在的更多问题. 1.TOP块:在制作top中,遇到的第一个问题,一旦top左右的背景色不同该怎样去调整 解决方式:建立两个div取代一个div ...

  7. Python md5解密

    这篇文章原来在我盘里存了好久了~    16年9月的.   这 Python的长进没多少啊.现在都17.4了 哎~~ Python之POST提交解密MD5 用易语言写出来md5解密软件后感觉一点成就感 ...

  8. 关于CSS的外边距合并问题

    首先,需要明确的是只有普通文档流中块框的垂直外边距才会发生外边距合并.行内框.浮动框或绝对定位之间的外边距不会合并. 而在普通文档流中,这又分两种情况,分别是父子元素之间和相邻元素之间. <!D ...

  9. 【剑指offer28:字符串的排列】【java】

    题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. import ja ...

  10. 本地Git搭建并与Github连接

    本地Git搭建并与Github连接 git 小结 1.ubuntu下安装git环境 ubuntu 16.04已经自带git ,可以通过下列命令进行安装与检测是否成功安装 sudo apt-get in ...