转自网友
目前找到3种办法可以获得hotplug事件。
一种是hotplug脚本。hotplug事件发生时,内核会调用脚本/sbin/hotplug。可以在这个脚本中加入自己的操作。不过2.6内核做了很大的改变。不再支持hotplug脚本。
还有就是netlink。这是一个特殊的socket,可以接受来自内核的消息。
下面是一个netlink例程。
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <sys/un.h>
- #include <sys/ioctl.h>
- #include <sys/socket.h>
- #include <linux/types.h>
- #include <linux/netlink.h>
- #include <errno.h>
-
- static int init_hotplug_sock(void)
- {
-
- struct sockaddr_nl snl;
- const int buffersize = 16 * 1024 * 1024;
- int retval;
-
- memset(&snl, 0x00, sizeof(struct sockaddr_nl));
- snl.nl_family = AF_NETLINK;
- snl.nl_pid = getpid();
- snl.nl_groups = 1;
-
- int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
- if (hotplug_sock == -1)
- {
- printf("error getting socket: %s", strerror(errno));
- return -1;
- }
-
- /* set receive buffersize */
-
- setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
- retval = bind(hotplug_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));
- if (retval < 0) {
- printf("bind failed: %s", strerror(errno));
- close(hotplug_sock);
- hotplug_sock = -1;
- return -1;
- }
-
- return hotplug_sock;
-
- }
-
- #define UEVENT_BUFFER_SIZE 2048
-
- int main(int argc, char* argv[])
- {
-
- int hotplug_sock = init_hotplug_sock();
- while(1)
- {
- char buf[UEVENT_BUFFER_SIZE*2] = {0};
- recv(hotplug_sock, &buf, sizeof(buf), 0);
- printf("SnoTest---------------%s\n", buf);
- }
-
- return 0;
- }
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <sys/un.h>
- #include <sys/ioctl.h>
- #include <sys/socket.h>
- #include <linux/types.h>
- #include <linux/netlink.h>
- #include <errno.h>
- static int init_hotplug_sock(void)
- {
- struct sockaddr_nl snl;
- const int buffersize = 16 * 1024 * 1024;
- int retval;
- memset(&snl, 0x00, sizeof(struct sockaddr_nl));
- snl.nl_family = AF_NETLINK;
- snl.nl_pid = getpid();
- snl.nl_groups = 1;
- int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
- if (hotplug_sock == -1)
- {
- printf("error getting socket: %s", strerror(errno));
- return -1;
- }
- /* set receive buffersize */
- setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
- retval = bind(hotplug_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));
- if (retval < 0) {
- printf("bind failed: %s", strerror(errno));
- close(hotplug_sock);
- hotplug_sock = -1;
- return -1;
- }
- return hotplug_sock;
- }
- #define UEVENT_BUFFER_SIZE 2048
- int main(int argc, char* argv[])
- {
- int hotplug_sock = init_hotplug_sock();
- while(1)
- {
- char buf[UEVENT_BUFFER_SIZE*2] = {0};
- recv(hotplug_sock, &buf, sizeof(buf), 0);
- printf("SnoTest---------------%s\n", buf);
- }
- return 0;
- }
2.6内核使用了一个用户态的程序udev来挂载/dev。这使得接受和处理来自内核的信息变得极为方便。
udev采用规则匹配的方式,来处理设备的挂载,同时触发其他处理。比如下列规则就可以捕捉来自U盘插入的事件。并调用对应的脚本。
BUS=="usb", KERNEL=="sd*", RUN+="/root/p.sh"
/root/p.sh 的内容
#!/bin/bash
set >> /root/usb.txt
echo -e "END****" >> /root/usb.txt
这样就可以把发生的事件的参数打印到文本文件中。以下是我的U盘插拔时的信息。
ACT
BASH=/bin/bash
BASH_ARGC=()
BASH_ARGV=()http://wubinjie.spaces.live.com/blog/cns!2055299901bf6246!234.entry
BASH_LINENO=([0]="0")
BASH_SOURCE=([0]="/root/p.sh")
BASH_VERSINFO=([0]="3" [1]="1" [2]="17" [3]="1" [4]="release" [5]="i686-redhat-linux-gnu")
BASH_VERSION='3.1.17(1)-release'
DEVLINKS='/dev/disk/by-id/usb-USB_2.0_6025 /dev/disk/by-path/pci-0000:00:1d.2-usb-0:1:1.0-scsi-0:0:0:0'
DEVNAME=/dev/sda
DEVPATH=/block/sda
DIRSTACK=()
EUID=0
GROUPS=()
HOSTNAME=linux230
HOSTTYPE=i686
ID_BUS=usb
ID_MODEL=6025
ID_PATH=pci-0000:00:1d.2-usb-0:1:1.0-scsi-0:0:0:0
ID_REVISION=2.00
ID_SERIAL=USB_2.0_6025
ID_TYPE=disk
ID_VENDOR=USB_2.0
IFS=$' \t\n'
MACHTYPE=i686-redhat-linux-gnu
MAJOR=8
MINOR=0
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/local/bin:/bin:/usr/bin
PHYSDEVBUS=scsi
PHYSDEVDRIVER=sd
PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/host5/target5:0:0/5:0:0:0
PPID=3549
PS4='+ '
PWD=/
SEQNUM=814
SHELL=/bin/bash
SHELLOPTS=braceexpand:hashall:interactive-comments
SHLVL=1
SUBSYSTEM=block
TERM=dumb
UDEVD_EVENT=1
UDEV_LOG=3
UID=0
_=/bin/bash
END****
ACT
BASH=/bin/bash
BASH_ARGC=()
BASH_ARGV=()
BASH_LINENO=([0]="0")
BASH_SOURCE=([0]="/root/p.sh")
BASH_VERSINFO=([0]="3" [1]="1" [2]="17" [3]="1" [4]="release" [5]="i686-redhat-linux-gnu")
BASH_VERSION='3.1.17(1)-release'
DEVLINKS='/dev/disk/by-id/usb-USB_2.0_6025-part1 /dev/disk/by-path/pci-0000:00:1d.2-usb-0:1:1.0-scsi-0:0:0:0-part1'
DEVNAME=/dev/sda1
DEVPATH=/block/sda/sda1
DIRSTACK=()
EUID=0
GROUPS=()
HOSTNAME=linux230
HOSTTYPE=i686
ID_BUS=usb
ID_FS_LABEL=
ID_FS_LABEL_SAFE=
ID_FS_TYPE=vfat
ID_FS_USAGE=filesystem
ID_FS_UUID=
ID_FS_VERSION=FAT32
ID_MODEL=6025
ID_PATH=pci-0000:00:1d.2-usb-0:1:1.0-scsi-0:0:0:0
ID_REVISION=2.00
ID_SERIAL=USB_2.0_6025
ID_TYPE=disk
ID_VENDOR=USB_2.0
IFS=$' \t\n'
MACHTYPE=i686-redhat-linux-gnu
MAJOR=8
MINOR=1
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/local/bin:/bin:/usr/bin
PHYSDEVBUS=scsi
PHYSDEVDRIVER=sd
PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.2/usb3/3-1/3-1:1.0/host5/target5:0:0/5:0:0:0
PPID=3576
PS4='+ '
PWD=/
SEQNUM=815
SHELL=/bin/bash
SHELLOPTS=braceexpand:hashall:interactive-comments
SHLVL=1
SUBSYSTEM=block
TERM=dumb
UDEVD_EVENT=1
UDEV_LOG=3
UID=0
_=/bin/bash
END****
可以看到挂载了2个设备 /dev/sda /dev/sda1 其中一个分区,一个是设备。
====================================================================================================================================
关于udev和mdev之间的区别与联系我发现自己现在还没有把它完整的给区分开来和联系起来.
设备文件系统有devfs,mdev,udev
mdev是udev的简化版本,是busybox中所带的程序,最适合用在嵌入式系统,而udev一般用在PC上的linux中,相对mdev来说要复杂些,devfs是2.4内核引入的,而在2.6内核中却被udev所替代,他们有着共同的优点,只是devfs中存在一些未修复的BUG,作者也停止了对他的维护,最显著的一个区别,采用devfs时,当一个并不存在的设备结点时,他却还能自动的加载对应的设备驱动,而udev则不能,udev认为当加载了不存在的对应的设备驱动的时候不应加载对应的驱动模块,因为加载也没有,浪费了资源.
从本质上来说,udev与mdev他们都是一个应用程序,配置了就可以使用,为了方便使用,我们可以使用busybox自带的mdev,当然也可以去下载udev的源码去编译和移植.
这应该是对udev和mdev的一个理解,我们下次可以再深入一些.
dev 和mdev 是两个使用uevent 机制处理热插拔问题的用户空间程序,两者的实现机理不同。udev 是基于netlink 机制的,它在系统启动时运行了一个deamon 程序udevd,通过监听内核发送的uevent 来执行相应的热拔插动作,包括创建/删除设备节点,加载/卸载驱动模块等等。mdev 是基于uevent_helper 机制的,它在系统启动时修改了内核中的uevnet_helper 变量(通过写/proc/sys/kernel/hotplug),值为“/sbin/mdev”。这样内核产生uevent 时会调用uevent_helper 所指的用户级程序,也就是mdev,来执行相应的热拔插动作。udev 使用的netlink 机制在有大量uevent 的场合效率高,适合用在PC 机上;而mdev 使用的uevent_helper 机制实现简单,适合用在嵌入式系统中。另外要说明的一点是,uevent_helper 的初始值在内核编译时时可配置的,默认值为/sbin/hotplug。如果想修改它的值,写/proc/sys/kernel/hotplug 文件就可以了,例如:
echo “/sbin/mdev” > /proc/sys/kernel/hotplug
补充一点:如果使用的是udevd,那么uevent_helper变量应为空,即
echo “ ” > /proc/sys/kernel/hotplug
这段内容总结的很好,我也看了linux内核有关uevent的代码,的确是这样,这样看来简单的将mdev看做udev的简化版也就不准确了。这样在做嵌入式的文件系统就要注意了,一般只会使用mdev,目前我还不确定能不能只用udev,理论上是可以的,当然两种一起用也是可以的,也就是mdev + udev,但是这时要注意了,写规则时一定要注意,避免让它们重复执行。也就是说udev执行过的,mdev不要再执行了。其实udev最大的特点就是使用了netlink,实质上是一个scoket,这个特别的scoket用来监测uevent,当然,我们也可以自己写一个函数用来监测任何uevent事件。
其他相关链接:udev和mdev学习总结:http://blog.youkuaiyun.com/michaelcao1980/article/details/39314615
blog.sina.com.cn/s/blog_944790400101kz8f.html