Chinaunix首页 | 论坛 | 博客
  • 博客访问: 257342
  • 博文数量: 35
  • 博客积分: 883
  • 博客等级: 准尉
  • 技术积分: 656
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-17 09:38
文章分类

全部博文(35)

文章存档

2013年(1)

2012年(34)

分类: LINUX

2012-05-06 15:54:40

    我们从字符设备驱动程序说起,注意,我们说的驱动程序。我们做个比喻,话说小明要去二叔家去串门,因为二叔家比较远,所以,需要做火车前去。去前,总要去银行取点银子,准备点盘缠和买点礼物啥的。小明大学计算机专业毕业几年,事业上进展还不错,买了房,又买了车。去银行取点钱,开私家车就可以了。
    小明打着火之后,不禁的开始注意起车的问题来?
首先,通常车子都是由固定的几个国有造车厂出品。当然,也有私有的,但造车的流程等类似,不累述。
其次,车子要有一个外壳、方向盘、中控及各种表盘仪器等各种提供给用户的外在界面接口。
最后,车子还要有发动机等驱动力,否则,车子没有办法跑起来。
而对应到我们的linux操作系统的字符设备上来,
首次,我们把单体的私家车比喻成一个字符设备,因为,字符设备相对来说比较简单,输送数据量不大,
      Linux系统默认提供几个标准的字符设备,用户也可以定制自己的字符设备。而正如我们的私家车
      一样,由有名的几个制造厂出台,年年都会出台一些经典的车型(款式不一定新,但受欢迎),用
      户也可以提出自己的要求,增加一些功能,若是您消费水平比较高,也可以定制几款车型。还有,
      很重要的就是,私家车一般载客量不大,一般不会超过5人。相对来说比较简单。
其次,正如车子有一些用户界面提供给用户驾驭车子一样,我们的字符设备驱动程序通常是通过设备文件
      的形式与用户打交道。而设备文件就是Linux内核提供给用户操作字符设备的一个用户界面。
最后,也是最核心的部分,就是设备驱动程序,正如车子一定要有发动机等引擎才能驱动一样,我们的字
      符设备也要有设备驱动程序才能工作。否则,就和没有了发动机等引擎的车子一样,变成一个空架
      子了。
好了,到此为止,我们知道了字符设备驱动的几个显著特点:简单,数据量不大,通过设备文件作为操作界面提供给用户,隐藏在操作界面后面的是真正的字符设备驱动程序。那么字符设备在Linux用什么来表示呢?
struct cdev {
 struct kobject kobj;
 struct module *owner;//防止所属的模块被从系统中删除掉,可以理解成防止我的车子被拖回报废厂被无缘无故的报废掉,所有,有个标记表明我们的车子现在还在用呢。
 const struct file_operations *ops;//字符设备提供的功能,可以想象成车子可以提供给你的功能。
 struct list_head list;
 dev_t dev;//设备号,可以想象成车牌号,是非常重要的资源。
 unsigned int count;
};
上面的结构是标准的linux内核中字符设备的表示,相关的关键字段的含义已经在旁边加以注释并做了适当的比喻。若是用户有特殊的要求,可以自己定义一个字符设备的结构,但,要把这个标准的结构嵌入到你的那个结构中,正如,你买回新车后,可以自己进行增加配置或者改装,但前提是一定要以你这个新车(cdev)为基础。前文我们也提到过,Linux内核可以想象成我们的政府,那么上面的cdev结构就相当于你在买车前对车子进行百般研究后,形成的一个抽象实体,这个抽象实体将来也是公安局对你的车子管理的一个重要内容。想想,要想最终有一辆自己的车子,车牌不能少吧,否则,上不了路多悲催呀;每个人都有对车子功能的定义,如有的人喜欢内饰好,功能多,有的人喜欢实用,不同的人要求不一样等等;而最重要的一项资源就是车牌号,北京、上海现在买个车子,弄个车号还需要摇号,想象一下这个资源有多么的重要。对linux系统来说,设备号是一项很重要的资源,是一个无符号的32位整数。
dev_t dev;
typedef __kernel_dev_t  dev_t;
typedef __u32 __kernel_dev_t;
不过,这个设备号在linux中有点特别,分为主、次设备号,而linux提供了下面三个宏方便你的使用。
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
在linux中,主、次设备号的功能分别是用来定位对应的设备驱动程序和由驱动程序使用,用来表示它所管理的若干同类设备。类比的想象一下,主设备号可以想象成车牌,而此设备号可以想象成在这个车牌下的其他的部件的号码,如,发动机等。这么重要的资源在现实生活中时通过摇号或者申请来获得的(当然,同各地车辆的所有量及限量有关),在linux中通过如下函数来申请获得。
int register_chrdev_region(dev_t from, unsigned count, const char *name)
from表示的是设备号,可以想象成车牌号
count表示是连续的设备编号的个数,可以想象成可以申请多个车牌号
name表示设备或者驱动的名称,可以想象成车子的命名或者车主名称。
这么看来,linux内核还是很开放的,提供了足够的自由度,让用户来获取资源。
前面说过了,设备号(车牌号)是一个很重要的,在linux系统中通过chrdevs这个全局数组来管理。定义如下:
static struct char_device_struct {
 struct char_device_struct *next;
 unsigned int major;
 unsigned int baseminor;
 int minorct;
 char name[64];
 struct cdev *cdev;  /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
#define CHRDEV_MAJOR_HASH_SIZE 255
从上面的宏我们可以看到linux中主设备号资源最多是255个,而且,这个数组是以hash表的形式进行组织的,每次在这次设备时,通过下面的内联函数对主设备号进行散列,散列后的值作为chrdevs的下标。
static inline int major_to_index(int major)
{
 return major % CHRDEV_MAJOR_HASH_SIZE;
}
这样一来,我们可以看到另外一面,就是当散列冲突时,例如:2和227可能散列到一个设备号上,这时,系统会比对次设备号,若次设备号不同,则,采用拉链的方式,将同一个主设备号的设备组成一个由小到大的链表。具体对应到内核函数的实现调用关系:register_chrdev_region--》__register_chrdev_region。代码很简单,在此,不累述。小明想想自己当初在申请车牌号的时候,公安局一定也是采用类似的方法,从系统中调出来车牌号的列表,然后,把自己报的车牌号进行一一比对,当没有重复的时候,才能有机会申请,否则,除了套牌,不可能申请成功。好了,车牌号(设备号)申请成功,接下来应当向系统(公安局)注册你的车辆信息,对应到字符设备驱动程序就是注册的过程,让内核知道你,便于对你的管理。此过程对应的内核函数cdev_add。这个函数实现也比较简单,在此不累述。我们要说就是一个关键的结构:cdev_map
struct kobj_map {
 struct probe {
  struct probe *next;
  dev_t dev;
  unsigned long range;
  struct module *owner;
  kobj_probe_t *get;
  int (*lock)(dev_t, void *);
  void *data;  //对应cdev结构,也就是具体设备,就是我们的车子
 } *probes[255];
 struct mutex *lock;
};
在内核中,通过cdev_map来统一管理注册进系统的设备。cdev_map同前面的设备号的管理完全相同,也是255,每注册一个设备就要申请一个probes结构,然后,通过data指向你的cdev,各个probe之间通过next进行链接,形成链表。
前面的手续都准备就绪了,剩下来的就是车子出厂或从4S店取车了,之后,你就可以随心所欲的驾驶着属于自己的车辆。这一部在linux中是通过mknod来实现的,通过mknod在文件系统中建立与你的设备号一致的设备文件节点,建立后,你就可以通过这个节点操作设备文件了,从而,间接地使用字符设备驱动程序。
好,到此,我们总结一下从开始想买车子到车子拿到可以正常上路的一个过程:
1、用户要定义出来自己想要什么样的车子,具体什么功能,想要什么样的车牌号;
2、将自己对车子功能的定义反馈给4S店,若你是高级用户,就直接反馈给厂家,让他们提供给你符合你的要求的车子
3、到公安局申请一个车牌号,否则不能上路
4、将申请成功的车牌号注册到公安局的车辆管理系统中。当然,这一步可能用户不用关心,主要是公安局来做的事情,但却是必不可少的内容。
5、前3步完成后,用户就可以从4S店取回车子,驾驭它了。
类比看看linux中,字符设备驱动程序标准的使用过程:
1、struct tty_driver *aa_driver;//定义字符设备结构
2、cdev_init(&aa_driver->cdev, &tty_fops);//初始化结构和功能列表
3、alloc_chrdev_region(&dev, aa_driver->minor_start,//分配设备号资源
      aa_driver->num, aa_driver->name);
4、cdev_init(&aa->cdev, &tty_fops);
5、mknod /dev/aa c 4,0
大家看看是不是一样的?到此,我们将字符设备的基本注册使用流程及内核中关键资源的管理大概说了一遍,希望能够让你明白。到这是不是就完事了呢?这个问题留给你:)
阅读(1904) | 评论(0) | 转发(4) |
给主人留下些什么吧!~~