内核怎么通过主设备号找驱动、次设备号找设备

本文深入解析Linux内核中主次设备号的管理机制,详细阐述了dev_t类型的定义及其在主次设备号之间的转换过程。通过内核全局哈希表的介绍,展示了如何根据主次设备号查找对应驱动程序。重点分析了memory_open函数的作用,以及它如何根据次设备号选择合适的file_operations。文章最后讨论了设备操作函数如何随次设备号的变化而变化,为理解Linux驱动机制提供了全面的视角。
摘要由CSDN通过智能技术生成

之前看韦东山老师视频,说到linux驱动就知道主设备号找驱动,次设备号找设备。这句到底怎么理解呢,如何在驱动中实现呢,在介绍该实现之前先看下内核中主次设备号的管理。在内核中,dev_t  类型( 在 <linux/types.h>头文件有定义 ) 用来表示设备号,包括主设备号和次设备号两部分。对于 2.6.x内核,dev_t是个32位量,其中高12位用来表示主设备号,低20位用来表示次设备号。

(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))   /*与次设备掩码与,得到次设备号*/</span>


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

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

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

    尽管这些从设备都涉及到内存访问,但所实现功能有很大差别。然后来看下图1中主设备号为1的memory_fops中定义了哪些函数指针。代码如下:

//driver/char/mem.c

static const struct file_operations memory_fops = {
  .open = memory_open,
  .llseek = noop_llseek,
};

仅有一个关键的函数 memory_open ,其作用是根据次设备号找到次设备的驱动程序。

static int memory_open(struct inode *inode, struct file *filp)
{// select different devices through minor device number commented by guoqingbo
  int minor;
  const struct memdev *dev;

  minor = iminor(inode); //get the minor device number commented by guoqingbo
  if (minor >= ARRAY_SIZE(devlist))
    return -ENXIO;

  dev = &devlist[minor];//select the specific file_operations
  if (!dev->fops)
    return -ENXIO;

  filp->f_op = dev->fops;
  if (dev->dev_info)
    filp->f_mapping->backing_dev_info = dev->dev_info;

  /* Is /dev/mem or /dev/kmem ? */
  if (dev->dev_info == &directly_mappable_cdev_bdi)
    filp->f_mode |= FMODE_UNSIGNED_OFFSET;

  if (dev->fops->open)  //open the device
    return dev->fops->open(inode, filp);

  return 0;
}


该函数用到的图 中的 dvlist 数组定义如下:

static const struct memdev {
  const char *name;
  mode_t mode;
  const struct file_operations *fops;
  struct backing_dev_info *dev_info;
} devlist[] = {
   [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
#ifdef CONFIG_DEVKMEM
   [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
#endif
   [3] = { "null", 0666, &null_fops, NULL },
#ifdef CONFIG_DEVPORT
   [4] = { "port", 0, &port_fops, NULL },
#endif
   [5] = { "zero", 0666, &zero_fops, &zero_bdi },
   [7] = { "full", 0666, &full_fops, NULL },
   [8] = { "random", 0666, &random_fops, NULL },
   [9] = { "urandom", 0666, &urandom_fops, NULL },
  [11] = { "kmsg", 0, &kmsg_fops, NULL },
#ifdef CONFIG_CRASH_DUMP
  [12] = { "oldmem", 0, &oldmem_fops, NULL },
#endif
};

    通过上面代码及图1可看出, memory_open 实际上实现了一个分配器(根据次设备号区分各个设备,并且选择适当的 file_operations ),图2说明了打开内存设备时,文件操作是如何改变的。 所涉及的函数逐渐反映了设备的具体特性。最初只知道用于打开设备的一般函数。然后由打开与内存相关设备文件的具体函数所替代。接下来根据选择的次设备号,进一步细化函数指针 ,为不同的次设备号最终选定函数指针。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂奔的乌龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值