udev 和模块加载的关系

本文深入探讨了内核驱动程序的注册机制,包括内建驱动与模块驱动的区别,以及Udev如何在Linux系统中创建设备节点。重点介绍了如何在系统启动时加载未编入内核的模块,并详细解释了热插拔事件处理过程。

1 对于已经编入内核的驱动程序

   当被内核检测到的时候,会直接在 sysfs中注册其对象;对于编译成模块的驱动程序,当模块载入的时候才会这样做。一旦挂载了sysfs 文件系统(挂载到 /sys),内建的驱动程序在 sysfs注册的数据就可以被用户空间的进程使用,并提供给 udev 以创建设备节点。
   udev 初始化脚本负责在 Linux 启动的时候创建设备节点,该脚本首先将/sbin/udevsend 注册为热插拔事件处理程序。热插拔事件(随后将讨论)本不应该在这个阶段发生,注册 udev 只是为了以防万一。然后udevstart 遍历 /sys 文件系统,并在 /dev 目录下创建符合描述的设备。例如,/sys/class/tty/vcs/dev 里含有"7:0"字符串,udevstart就根据这个字符串创建主设备号为 7 、次设备号为 0 的 /dev/vcs 设备。udevstart 创建的每个设备的名字和权限由 /etc/udev/rules.d/目录下的文件指定的规则来设置。如果 udev 找不到所创建设备的权限文件,就将其权限设置为缺省的 660 ,所有者为 root:root 。
    上面的步骤完成后,那些已经存在并且已经内建驱动的设备就可以使用了,那么以模块驱动的设备呢?

2 对于没有编进内核的模块
 
前面我们提到了"热插拔事件处理程序"的概念,当内核检测到一个新设备连接时,内核会产生一个热插拔事件, 并在 /proc/sys/kernel/hotplug 文件里查找处理设备连接的用户空间程序。udev初始化脚本将 udevsend 注册为该处理程序。
   当产生热插拔事件的时候,内核让 udev 在 /sys文件系统里检测与新设备的有关信息,并为新设备在 /dev 里创建项目。

   大多数 Linux 发行版通过 /etc/modules.conf配置文件来处理模块加载,对某个设备节点的访问导致相应的内核模块被加载。对 udev这个方法就行不通了,因为在模块加载前,设备节点根本不存在。为了解决这个问题,可以在开机时加载相应的模块,可以用到的方法有:
1 可以在 /etc/rc.local rc.sysinit 中添加相应的加载模块命令。
2 可以在 /etc/sysconfig/modules/
下放相应的模块加载脚本。具体脚本的书写可以参考该目录下的其他脚本。 在
/etc/rc.sysinit 中有以下内容:
# Load other user-defined modules
for file in /etc/sysconfig/modules/*.modules ; do
  [ -x $file ] && $file
done

 可知在rc.sysinit中执行了 /etc/sysconfig/modules/中的脚本。
 当然modules/中的脚本是调用modprobe来加载模块的,这样必须将编译好的ko文件放到kernel的modules目录,并执行depmod命令:
 cp hello.ko /lib/modules/2.6.35.6-45.fc14.i686/kernel/drivers/char/
 depmod
 之后就可以用modprobe加载hello模块了

这样就可以在系统启动的时候加载这些模块,这样 udev 就可以检测到设备,并创建相应的设备节点了。

3  如果插入的设备有一个驱动程序模块但是尚未加载
Hotplug软件包就有用了,它就会响应上述的内核总线驱动热插拔事件并加载相应的模块,为其创建设备节点,这样设备就可以使用了。
`udev_new()` 是 `libudev` 库中用于创建一个新的 `udev` 上下文(context)的函数。它返回一个指向 `struct udev` 的指针,用于后续的 `udev` 设备管理操作。如果创建失败,则返回 `NULL`,此时可以通过 `errno` 获取具体的错误原因。 --- ### **`udev_new()` 函数原型** ```c #include <libudev.h> struct udev *udev_new(void); ``` ### **返回值** 1. **成功**: - 返回一个指向 `struct udev` 的指针,表示 `udev` 上下文创建成功。 - 后续可以通过该指针调用 `udev_*` 系列函数(如 `udev_device_new_from_subsystem_sysname()`)。 2. **失败**: - 返回 `NULL`,表示 `udev` 上下文创建失败。 - 此时可以通过 `errno` 获取具体的错误码(如内存不足、权限问题等)。 --- ### **常见错误码(`errno`)** 当 `udev_new()` 返回 `NULL` 时,`errno` 可能包含以下错误码: 1. **`ENOMEM`**: - **原因**:系统内存不足,无法分配 `udev` 上下文。 - **解决方法**:检查系统内存使用情况,或优化程序内存占用。 2. **`EPERM`**: - **原因**:权限不足(如非 `root` 用户尝试访问 `/sys` 或 `/dev`)。 - **解决方法**:以 `root` 用户运行程序,或调整系统权限。 3. **`ENODEV`**: - **原因**:系统不支持 `udev`(如未加载 `udev` 内核模块)。 - **解决方法**:检查系统是否支持 `udev`,或加载相关内核模块。 4. **`EACCES`**: - **原因**:无法访问 `/sys` 或 `/dev` 目录(如 SELinux/AppArmor 限制)。 - **解决方法**:检查安全策略配置,或调整程序运行环境。 --- ### **示例代码** ```c #include <libudev.h> #include <stdio.h> #include <errno.h> #include <string.h> int main() { // 创建 udev 上下文 struct udev *udev = udev_new(); if (udev == NULL) { fprintf(stderr, "Failed to create udev context: %s\n", strerror(errno)); return 1; } printf("udev context created successfully!\n"); // 释放 udev 上下文 udev_unref(udev); return 0; } ``` --- ### **注意事项** 1. **内存管理**: - 使用 `udev_new()` 创建的 `udev` 上下文必须通过 `udev_unref()` 释放,否则会导致内存泄漏。 2. **线程安全**: - `udev_new()` 是线程安全的,但返回的 `udev` 上下文不保证线程安全(需自行加锁)。 3. **错误处理**: - 始终检查 `udev_new()` 的返回值是否为 `NULL`,并处理可能的错误。 4. **依赖关系**: - 确保系统已安装 `libudev` 开发包(如 `libudev-dev`)。 --- ### **总结** - `udev_new()` 返回 `struct udev*` 指针(成功)或 `NULL`(失败)。 - 失败时可通过 `errno` 获取错误码(如 `ENOMEM`、`EPERM`)。 - 必须调用 `udev_unref()` 释放资源。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值