曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 10582|回复: 4
打印 上一主题 下一主题

主设备号与次设备号

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2009-8-8 21:29:27 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
字符设备通过文件系统上的 “名字” 进行访问。这些名字被称为特殊文件,或者是设备文件,还可以是文件系统树的简单结点

设备文件通常都在 /dev 目录下。如:
beyes@linux-beyes:~/C/kernel/memory> ll /dev |more
总计 0
crw-rw----  1 root uucp    470 04-14 18:16 ttyS6
crw-rw----  1 root uucp    471 04-14 18:16 ttyS7
crw-rw----  1 root tty         7,   0 08-08 18:58 vcs
crw-rw----  1 root tty         7,   1 08-08 18:58 vcs1
crw-rw-rw-  1 root root    1,   7 08-08 18:58 full
crw-rw-rw-  1 root root    1,   3 04-14 18:16 null
如上,前面第一个字符为  c 的表示字符设备。
在字符设备里,有主设备号和次设备号。如上,1,4,7 分别是主设备号,0,1,3,7,70,71都是次设备号。

一般的,主设备号标识出与设备关联的设备驱动。如 /dev/null 和 /dev/full 由 1 号驱动来管理,/dev/vcs 由 7 号驱动来管理,/dev/ttyS6 由 4 号驱动来管理。

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

内核由次设备号确定当前所指向的设备。根据所编写的驱动程序,可以从内核那里得到一个直接指向设备的指针,或者使用次设备号作为一个设备本地数组的索引。但不论如何,内核自身几乎不知道次设备号的什么事情。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
沙发
 楼主| 发表于 2009-8-8 23:24:03 | 只看该作者

设备号的内部表示

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

在 linux/types.h 头文件里定义有
typedef __kernel_dev_t          dev_t;
在 linux/posix_types.h 里定义有:
typedef int __kernel_key_t;

在自己编写的代码里,永远不要对这个设备号的组织形式做任何假设。因为,可以利用下面一组宏 ( 在 linux/kdev.h 中定义 )来获得主设备号和次设备号:
MAJOR (dev_t dev);
MINOR (dev_t dev);

在 <linux/kdev.h> 里可以看到
#define MINORBITS       20
#define MINORMASK       ((1U << MINORBITS) - 1)

#define MAJOR(dev)      ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)      ((unsigned int) ((dev) & MINORMASK))
由此可见, 设备号 dev 中,高 12 位为主设备号,低 20 位为次设备号。

如果已经确定了主设备号和次设备号,那么可以使用以下宏把主设备号和次设备号转换为 dev_t 类型:
MKDEV(int major, int minor);
比如主设备号为 4,次设备号为 17,那么就 MKDEV(4, 17);

同样,MKDEV 宏在 <linux/kdev.h> 里有定义:
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))

2.6 的内核可以容纳大量的设备,而以前版本的内核把主设备号和次设备号都限制为 255。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
板凳
 楼主| 发表于 2009-8-9 12:58:44 | 只看该作者

分配与释放设备号(register_chrdev_region, alloc_chrdev_region, unregister_chrd

在设置一个字符设备时,设备驱动第一件要做的事是要获取一个或者多个设备号。这项工作,由下面的函数来实现:
int register_chrdev_region(dev_t first, unsigned int count, const char *name);
这个函数在 <linux/fs.h> 中有声明。
参数说明
      first : 要分配设备编号范围的初始值( 次设备号通常为 0 )。
      count : 请求的连续设备编号范围(最大值)
      name : 与编号相关联的设备名称( 这会出现 /proc/devices 和  sysfs 里 )
需要注意的是,count 如果过大,就会溢出到下一个主设备号上。如果分配成功,函数返回 0 ,如果失败,则返回一负值。

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
地板
 楼主| 发表于 2009-9-24 11:35:53 | 只看该作者

次设备号的主要用途

1、区分设备驱动程序控制的实际设备;

2、区分不同用途的设备 (misc 系列设备)

3、区分块设备的分区 (partition)

通常,为了使应用程序区分所控制设备的类型,内核使用主设备号。而存在多台同类设备时,为了选择其中的一种,设备驱动程序就使用次设备号。



区分块设备的分区

块设备具有称为分区的分配领域。例如,硬件在物理上一台设备,在内核的角度,硬件被分为多个分区,而以这些分区为对象则形成了文件系统,此时,次设备号既表示设备,也表示分区。
brw-rw----  1 root disk    8,  16 2009-09-24 sdb
brw-rw----  1 root disk    8,  17 2009-09-24 sdb1
brw-rw----  1 root disk    8,  18 2009-09-24 sdb2
brw-rw----  1 root disk    8,  21 2009-09-24 sdb5
brw-rw----  1 root disk    8,  22 2009-09-24 sdb6
brw-rw----  1 root disk    8,  23 2009-09-24 sdb7
brw-rw----  1 root disk    8,  24 2009-09-24 sdb8

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
5#
 楼主| 发表于 2009-9-24 15:38:35 | 只看该作者

基于次设备号的文件处理方式

设备驱动程序的主设备号定义了设备提供的功能特性。相反,次设备号号可以定义功能特性,也可以用于区别设备控制的硬件。

设备文件是应用程序区分从内核调用设备驱动程序时,使用的主设备号和次设备号的信息文件。因此,应用程序打开设备文件时最先进行主设备相关的处理。应用程序打开设备文件后,内核就调用注册设备驱动程序时注册的 file_operations 结构体域的 open 域中定义的函数。由 open 函数运行与次设备号相关的 file_operations 的再处理操作。

利用次设备号,重新定义 file_operations 的常见例子为内核源代码中的 drivers/char/mem.c 。 该设备驱动程序的主设备号为 1 ,而次设备号决定设备驱动程序的特性。作为处理内存的设备驱动程序时,次设备号设置为 1 ,作为输入输出端口处理设备驱动程序时,次设备号设置为 4。



设备驱动程序中由主设备号再定义 file_operations 的典型结构如下:
struct file operations minor0_fops = {
    
      /* 次设备号为 1 时,处理的 file_operations */

     ... ....

}

struct file operations minor1_fops = {
    
      /*次设备号为 2 时,处理的 file_operations  */

     ... ....

}

int minor_open (struct inode *inode, struct file *filp)
{
             switch (MINOR (inode->i_rdev)) {
                          case 1:  
                                filp->f_op = &minor0_fops;
                                break;
                        
                           case 2:
                                 filp->f_op = &minor1_fops;
                                 break;

                           ... ...

                            default:
                                 return -ENXIO;
              }

                  if (filp->f_op && filp->f_op->open)
                          return filp->f_op->open (inode, filp);   /* 运行与实际次设备号相匹配的 open() 函数 */

                return 0;
}

struct file_operations master_fops = {
          .open = minor_open,
};


int xxx_init (void)
{
           int result;

           result = register_chrdev (MINOR_DEV_MAJOR, MINOR_DEV_NAME, &master_fops);
}

设备驱动程序的初始化函数中只有 open() 函数注册了 file_operations 。 该 file_operations ,即 open() 函数只在打开设备文件时使用。

初始化模块时,无法得知次设备号,只有在打开设备文件时,才能传送到设备驱动程序上,并由 open() 函数处理次设备号。在上面的 minor_open() 里,利用 MINOR 宏函数获取传送到 open() 函数上的次设备号后,使用可处理次设备号相关信息的 file_operations 结构体代替。
        switch (MINOR (inode->i_rdev)) {
                          case 1:  
                                filp->f_op = &minor0_fops;
                                break;
                        
                           case 2:
                                 filp->f_op = &minor1_fops;
                                 break;

                           ... ...

                            default:
                                 return -ENXIO;
              }
如上,通常在 filp->f_op 上代入次设备号相关的 file_operations 结构体地址。

应用程序打开设备文件后,通常代入 file->f_op 域上指定的 master_fops 的地址 (register_chrdev) ,此时只有打开次设备号相应设备文件的应用程序受到影响。

由于 file_operations 结构体也有可能没有定义 open() 函数域,因此先检查 open() 函数域是否正常,如:
     if (filp->f_op && filp->f_op->open)
                          return filp->f_op->open (inode, filp);

相关实例:http://www.groad.net/bbs/read.php?tid-1207.html
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-3 00:26 , Processed in 0.084791 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表