Linux中设备管理

什么是 udev

udev 是 Linux2.6 内核里的一个功能,它替代了原来的 devfs,成为当前 Linux 默认的设备管理工具。udev 以守护进程的形式运行,通过侦听内核发出来的 uevent 来管理 /dev目录下的设备文件。不像之前的设备管理工具,udev 在用户空间 (user space) 运行,而不在内核空间 (kernel space) 运行。

使用 udev 的好处

所有的设备在 Linux 里都是以设备文件的形式存在。在早期的 Linux 版本中,/dev目录包含了所有可能出现的设备的设备文件。现在 udev 只为那些连接到 Linux 操作系统的设备产生设备文件。并且 udev 能通过定义一个 udev 规则 (rule) 来产生匹配设备属性的设备文件,这些设备属性可以是内核设备名称、总线路径、厂商名称、型号、序列号或者磁盘大小等等。

    动态管理:当设备添加 / 删除时,udev 的守护进程侦听来自内核的 uevent,以此添加或者删除 /dev下的设备文件,所以 udev 只为已经连接的设备产生设备文件,而不会在 /dev下产生大量虚无的设备文件。

    自定义命名规则:通过 Linux 默认的规则文件,udev 在 /dev/ 里为所有的设备定义了内核设备名称,比如 /dev/sda、/dev/hda、/dev/fd等等。由于 udev 是在用户空间 (user space) 运行,Linux 用户可以通过自定义的规则文件,灵活地产生标识性强的设备文件名,比如 /dev/boot_disk、/dev/root_disk、/dev/color_printer等等。

设定设备的权限和所有者 / 组:udev 可以按一定的条件来设置设备文件的权限和设备文件所有者 / 组。在不同的 udev 版本中,实现的方法不同,在“如何配置和使用 udev”中会详解。

udev工作原理

当系统添加设备时,/sys目录下会生成设备的相应信息.udev就是通过读取/sys下的信息来识别硬件设备的.

udev有3部分组成: namedev,libsysfs,udev

namedev 负责解析/etc/udev/rules.d中的命名规则,遇到匹配规则后,会返回一个设备名.

libsysfs 负责读取/sys下的硬件信息.

udev程序负责协调namedev和libsysfs,来完成指定的命名策略.

当有设备添加或移出时,在早期的发行中常使用一个外部二进制文件/sbin/hotplug来将设备状态的改变通知Udev。现在这个工具已经被替换掉,Udev可以通过Netlink直接监听这些事件了。

udev获得这些信息,然后调用namedev,为设备指定一个名称.如果这是一个已经增加的新设备,则udev使用

libsysfs来获得设备的主从设备号,然后在/dev下建立设备文件.如果这个设备已经移出,则将从/dev下删除它的设备文件.

用户程序获取设备信息

有了udev和netlink这两个工具,就可以实现在用户空间中获取内核里设备的所有信息。如图所示

 

 

用户空间编写个类似udev的应用程序,在启动时检查/bus/*/device/下设备及/class/*/下设备,并创建一个netlink的socket,监听NETLINK_KOBJECT_UEVENT事件,来获取设备热插拔的信息。

此外还需要编写一个内核模块来配合udev获取其他相关的信息,该模块不妨称为设备信息模块,该模块通过调用netlink_kernel_create来创建一个sock监听所有发送到某个protocol的消息,这样前面的应用程序就可以通过这个protocol和内核模块进行通讯,由于内核模块可以通过直接函数调用访问各种设备的信息,这样设备信息模块就相当于一个中转,来完成应用程序的get操作。

使用netlink的优点

Netlink 相对于系统调用,ioctl 以及 /proc 文件系统而言具有以下优点:

1,为了使用 netlink,用户仅需要在 include/linux/netlink.h 中增加一个新类型的 netlink 协议定义即可, 如 #define NETLINK_MYTEST 17 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换。但系统调用需要增加新的系统调用,ioctl 则需要增加设备或文件, 那需要不少代码,proc 文件系统则需要在 /proc 下添加新的文件或目录,那将使本来就混乱的 /proc 更加混乱。

2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息,但系统调用与 ioctl 则是同步通信机制,如果传递的数据太长,将影响调度粒度。

3.使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。

4.netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性,任何对内核事件感兴趣的应用都能收到该子系统发送的内核事件,在后面的文章中将介绍这一机制的使用。

5.内核可以使用 netlink 首先发起会话,但系统调用和 ioctl 只能由用户应用发起调用。

6.netlink 使用标准的 socket API,因此很容易使用,但系统调用和 ioctl则需要专门的培训才能使用。

内核和用户空间消息传递

使用消息队列

Msgsend,msgrcv均属于系统调用,内核中如果要使用需要查找system_call_table来进行调用,实现非常复杂,因为考虑到安全的原因,在2.6的内核中已经不把sys_call_table给export出来,需要自行编写程序去查找该地址,见

http://chenm.blogbus.com/logs/52077181.html

而且这个方法找SYS_CALL_TABLE不适合于SMP的情形。

使用/proc

/proc只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。用户和应用程序可以通过proc得到系统的信息。用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的。

procfs有一些限制,因为它提供的缓存,只有一个页,因此必须特别小心,并对超过页的部分做特别的考虑,处理起来比较复杂并且很容易出错,所有procfs并不适合于大数据量的输入输出

关于内核通告用户程序

本次模块化设计考虑由用户程序负责管理设备信息。根据前面的描述,为了实现内核模块与用户程序之间传输设备信息。考虑一种方法,如下图所示,

 

 

当用户程序启动后,创建一个user file,不妨称为”/usr/my_pid”,往里面填写该程序的pid,然后注册接收SIGIO,及处理该信号的handle函数。

当内核模块程序启动后,创建一个driver file,不妨称为”/proc/my_device”,然后打开user file,读取用户程序的pid,向该pid发送signal。用户程序收到通告,handle函数打开driver file,通过read来读取消息,内核模块的read handler此时就可以读取设备信息,告知用户程序。

这里有个限制,需要保证用户程序比内核模块先启动,因此需要对用户程序进行改进,即启动时候先打开一次打开driver file,读取消息,这样即使内核模块先启动,用户模块再启动,也可以读取出设备信息,不关心启动顺序了。

注:由于任何用户都不能在/proc目录下创建或者删除一个文件或目录,故user prog只能通过一个普通文件告之内核其pid。

关于设备信息的备份

用户程序负责储存设备信息,但是一旦用户程序崩溃,重启后设备信息如何重新获取?这里有几种思路。

思路1是用户程序将设备信息以文件形式储存,重启后读取文件(优点是不依赖备份程序或内核来备份设备信息,缺点是需要防止文件被删除)

思路2是创建主备用户程序,当主程序崩溃,备份程序接替主程序接收内核通告(优点是不需要构造文件,所有信息备份在内存中,缺点是额外需要设计主备程序同步逻辑)

思路3是内核模块备份设备信息,应用程序启动后读取(优点是应用程序不需备份,但内核需要判断pid有无变化,以区分是崩溃重启还是首次重启,如果pid不变,则read只返回曾经未读取的新设备,如果pid变化,则将当前设备加入备份设备链表,并把备份设备链表通告给应用程序)

附录

udev的官方网址:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html

udev src code的下载地址:http://www.us.kernel.org/pub/linux/utils/kernel/hotplug/

参考网址:

http://blog.csdn.net/zhoujunyi/archive/2007/06/18/1656759.aspx

http://www.ibm.com/developerworks/cn/linux/l-cn-udev/index.html?ca=drs-cn-0304

http://hi.baidu.com/yyywwweee/blog/item/8070f1d0339c6c89a0ec9c9b.html

 

 

 

alloc_netdev主要分配net_device结构,每个网络设备对象是标准的结构,但是不同的网络驱动程序可能都要维护不同的私有信息,所以在分配net_device结构的同时可以多分配出sizeof_priv大小的结构来。

 

loopback代码解析

http://blog.chinaunix.net/u3/114767/showart_2320113.html

 

lsmode查看当前加载模块

在linux 下网卡的驱动是以模块的形式添加上去的,因此可以直接用lsmod看到当前加载的模块

驱动在lib/modules/2..../kernel/drviers/net/

 

注:其中与硬件有关的关键信息初始化过程中需要考虑对linux内核屏蔽,也就是说,通过Linux的设备管理系统,不能察看设备的硬件关键信息(比如桥型号,网络接口芯片,硬件体系结构信息等)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值