泰山派3M-RK3576-Linux内核驱动教程-Linux驱动基础-字符驱动设备-设备号详解

02.设备号详解

一、为什么需要设备号

在上一节中,已经看到 /dev 目录下包含大量设备文件。例如:

ls -l /dev/null /dev/tty0 /dev/mmcblk0

示例输出:

crw-rw-rw- 1 root root   1,   3 12月  9 19:02 /dev/null
crw--w---- 1 root tty    4,   0 12月  9 19:02 /dev/tty0
brw-rw---- 1 root disk 179,   0 12月  9 19:02 /dev/mmcblk0

其中有两个重要信息:

  • 第一列首字符:c 表示字符设备,b 表示块设备。
  • 倒数第三、倒数第二列两个数字:例如 1, 34, 0179, 0

这两个数字组合在一起,构成该设备的设备号(device number)

  • 前一个数字是主设备号(major number)
  • 后一个数字是次设备号(minor number)

当用户空间程序执行:

int fd = open("/dev/tty0", O_RDWR);

内核需要根据 /dev/tty0 找到对应的驱动以及具体设备实例。 设备号在这里起到了桥梁作用:

  • 通过主设备号确定由哪个驱动负责处理;
  • 通过次设备号确定该驱动下的哪一个设备实例。

可以概括为:

设备号用于实现 “ 设备文件 ↔ 驱动程序 ↔ 具体设备实例 ” 的映射。

二、主设备号与次设备号

1. 主设备号(major number)

主设备号的作用是标识设备所属的驱动或设备类别

  • 在内核内部,字符设备子系统会维护按主设备号索引的表结构。
  • 每个主设备号与某个驱动(或某个子系统)相关联。
  • 当内核解析 /dev/xxx 的设备号时,会根据主设备号找到对应驱动的入口。

例如(以 /proc/devices 为例):

cat /proc/devices

输出(节选):

lckfb@linaro-alip:~$ cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 81 video4linux
 89 i2c
 90 mtd
.......................

说明:

  • 字符设备主设备号 4 对应 tty 类设备。
  • 块设备主设备号 13 对应 input 设备(鼠标/键盘)。

主设备号的范围和分配策略由内核维护,部分主设备号在文档中有约定用途,其他范围可供动态分配给各种驱动。

2. 次设备号(minor number)

次设备号用于标识同一主设备号下的不同设备实例

  • 一个驱动(对应某个主设备号)可以管理多路设备。
  • 每一路设备具有不同次设备号,用于区分。

例如,假设某字符设备驱动使用主设备号 240,并管理两个实例:

  • /dev/mychar0:主设备号 240,次设备号 0 → <240, 0>
  • /dev/mychar1:主设备号 240,次设备号 1 → <240, 1>

驱动在处理 open / read / write 等操作时,可以根据次设备号选择对应的数据结构或硬件资源。

我们继续来看 tty 串口的例子:

lckfb@linaro-alip:~$ ls -l /dev/tty*
crw-rw-rw- 1 root  tty       5,  0 12月 9日 19:02 /dev/tty
crw--w---- 1 root  tty       4,  0 12月 9日 19:02 /dev/tty0
crw--w---- 1 root  tty       4,  1 12月 9日 19:02 /dev/tty1
crw--w---- 1 root  tty       4, 10 12月 9日 19:02 /dev/tty10
crw--w---- 1 root  tty       4, 11 12月 9日 19:02 /dev/tty11
crw--w---- 1 root  tty       4, 12 12月 9日 19:02 /dev/tty12
crw--w---- 1 root  tty       4, 13 12月 9日 19:02 /dev/tty13
crw--w---- 1 root  tty       4, 14 12月 9日 19:02 /dev/tty14
crw--w---- 1 root  tty       4, 15 12月 9日 19:02 /dev/tty15
........

我们的串口资源有这么多同一个串口类型的设备,可能有几十上百个,主次结合就能很快定位:

  • 主设备号确定设备的类型为串口设备
  • 次设备号确定为串口设备下面的那个设备

tty10

  • 主设备号4
  • 次设备号10

tty11

  • 主设备号4
  • 次设备号11

tty12

  • 主设备号4
  • 次设备号12

……

三、设备号的数据类型与布局

在内核中,设备号使用 dev_t 类型表示。 在多数架构上,它是一个 32 位无符号整数,其位布局大致为:

  • 高 12 位:主设备号
  • 低 20 位:次设备号

即:

  • 主设备号理论范围:0 ~ 2¹² - 1(0 ~ 4095)
  • 次设备号理论范围:0 ~ 2²⁰ - 1(0 ~ 1,048,575)

内核提供了一组宏用于操作 dev_t

MAJOR(dev_t dev);  // 从 dev 中取得主设备号
MINOR(dev_t dev);  // 从 dev 中取得次设备号
MKDEV(unsigned int major, unsigned int minor); // 构造 dev_t

典型用法示例:

dev_t dev;

/* 由主设备号 240、次设备号 0 构造一个 dev_t */
dev = MKDEV(240, 0);

/* 提取主、次设备号 */
unsigned int ma = MAJOR(dev);
unsigned int mi = MINOR(dev);

在字符设备驱动中,通常会定义:

static dev_t dev_num;  // 完整设备号
static int major;      // 主设备号
static int minor = 0;  // 起始次设备号

四、设备号的两种分配

在编写字符设备驱动时,驱动需要向内核登记自己要使用的设备号范围。 这一过程与后续的 cdev 注册、/dev 节点创建是分离的,本节仅讨论设备号本身的注册与释放。

内核提供了两种常用方式:

  1. 静态注册(指定主设备号):register_chrdev_region
  2. 动态分配(自动分配主设备号):alloc_chrdev_region

1. 静态注册

驱动可以直接指定期望使用的主设备号和起始次设备号,然后向内核注册:

static dev_t dev_num;
static int major = 240;  /* 希望使用主设备号 240 */
static int minor = 0;    /* 起始次设备号 */

static int __init mydrv_init(void)
{
    int ret;

    dev_num = MKDEV(major, minor);

    /* 注册从 dev_num 开始,连续 1 个设备号 */
    ret = register_chrdev_region(dev_num, 1, "mychar_static");
    if (ret < 0) {
        pr_err("register_chrdev_region failed\n");
        return ret;
    }

    pr_info("mychar_static: registered with major=%d, minor=%d\n",
            MAJOR(dev_num), MINOR(dev_num));

    return 0;
}

static void __exit mydrv_exit(void)
{
    unregister_chrdev_region(dev_num, 1);
}

说明:

  • register_chrdev_region(dev_t from, unsigned count, const char *name);
  • from:起始设备号(包含主、次设备号)。
  • count:连续设备号的数量。
  • name:名称,用于 /proc/devices 等位置显示。
  • 对于字符设备,主设备号必须尚未被其他驱动使用。

适用场景

  • 某些需要固定主设备号的旧系统或特定应用场景。

局限性

  • 需要开发者手动选择主设备号,并确保不与系统中已有设备冲突。
  • 在不同内核版本或不同平台上,该主设备号可能已经被占用。

对于一般新开发的驱动,更推荐使用动态分配方式。

2. 动态分配

动态分配由内核选择尚未被使用的主设备号,驱动只需指定:

  • 起始次设备号;
  • 需要的连续设备数量;
  • 名称。
static dev_t dev_num;
static int major;
static int minor = 0;

static int __init mydrv_init(void)
{
    int ret;

    /* 请求动态分配 1 个设备号,从次设备号 0 开始 */
    ret = alloc_chrdev_region(&dev_num, minor, 1, "mychar_dynamic");
    if (ret < 0) {
        pr_err("alloc_chrdev_region failed\n");
        return ret;
    }

    major = MAJOR(dev_num);
    minor = MINOR(dev_num);

    pr_info("mychar_dynamic: registered with major=%d, minor=%d\n",
            major, minor);

    return 0;
}

static void __exit mydrv_exit(void)
{
    unregister_chrdev_region(dev_num, 1);
}

说明:

  • alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
  • dev:返回的起始 dev_t
  • baseminor:起始次设备号。
  • count:从 baseminor 开始,连续申请多少个次设备号。
  • name:设备名称标识。

优点

  • 不需要手动管理主设备号的分配。
  • 跨平台、跨内核版本时更不易产生冲突。
  • 是新驱动开发中推荐的做法。
一款轻量而功能强大的点云可视化和编辑软件,支持pcd, ply, las等多种格式,轻松打开海量点云数据,支持多方式多字段渲染点云,对点进行方便的查询、量测和编辑,提供了地面滤波算法,可应用于测绘、高精地图、SLAM等领域。 PCDViewer是一款专业的点云数据处理软件,特别适用于处理和编辑大规模点云数据。该软件支持多种点云文件格式,包括pcd、ply和las等,这些格式广泛应用于激光雷达扫描数据、三维建模以及其他测绘技术。PCDViewer的强大之处在于其轻量级的系统要求与丰富的功能集,使得用户可以在Windows、Ubuntu等操作系统上轻松运行软件,高效地处理海量点云数据。 这款软件的一个主要特点是其多方式多字段渲染点云的能力。这允许用户根据不同的属性,如颜色、强度、高度等,对点云进行视觉上的分类和区分,从而更直观地分析和理解点云数据。此外,PCDViewer还提供了方便的查询、量测和编辑功能,允许用户直接对点云数据进行操作,诸如添加注释、删除噪声点或进行精确测量等,极大地提高了工作效率。 软件还内置了地面滤波算法,这一功能对于测绘学、地理信息系统(GIS)以及机器人导航和定位(SLAM)等领域尤为关键。地面滤波算法能够从点云数据中分离出地面点和非地面点,这对于如道路建模、地形分析、植被测量等应用来说至关重要。通过分离地面点,可以更准确地进行地面建模和地形特征分析,为自动化系统提供清晰的环境地图。
内容概要:本文提出了一种计及并网波动约束和储能荷电状态(SOC)的混合储能功率协调控制方法,并提供了完整的Matlab代码实现。该方法针对可再生能源并网系统中存在的功率波动问题,采用锂电池与超级电容构成的混合储能系统进行功率平抑,通过低通滤波与动态时间常数调节实现高频/低频功率分量的合理分配,同时引入SOC反馈控制机制,实时调节功率分配系数,确保各储能单元的荷电状态维持在安全范围内,避免过充过放,从而在满足并网功率波动标准的同时,延长储能系统使用寿命。文中详细阐述了控制策略的设计原理、关键参数整定方法及仿真验证过程,展示了该方法在平抑功率波动和均衡储能SOC方面的优越性能。; 适合人群:具备电力系统、新能源并网或储能控制基础知识的研究生、科研人员及从事相关领域工程开发的技术人员。; 使用场景及目标:①研究混合储能系统在平抑风电/光伏并网功率波动中的应用;②掌握基于SOC反馈的储能功率协调控制策略设计方法;③学习Matlab/Simulink在电力电子与电力系统仿真中的建模与分析技巧;④为撰写学术论文或完成科研项目提供可复现的技术方案与代码参考。; 阅读建议:建议结合Matlab代码逐行理解控制逻辑,重点关注低通滤波与SOC反馈环节的实现方式,并尝试调整参数观察系统响应变化,以深入掌握控制策略的动态特性与优化思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值