首先我们在注册函数里面调用了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. window对象的属性

    还不是很了解,以后补充frames      opener       parent

  2. BZOJ 1180: [CROATIAN2009]OTOCI [LCT]

    1180: [CROATIAN2009]OTOCI Time Limit: 50 Sec  Memory Limit: 162 MBSubmit: 961  Solved: 594[Submit][S ...

  3. 计算机基础之Windows10操作系统安装U盘制作

    1.第一步,下载Windows10--ISO镜像(Windows7类似),下载站点: https://msdn.itellyou.cn/(百度搜索msdn即可),个人认为这是最干净的操作系统镜像站点, ...

  4. 让js调试更简单—console

    一.显示信息的命令 console.log 用于输出普通信息 console.info 用于输出提示性信息 console.error用于输出错误信息 console.warn用于输出警示信息 最常用 ...

  5. MIT-线性代数笔记(1-6)

    学习目录 第 01 讲 行图像和列图像 第 02 讲 矩阵消元 第 03 讲 矩阵的乘法和逆矩阵 第 04 讲 矩阵的LU 分解 第 05 讲 转置.置换和空间 第 06 讲 列空间和零空间 第 07 ...

  6. 机器学习实践之Logistic回归

        关于本文说明,本人原博客地址位于http://blog.csdn.net/qq_37608890,本文来自笔者于2017年12月17日 19:18:31所撰写内容(http://blog.cs ...

  7. ionic2+Angular 组件(多个组件)浅谈

    第一步,新建组件: ionic g component product-img-list 命令执行成功之后项目中生成的文件: 第二步:生成文件解析: ①product-img-list.ts impo ...

  8. 金融&业务常识积累

    前言 在项目中遇到很多名词,不太明白其含义.这些名词都是和金融领域紧密相关并且与项目的业务有着直接的联系.因此,决定通过搜集资料和归纳总结,对经后的工作产生一定的帮助. 常见的金融知识 PDL: Pa ...

  9. Nginx:413 Request Entity Too Large解决

    最近在做给博客添加上传PDF的功能,但是在测试上传文件的过程中遇到了413 Request Entity Too Large错误.不过这个无错误是很好解决的,这个错误的出现是因为上传的文件大小超过了N ...

  10. iOS通知传值的使用

    通知 是在跳转控制器之间常用的传值代理方式,除了代理模式,通知更方便.便捷,一个简单的Demo实现通知的跳转传值. 输入所要发送的信息 ,同时将label的值通过button方法调用传递, - (IB ...