首先我们在注册函数里面调用了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. Notepad++ 运行java(转)

    Notepad++ 运行java java, 2013/05/04, 9 replies, 6,007 views 文章目录 Notepad++ for java 安装必须的程序 配置NppExec ...

  2. Sql Server的艺术(五) SQL UNION与UNION JOIN运算符

    学习本节所用表: CREATE TABLE TEACHER ( ID INT IDENTITY (,) PRIMARY KEY , --主键,自增长 TNO INT NOT NULL, --教工号 T ...

  3. VS工程中的Windows.h

    才发现这个Windows.h是有些奥秘的,不是随便引用就可以的. 1,C++工程,头文件引用要讲求一定顺序.如果cpp文件先引用a.h,再引用b.h,则后者自动包含a.h.这一点很重要. 2,Wind ...

  4. CentOS 7.1静默安装11.2.0.3 64位单机数据库软件

    第1章 CentOS 7.1静默安装11.2.0.3 64位单机数据库软件 1.1  安装前的准备工作 1.1.1      软件准备 1.1.2      检查硬件 注意这里的内存应该满足要求,不然 ...

  5. 夏令营讲课内容整理 Day 6 Part 2.

    Day 6的第二部分,数论 数论是纯粹数学的分支之一,主要研究整数的性质   1.一些符号: a mod b 代表a除以b得到的余数 a|b a是b的约数 floor(x) 代表x的下取整,即小于等于 ...

  6. BZOJ 3932: [CQOI2015]任务查询系统 [主席树]

    传送门 题意: 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行),其优先级为Pi 调度系统会经常向查询系统询问,第Xi ...

  7. c++中对于json的key不带双引号的问题修复

    在引用了第三方数据时,数据源通过转义,将json的key上双引号给去掉了. 在PHP开发时,可以通过正则表达式替换方式来补充丢失的双引号,处理代码如下 function ex_json_decode( ...

  8. WPF Effect 造成的字体模糊

    WPF 里面有个Effect ,暂且可以理解为 "特效" 分类. 但是有时候使用不恰当,容易出现各种毛病. 例如: 代码如下: <StackPanel HorizontalA ...

  9. SpringMVC之使用requestMapping映射请求、映射参数、映射头

    1. 映射请求 作用:使用requestMapping可以指定处理器可以处理那些请求 地方:类和方法前面都可以 @requestMapping 类定义处: 提供初步的请求映射信息,相对于web应用的根 ...

  10. python爬虫(3)——SSL证书与Handler处理器

    一.SSL证书问题 上一篇文章,我们创建了一个小爬虫,下载了上海链家房产的几个网页.实际上我们在使用urllib联网的过程中,会遇到证书访问受限的问题. 处理HTTPS请求SSL证书验证,如果SSL证 ...