linux主编号的动态分配

一些主设备编号是静态分派给最普通的设备的. 一个这些设备的列表在内核源码树的 Documentation/devices.txt 中. 分配给你的新驱动使用一个已经分配的静态编号的机会 很小, 但是, 并且新编号没在分配. 因此, 作为一个驱动编写者, 你有一个选择: 你可以 简单地捡一个看来没有用的编号, 或者你以动态方式分配主编号. 只要你是你的驱动的唯 一用户就可以捡一个编号用; 一旦你的驱动更广泛的被使用了, 一个随机捡来的主编号将 导致冲突和麻烦.

因此, 对于新驱动, 我们强烈建议你使用动态分配来获取你的主设备编号, 而不是随机选 取一个当前空闲的编号. 换句话说, 你的驱动应当几乎肯定地使用 alloc_chrdev_region, 不是 register_chrdev_region.

动态分配的缺点是你无法提前创建设备节点, 因为分配给你的模块的主编号会变化. 对于 驱动的正常使用, 这不是问题, 因为一旦编号分配了, 你可从 /proc/devices 中读取 它.[6]6

为使用动态主编号来加载一个驱动, 因此, 可使用一个简单的脚本来代替调用 insmod, 在 调用 insmod 后, 读取 /proc/devices 来创建特殊文件.

一个典型的 /proc/devices 文件看来如下:

Character devices: 1 mem

2  pty

3  ttyp

4  ttyS

6  lp

7  vcs

10 misc

13  input

14  sound

21 sg

180 usb

Block devices: 2 fd

8 sd

11 sr

65  sd

66  sd

6  [6]

从 sysfs 中能获取更好的设备信息, 在基于 2.6 的系统通常加载于 /sys. 但是使 scull 通过 sysfs 输出信息超

出了本章的范围; 我们在 14 章中回到这个主题.

因此加载一个已经安排了一个动态编号的模块的脚本, 可以使用一个工具来编写, 如 awk , 来从 /proc/devices 获取信息以创建 /dev 中的文件.

下面的脚本, snull_load, 是 scull 发布的一部分. 以模块发布的驱动的用户可以从系统 的 rc.local 文件中调用这样一个脚本, 或者在需要模块时手工调用它.

#!/bin/sh module="scull" device="scull" mode="664"

# invoke insmod with all arguments we got

# and use a pathname, as newer modutils don't look in . by default

/sbin/insmod ./$module.ko $* || exit 1

# remove stale nodes

rm -f /dev/${device}[0-3]

major=$(awk "\$2=="$module" {print \$1}" /proc/devices) mknod /dev/${device}0 c $major 0

mknod /dev/${device}1 c $major 1 mknod /dev/${device}2 c $major 2

mknod /dev/${device}3 c $major 3

# give appropriate group/permissions, and change the group.

# Not all distributions have staff, some have "wheel" instead. group="staff"

grep -q '^staff:' /etc/group || group="wheel"

chgrp $group /dev/${device}[0-3] chmod $mode /dev/${device}[0-3]

这个脚本可以通过重定义变量和调整 mknod 行来适用于另外的驱动. 这个脚本仅仅展示了 创建 4 个设备, 因为 4 是 scull 源码中缺省的.

脚本的最后几行可能有些模糊:为什么改变设备的组和模式? 理由是这个脚本必须由超级用 户运行, 因此新建的特殊文件由 root 拥有. 许可位缺省的是只有 root 有写权限, 而任 何人可以读. 通常, 一个设备节点需要一个不同的存取策略, 因此在某些方面别人的存取 权限必须改变. 我们的脚本缺省是给一个用户组存取, 但是你的需求可能不同. 在第 6 章 的"设备文件的存取控制"一节中, sculluid 的代码演示了驱动如何能够强制它自己的对设 备存取的授权.

还有一个 scull_unload 脚本来清理 /dev 目录并去除模块.

作为对使用一对脚本来加载和卸载的另外选择, 你可以编写一个 init 脚本, 准备好放在 你的发布使用这些脚本的目录中. [7] 7作为 scull 源码的一部分, 我们提供了一个相当完整

和可配置的 init 脚本例子, 称为 scull.init; 它接受传统的参数 -- start, stop, 和 restart -- 并且完成 scull_load 和 scull_unload 的角色.

如果反复创建和销毁 /dev 节点, 听来过分了, 有一个有用的办法. 如果你在加载和卸载 单个驱动, 你可以在你第一次使用你的脚本创建特殊文件之后, 只使用 rmmod 和 insmod: 这样动态编号不是随机的. [8]8并且你每次都可以使用所选的同一个编号, 如果你不加载任 何别的动态模块. 在开发中避免长脚本是有用的. 但是这个技巧, 显然不能扩展到一次多 于一个驱动.

安排主编号最好的方式, 我们认为, 是缺省使用动态分配, 而留给自己在加载时指定主编 号的选项权, 或者甚至在编译时. scull 实现以这种方式工作; 它使用一个全局变量, scull_major, 来持有选定的编号(还有一个 scull_minor 给次编号). 这个变量初始化为 SCULL_MAJOR, 定义在 scull.h. 发布的源码中的 SCULL_MAJOR 的缺省值是 0, 意思是"使 用动态分配". 用户可以接受缺省值或者选择一个特殊主编号, 或者在编译前修改宏定义或 者在 insmod 命令行指定一个值给 scull_major. 最后, 通过使用 scull_load 脚本, 用 户可以在 scull_load 的命令行传递参数给 insmod.[9]9

这是我们用在 scull 的源码中获取主编号的代码:

if (scull_major) {

dev = MKDEV(scull_major, scull_minor);

result = register_chrdev_region(dev, scull_nr_devs, "scull");

} else {

result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev);

}

if (result < 0) {

printk(KERN_WARNING "scull: can't get major %d ", scull_major); return result;

}

本书使用的几乎所有例子驱动使用类似的代码来分配它们的主编号.

原文地址:https://www.cnblogs.com/fanweisheng/p/11138546.html