zoukankan      html  css  js  c++  java
  • [转]udev

    可看 : 使用udev高效地管理linux设备文件  https://www.ibm.com/developerworks/cn/linux/l-cn-udev/index.html 

    相对于linux来说,udev还是一个新事物。然而,尽管它03年才出现,尽管它很低调(J),但它无疑已经成为linux下不可或缺的组件了。udev是什么?它是如何实现的?最近研究Linux设备管理时,花了一些时间去研究udev的实现。

      
      udev是什么?u 是指user space,dev是指device,udev是用户空间的设备驱动程序吗?最初我也这样认为,调试内核空间的程序要比调试用户空间的程序复杂得多,内核空间的程序的BUG所引起的后果也严重得多,device driver是内核空间中所占比较最大的代码,如果把这些device driver中硬件无关的代码,从内核空间移动到用户空间,自然是一个不错的想法。
      
      但我的想法并不正确,udev的文档是这样说的,
      1. dynamic replacement for /dev。作为devfs的替代者,传统的devfs不能动态分配major和minor的值,而major和minor非常有限,很快就会用完了。 udev能够像DHCP动态分配IP地址一样去动态分配major和minor。
      
      2. device naming。提供设备命名持久化的机制。传统设备命名方式不具直观性,像/dev/hda1这样的名字肯定没有boot_disk这样的名字直观。udev能够像DNS解析域名一样去给设备指定一个有意义的名称。
      
      3. API to access info about current system devices 。提供了一组易用的API去操作sysfs,避免重复实现同样的代码,这没有什么好说的。
      
      我们知道,用户空间的程序与设备通信的方法,主要有以下几种方式,
      1. 通过ioperm获取操作IO端口的权限,然后用inb/inw/ inl/ outb/outw/outl等函数,避开设备驱动程序,直接去操作IO端口。(没有用过)
      2. 用ioctl函数去操作/dev目录下对应的设备,这是设备驱动程序提供的接口。像键盘、鼠标和触摸屏等输入设备一般都是这样做的。
      3. 用write/read/mmap去操作/dev目录下对应的设备,这也是设备驱动程序提供的接口。像framebuffer等都是这样做的。
      
      上面的方法在大多数情况下,都可以正常工作,但是对于热插拨(hotplug)的设备,比如像U盘,就有点困难了,因为你不知道:什么时候设备插上了,什么时候设备拔掉了。这就是所谓的hotplug问题了。
      
      处理hotplug传统的方法是,在内核中执行一个称为hotplug的程序,相关参数通过环境变量传递过来,再由hotplug通知其它关注 hotplug事件的应用程序。这样做不但效率低下,而且感觉也不那么优雅。新的方法是采用NETLINK实现的,这是一种特殊类型的socket,专门用于内核空间与用户空间的异步通信。下面的这个简单的例子,可以监听来自内核hotplug的事件

     1 #include <stdio.h> 
     2 #include <stdlib.h> 
     3 #include <string.h> 
     4 #include <ctype.h> 
     5 #include <sys/un.h> 
     6 #include <sys/ioctl.h> 
     7 #include <sys/socket.h> 
     8 #include <linux/types.h> 
     9 #include <linux/netlink.h> 
    10 #include <errno.h> 
    11 #include <unistd.h> 
    12 #include <arpa/inet.h> 
    13 #include <netinet/in.h> 
    14 
    15 #define UEVENT_BUFFER_SIZE 2048 
    16 
    17 static int init_hotplug_sock() 
    18 { 
    19 const int buffersize = 1024; 
    20 int ret; 
    21 
    22 struct sockaddr_nl snl; 
    23 bzero(&snl, sizeof(struct sockaddr_nl)); 
    24 snl.nl_family = AF_NETLINK; 
    25 snl.nl_pid = getpid(); 
    26 snl.nl_groups = 1; 
    27 
    28 int s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 
    29 if (s == -1) 
    30 { 
    31 perror("socket"); 
    32 return -1; 
    33 } 
    34 setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize)); 
    35 
    36 ret = bind(s, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)); 
    37 if (ret < 0) 
    38 { 
    39 perror("bind"); 
    40 close(s); 
    41 return -1; 
    42 } 
    43 
    44 return s; 
    45 } 
    46 
    47 int main(int argc, char* argv[]) 
    48 { 
    49 int hotplug_sock = init_hotplug_sock(); 
    50 
    51 while(1) 
    52 { 
    53 /* Netlink message buffer */ 
    54 char buf[UEVENT_BUFFER_SIZE * 2] = {0}; 
    55 recv(hotplug_sock, &buf, sizeof(buf), 0); 
    56 printf("%s
    ", buf); 
    57 
    58 /* USB 设备的插拔会出现字符信息,通过比较不同的信息确定特定设备的插拔,在这添加比较代码 */
    59 
    60 }
    61 return 0; 
    62 }

      编译:
      gcc -g hotplug.c -o hotplug_monitor
      
      运行后插/拔U盘,可以看到:

     1 add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1 
     2   add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00 
     3   add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0 
     4   add@/class/scsi_host/host2 
     5   add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81 
     6   add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02 
     7   add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83 
     8   add@/class/usb_device/usbdev2.2 
     9   add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0 
    10   add@/class/scsi_disk/2:0:0:0 
    11   add@/block/sda 
    12   add@/block/sda/sda1 
    13   add@/class/scsi_device/2:0:0:0 
    14   add@/class/scsi_generic/sg0 
    15   remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81 
    16   remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02 
    17   remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83 
    18   remove@/class/scsi_generic/sg0 
    19   remove@/class/scsi_device/2:0:0:0 
    20   remove@/class/scsi_disk/2:0:0:0 
    21   remove@/block/sda/sda1 
    22   remove@/block/sda 
    23   remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0 
    24   remove@/class/scsi_host/host2 
    25   remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0 
    26   remove@/class/usb_device/usbdev2.2 
    27   remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00 
    28   remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1 

    udev的主体部分在udevd.c文件中,它主要监控来自4个文件描述符的事件/消息,并做出处理

      1. 来自客户端的控制消息。这通常由udevcontrol命令通过地址为/org/kernel/udev/udevd的本地socket,向udevd发送的控制消息。其中消息类型有:
      l UDEVD_CTRL_STOP_EXEC_QUEUE 停止处理消息队列。
      l UDEVD_CTRL_START_EXEC_QUEUE 开始处理消息队列。
      l UDEVD_CTRL_SET_LOG_LEVEL 设置LOG的级别。
      l UDEVD_CTRL_SET_MAX_CHILDS 设置最大子进程数限制。好像没有用。
      l UDEVD_CTRL_SET_MAX_CHILDS_RUNNING 设置最大运行子进程数限制(遍历proc目录下所有进程,根据session的值判断)。
      l UDEVD_CTRL_RELOAD_RULES 重新加载配置文件。
      2. 来自内核的hotplug事件。如果有事件来源于hotplug,它读取该事件,创建一个udevd_uevent_msg对象,记录当前的消息序列号,设置消息的状态为EVENT_QUEUED,然后并放入running_list和exec_list两个队列中,稍后再进行处理。
      3. 来自signal handler中的事件。signal handler是异步执行的,即使有signal产生,主进程的select并不会唤醒,为了唤醒主进程的select,它建立了一个管道,在 signal handler中,向该管道写入长度为1个子节的数据,这样就可以唤醒主进程的select了。
      4. 来自配置文件变化的事件。udev通过文件系统inotify功能,监控其配置文件目录/etc/udev/rules.d,一旦该目录中文件有变化,它就重新加载配置文件。
      
      其中最主要的事件,当然是来自内核的hotplug事件,如何处理这些事件是udev的关键。udev本身并不知道如何处理这些事件,也没有必要知道,因为它只实现机制,而不实现策略。事件的处理是由配置文件决定的,这些配置文件即所谓的rule。
      
      关于rule的编写方法可以参考《writing_udev_rules》,udev_rules.c实现了对规则的解析。
      
      在规则中,可以让外部应用程序处理某个事件,这有两种方式,一种是直接执行命令,通常是让modprobe去加载驱动程序,或者让mount去加载分区。另外一种是通过本地socket发送消息给某个应用程序。
      
      在udevd.c:udev_event_process函数中,我们可以看到,如果RUN参数以”socket:”开头则认为是发到socket,否则认为是执行指定的程序。
      
      下面的规则是执行指定程序:
      60-pcmcia.rules: RUN+="/sbin/modprobe pcmcia"
      
      下面的规则是通过socket发送消息:
      90-hal.rules:RUN+="socket:/org/freedesktop/hal/udev_event"


    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ken_GL/archive/2010/12/02/6050168.aspx

  • 相关阅读:
    1.计算机初识
    re模块前瞻后顾 快速
    getattr 对类使用
    sklearn iris快速
    numpy c_
    sorted函数 字典按值倒序 实现
    logging快速入门
    configparser快速应用
    reduce 和 map 函数
    一个简单的类继承
  • 原文地址:https://www.cnblogs.com/yi-mu-xi/p/12794288.html
Copyright © 2011-2022 走看看