一、引言

  很久前接触linux驱动知道主设备号找驱动,次设备号找设备。这句到底怎么理解呢,如何在驱动中实现呢,在介绍该实现之前先看下内核中主次设备号的管理:

  二、Linux内核主次设备号的管理

  Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux有约定俗成的编号,如终端类设备的主设备号是4。

  设备号的内部表示

  在内核中,dev_t  类型( 在 <linux/types.h>头文件有定义 ) 用来表示设备号,包括主设备号和次设备号两部分。对于 2.6.x内核,dev_t是个32位量,其中高12位用来表示主设备号,低20位用来表示次设备号。

  在 linux/types.h 头文件里定义有

  typedef __kernel_dev_t          dev_t;
  typedef __u32 __kernel_dev_t;

  主设备号和次设备号的获取

  为了写出可移植的驱动程序,不能假定主设备号和次设备号的位数。不同的机型中,主设备号和次设备号的位数可能是不同的。应该使用MAJOR宏得到主设备号,使用MINOR宏来得到次设备号。下面是两个宏的定义:(linux/kdev_t.h)

  #define MINORBITS   20                                 /*次设备号*/
  #define MINORMASK   ((1U << MINORBITS) - 1)            /*次设备号掩码*/
  #define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS))  /*dev右移20位得到主设备号*/
  #define MINOR(dev)  ((unsigned int) ((dev) & MINORMASK))   /*与次设备掩码与,得到次设备号*/

  MAJOR宏将dev_t向右移动20位,得到主设备号;MINOR宏将dev_t的高12位清零,得到次设备号。相反,可以将主设备号和次设备号转换为设备号类型(dev_t),使用宏MKDEV可以完成这个功能。

  #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

  MKDEV宏将主设备号(ma)左移20位,然后与次设备号(mi)相或,得到设备号

  三、主设备号找驱动、次设备号找设备的内核实现

  Linux内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。

  内核维护着一个以主设备号为key的全局哈希表,而哈希表中数据部分则为与该主设备号设备对应的驱动程序(只有一个次设备)的指针或者多个同类设备驱动程序组成的数组的指针(设备共享主设备号)。根据所编写的驱动程序,可以从内核那里得到一个直接指向设备驱动的指针,或者使用次设备号作为索引的数组来找到设备驱动程序。但无论哪种方式,内核自身几乎不知道次设备号的什么事情。如下图所示:

图1:应用程序调用open时通过主次设备号找到相应驱动