首先我们在注册函数里面调用了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. Git Batch命令(转)

    echo 和 @ 回显命令 @ #关闭单行回显 echo off #从下一行开始关闭回显 @echo off #从本行开始关闭回显.一般批处理第一行都是这个 echo on #从下一行开始打开回显 e ...

  2. python监控微信报警

    微信接口调用代码: #coding=utf8 import itchat from flask import Flask, request itchat.auto_login(enableCmdQR= ...

  3. 奇怪的问题0xc000007b

    自己的程序运行debug版本正常,运行release版本main函数还未进入就弹出异常. 令我感觉很奇怪. 觉得应该就是动态库哪里有问题. 仔细检查,发现并没有缺什么动态库.也不存在版本问题. 最后我 ...

  4. C#使用Redis

    一,引入dll 1.ServiceStack.Common.dll 2.ServiceStack.Interfaces.dll 3.ServiceStack.Redis.dll 4.ServiceSt ...

  5. 02_Python基本数据类型

    一.什么是数据 数据是描述客观事物的字符(比如95,不同的语义可表示成绩或体重),是计算机可以操作的对象,能够被计算机识别并输入给计算机处理的符号集合. 数据不仅仅包含整形,还包括图像.音乐.视频等非 ...

  6. HTML图片上下之间的空隙是什么原因

    在这个问题上,<权威指南>该书第三版第 146 页有明确说到: 如果一个垂直元素没有基线——也就是说,这是一个图像或表单输入元素,或者其它替换元素——那么该元素低端与其父元素的基线对齐.这 ...

  7. Azure Automation (5) 调整Azure SQL Database DTU

    <Windows Azure Platform 系列文章目录> 之前有客户提了需求,在9点以后,把Azure SQL Database DTU提高 在凌晨,把Azure SQL Datab ...

  8. 2018/1/27 Zookeeper实现分布式锁

    public class DistributedClient { // 超时时间 private static final int SESSION_TIMEOUT = 5000; // zookeep ...

  9. zend framework框架学习走起——从零开始,点击记录-安装

    zend framework第一步,先来安装下zend framework框架. 先介绍下,我这边的php配置环境,为图省事,我是安装wampserver来搭载php环境的,,php+Apache+m ...

  10. Centos 7系统优化脚本

    脚本如下,后续继续优化 #!/bin/bash #author junxi by #this script is only for CentOS 7.x #check the OS platform= ...