U盘的挂载过程-先记录一下

从具体实现层面来说就是:

App
↑ App等候设备处理信息并挂载设备
D-Bus
↑ 过滤处理内容后送给D-Bus
HAL 它是一个位于操作系统和驱动程序之上,运行在用户空间中的服务程序
↑ 把硬件相关内容送到HAL
udev
↑ kernel2.6发现设备变化反映到sysfs, 并通过hotplug机制通知udev
Linux Kernel2.6 自动调用驱动模块

**1. 自动挂载磁盘分区的操作从底层来说,是要内核支持的,2.6 内核的sysfs 虚拟文件系统就提供了这一支持,这个文件系统 (/sys/) 通常用于反应系统硬件信息,总线上的设备变化、网络设备的变化等事件在这里都能反应出来,这个文件系统的变化配合上内核的 hotplug 机制就可以掌握硬件改动相关的信息.
说到内核的 hotplug 机制,简单地说就是在硬件发生变化的时候去通知某一用户态程序,缺省是 /sbin/hotplug,不过现在它已经被 udev 取代了,收到信息之后,udev 会根据 sysfs 的变化调用一些脚本来处理这个事件,这里的处理是指某个用户空间的动作,而不是加载驱动,加载驱动是内核自己的事情,在 udev 反应过来之前就完成了.
2. 下面轮到我们著名的硬件抽象层(HAL)
HAL是Hardware Abstraction Layer的首字母缩写。我最早是在Winnt 3.5的帮助中知道这个名词的,对帮助文档中的说法我比较认同,所以一直对它抱有好感。不过Windows下的HAL和Linux下的HAL两者所指并非相同之物:
Windows 下的HAL : 位于操作系统的最底层,直接操作物理硬件,隔离与硬件相关的信息,为上层的操作系统和设备驱动程序提供一个统一的接口,起到对硬件的抽象作用。有了HAL,编写驱动程序就容易多了,因为HAL的接口不但使用简单,而且具有更好的可移植性(没用过)。
Linux 下的HAL :至于对硬件的抽象,Linux内核早就有类似机制,只不过没有专门的名称罢了。而Linux的HAL指的并非这个,它不是位于操作系统的最底层,直接操作硬件,相反,它位于操作系统和驱动程序之上,是一个运行在用户空间中服务程序。
我们知道,Linux和所有的Unix一样,习惯用文件来抽象设备,任何设备都是一个文件,比如/dev/mouse是鼠标的设备文件。这种方法看起来不 错,每个设备都有统一的形式,但使用并不那么容易,设备文件名没有什么规范,从简单的一个文件名,你无法得知它是什么设备,具有有什么特性。
结果形成这样的尴尬:有了设备和设备驱动程序,却不知道如何使用它。这些乱七八糟的设备文件,让设备的管理和应用程序的开发都变得很麻烦,所以有必要提供一个硬件抽象层,来为上层应用程序提供一个统一的接口,Linux的HAL就这样应运而生了。
但HAL并不提供诸如拍照和刻录等之类的功能,相反它只是告诉应用程序,系统中有哪些设备可用,以及这些设备的类型、特性和能力等。主要说来,它提供以下几项功能:

  1. 获取指定类型的设备列表。
  2. 获取/更改设备的属性值。
  3. 获取设备具有的能力描述。
  4. 设备插入/拔除时,通知相关应用程序。
  5. 设备属性或能力变化时,通知相关应用程序。
    udev创建dev下的文件结点,加载驱动程序,让设备处于可用状态。而HAL则告诉应用程序,现在有哪些设备可用,这些设备的类型、特性和能力,让应用程序知道如何使用它们。
    设备的属性管理是HAL最重要任务之一,有的设备属性来源于实际的硬件,有的来源于设备信息文件(/usr/share/hal/fdi/),有的来源其 它配置信息(如/usr/share/hwdata/)。设备属性的都有标准的定义,这些属性定义是HAL的SPEC的主要内容之一,可以参考
    http://people.freedesktop.org/~david/hal-spec/hal-spec.html
  6. udev & HAL
    udev通过NetLink注册内核的设备事件,当有设备插入/拔除时,udev就会收到通知,它会从事件中所带参数和sysfs中的信息,加载适当的驱动程序,创建dev下的结点,让设备处于可用的状态。
    . udev只是一个框架,它的行为完全受它的规则所控制,这些规则存放在目录/etc/udev/rules.d/中,其中90-hal.rules是用 来让udev把设备插入/拔除的事件通过socket socket:/org/freedesktop/hal/udev_event转发给HAL的。
    . HAL挂在socket:/org/freedesktop/hal/udev_event上等待事件,有事件发生时就调用函数 hald_udev_data处理,它先从事件中取出主要参数,创建一个hotplug_event对象,把它放入事件队列中,然后调用 hotplug_event_process_queue处理事件。
    . 函数hotplug_event_begin负责具体事件的处理,它把全部事件分为四类,并分别处理 hotplug_event_begin_sysfs处理普通设备事件,hotplug_event_begin_acpi处理ACPI事 件,hotplug_event_begin_apm处理APM事件,hotplug_event_begin_pmu处理PMU事件。要注意的是,后三 者的事件源并非源于udev,而是在device_reprobe时触发的 (osspec_device_reprobe/hotplug_reprobe_tree /hotplug_reprobe_generate_add_events/acpi_generate_add_hotplug_event)。
    . 函数hotplug_event_begin_sysfs中,如果是插入设备,则创建一个设备对象,设置设备的属性,调用相关callouts,然后放 入设备列表中,并触发signal让dbus通知相关应用程序。如果是拔除设备,则调用相关callouts,然后从设备列表中删除,并触发signal 让dbus通知相关应用程序。
    . 应用程序可以主动调用HAL提供的DBUS接口函数,这些函数在libhal.h中有定义。应用程序也可以注册HAL的signal,当设备变化时,HAL通过DBUS上报事件给应用程序。
    . callout是HAL一种扩展方式,它在设备插入/拔除时执行。可以在设备信息文件中(/usr/share/hal目录)指定。
    . addon也是HAL一种扩展方式,它与callout的不同之处在于addon往往是事件的触发者,而不是事件的消费者。HAL的事件源主要源于 udev,而udev源于kernel的hotplug,然而有的设备如电源设备、磁盘设备和特殊按键等,它们并不产生hotplug事件。HAL就得不 到通知,怎么办呢,addon就是用于支持新事件源的扩展方式。比如addon-acpi从/proc/acpi/event或者/var/run /acpid.socket收到事件,然后转发成HAL事件。addon-storage检测光盘或磁盘的状态,并设置设备的属性。addon- keyboard检测一些特殊按键,并触发相应事件。
    access-check/ci-tracker/ck-tracker负责权限的检查,里面提到的PolicyKit/ConsoleKit不是太熟悉,有时间再看看。
    简单的说,HAL就是一个设备数据库,它管理当前系统中所有的设备,你可以以多种灵活的方式去查询这些设备,可以获取指定设备的特性,可以注册设备变化事件。
  7. 那 hal 怎么发出通知呢? 这是利用一个新兴的系统内用户空间消息总线系统 – dbus 来完成的,hal 会通过 dbus 上的一条消息总线把新加入的块设备和相关的挂载提示信息,比如加载选项 sync, iocharset 之类的信息发送到总线上来,只等待一个终结者来接受并处理这些信息了.
  8. 这个终结者对于 gnome 来说就是 gnome-volume-manager (名字太长了,下面简称 gvm),它从 dbus 上探听消息,当发现有设备挂载提示的时候就会尝试把设备挂载上来。缺省的,还会打开一个 nautilus 浏览窗口来浏览新挂载上的分区的内容。**

同样linux内核也自带了USB总线驱动程序,框架如下:
在这里插入图片描述
在这里插入图片描述
引用自:https://www.cnblogs.com/lifexy/p/7631900.html

要想成为一个USB主机,硬件上就必须要有USB主机控制器才行,USB主机控制器又分为4种接口:
OHCI(Open Host Controller Interface): 微软主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps),OHCI接口的软件简单,硬件复杂
UHCI(Universal Host Controller Interface): Intel主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps), 而UHCI接口的软件复杂,硬件简单
EHCI(Enhanced Host Controller Interface):高速USB2.0(480Mbps),
xHCI(eXtensible Host Controller Interface):USB3.0(5.0Gbps),采用了9针脚设计,同时也支持USB2.0、1.1等

最后一个问题就是怎么挂了,众所周知,如果块设备在 /etc/fstab 里没有描述存在的话,挂载就比较麻烦:
本来如果有 fstab 中的 user 属性的话,普通用户可以挂载,但 现在没有,普通用户没法挂
如果 sudo 授权的话,用户可能获得挂载其他分区的过大权限,危及 系统安全。
pmount 就是这个工具链上的最后一环,它可以代表用户 (运行 gvm 的用户) 来挂载一个属于他的可移动存储设备,即使 fstab 里没有这个设备存在,缺省的挂载位置是 /media/ 下,和 块设备同名,比如 /dev/sda1 挂载到 /media/sda1/ 目录。
嗯,综上,设备的自动挂载就完成了,呵呵,希望一大堆的关键词没把大家搞晕,回顾一下: kernel 发现设备变化反应到 sysfs 上并通过 hotplug 机制通知udev, udev 把硬件相关内容送给 hal,hal 过滤、处理之后发送信息到 dbus 上,在 dbus 上等候的 gvm 收到消息后用 pmount 把设备挂上,这样,设备的自动挂载就完成.
PS:HAL的相关文件:
首先是硬件信息文件fdi的路径会有:
/usr/share/hal/fdi 通常是由系统程序安装包提供的文件。
/etc/hal/fdi 这里是用户或者管理员修改fdi的位置。
这两个路径下各自存在information policy preprobe等3个目录,用来存放不同用途的fdi文件。后面再解释。
其次是HAL的一些Callout和Addon,他们位于 /usr/lib/hal/scripts 及 /usr/lib/hal/ 下面
再有一些与HAL本身相关的配置文件等:
/etc/init.d/hal hal的启动脚本
/etc/udev/rules.d/95-hal.rules HAL在UDEV中的规则
/etc/dbus-1/system.d/hal.conf HAL的一些常用的Interface在DBUS中的权限设置。
相关程序
/usr/bin/lshal
/usr/bin/hal-device
/usr/bin/hal-get-property
/usr/bin/hal-set-property
/usr/bin/hal-find-by-capability
/usr/bin/hal-find-by-property
/usr/bin/hal-disable-polling
/usr/bin/hal-is-caller-locked-out
/usr/bin/hal-lock
/usr/sbin
/usr/sbin/hald

以上内容引用自:https://blog.csdn.net/yanzi1225627/article/details/7867898

而自2.6 内核开始,引入了sysfs 文件系统。sysfs 把连接在系统上的设备和总线组织成一个分级的文件,并提供给用户空间存取使用。udev 运行在用户模式,而非内核中。udev 的初始化脚本在系统启动时创建设备节点,并且当插入新设备——加入驱动模块——在sysfs上注册新的数据后,udev会创新新的设备节点。

 udev 是一个工作在用户空间的工具,它能根据系统中硬件设备的状态动态的更新设备文件,包括设备文件的创建,删除,权限等。这些文件通常都定义在/dev 目录下,但也可以在配置文件中指定。udev 必须内核中的sysfs和tmpfs支持,sysfs 为udev 提供设备入口和uevent 通道,tmpfs 为udev 设备文件提供存放空间。

注意,udev 是通过对内核产生的设备文件修改,或增加别名的方式来达到自定义设备文件的目的。但是,udev 是用户模式程序,其不会更改内核行为。也就是说,内核仍然会创建sda,sdb等设备文件,而udev可根据设备的唯一信息来区分不同的设备,并产生新的设备文件(或链接)。而在用户的应用中,只要使用新产生的设备文件即可。

提到udev,不能不提的就是devfs,下面看一下udev 与 devfs 的区别:
1、udev能够实现所有devfs实现的功能。但udev运行在用户模式中,而devfs运行在内核中。
2、当一个并不存在的 /dev 节点被打开的时候, devfs一样自动加载驱动程序而udev确不能。udev设计时,是在设备被发现的时候加载模块,而不是当它被访问的时候。 devfs这个功能对于一个配置正确的计算机是多余的。系统中所有的设备都应该产生hotplug 事件、加载恰当的驱动,而 udev 将会注意到这点并且为它创建对应的设备节点。如果你不想让所有的设备驱动停留在内存之中,应该使用其它东西来 管理你的模块 (如脚本, modules.conf, 等等) 。其中devfs 用的方法导致了大量无用的 modprobe 尝试,以此程序探测设备是否存在。每个试探性探测都新建一个运行 modprobe 的进程,而几乎所有这些都是无用的
3、udev是通过对内核产生的设备名增加别名的方式来达到上述目的的。前面说过,udev是用户模式程序,不会更改内核的行为。
因此,内核依然会我行我素地产生设备名如sda,sdb等。但是,udev可以根据设备的其他信息如总线(bus),生产商(vendor)等不同来区分不同的设备,并产生设备文件。udev只要为这个设备文件取一个固定的文件名就可以解决这个问题。在后续对设备的操作中,只要引用新的设备名就可以了。但为了保证最大限度的兼容,一般来说,
新设备名总是作为一个对内核自动产生的设备名的符号链接(link)来使用的。

例如:内核产生了sda设备名,而根据信息,这个设备对应于是我的内置硬盘,那我就可以制定udev规则,让udev除了产生/dev/sda设备文件 外,另外创建一个符号链接叫/dev/internalHD。这样,我在fstab文件中,就可以用/dev/internalHD来代替原来的 /dev/sda了。下次,由于某些原因,这个硬盘在内核中变成了sdb设备名了,那也不用着急,udev还会自动产生/dev/internalHD这 个链接,并指向正确的/dev/sdb设备。所有其他的文件像fstab等都不用修改。

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

b – 编译成模块的驱动程序
前面我们提到了"热插拔事件处理程序"的概念,当内核检测到一个新设备连接时,内核会产生一个热插拔事件,并在 /proc/sys/kernel/hotplug 文件里查找处理设备连接的用户空间程序。udev 初始化脚本将 udevsend 注册为该处理程序。当产生热插拔事件的时候,内核让 udev 在 /sys 文件系统里检测与新设备的有关信息,并为新设备在 /dev 里创建项目。
大多数 Linux 发行版通过 /etc/modules.conf 配置文件来处理模块加载,对某个设备节点的访问导致相应的内核模块被加载。对 udev 这个方法就行不通了,因为在模块加载前,设备节点根本不存在。为了解决这个问题,在 LFS-Bootscripts 软件包里加入了 modules 启动脚本,以及 /etc/sysconfig/modules 文件。通过在 modules 文件里添加模块名,就可以在系统启动的时候加载这些模块,这样 udev 就可以检测到设备,并创建相应的设备节点了。如果插入的设备有一个驱动程序模块但是尚未加载,Hotplug 软件包就有用了,它就会响应上述的内核总线驱动热插拔事件并加载相应的模块,为其创建设备节点,这样设备就可以使用了。

创建和配置mdev

mdev是udev的简化版本,是busybox中所带的程序,最适合用在嵌入式系统,而udev一般都用在PC上的Linux中,相对mdev来说要复杂些;

1、我们应该明白,不管是udev还是mdev,他们就是一个应用程序,就跟其他应用程序一样(比如:Boa服务),配置了就可以使用了。为了方便起见,我们就使用busybox自带的一个mdev,这样在配置编译busybox时,只要将mdev的支持选项选上,编译后就包含了mdev设备文件系统的应用(当然你也可以不使用busybox自带的,去下载udev的源码进行编译移植。

#cd busybox-1.13.0/
#make menuconfig

Linux System Utilities —>
[] mdev
[
] Support /etc/mdev.conf
[] Support subdirs/symlinks
[
] Support regular expressions substitutions when renaming device
[*] Support command execution at device addition/removal

2、udev或者mdev需要内核sysfs和tmpfs虚拟文件系统的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间。所以在/etc/fstab配置文件中添加如下内容(红色部分):

device mount-point type options dump fsck order

#----------------------------------------------------------------
procfs /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev/shm tmpfs defaults 0 0
usbfs /proc/bus/usb usbfs defaults 0 0
ramfs /dev ramfs defaults 0 0
none /dev/pts devpts mode=0622 0 0

3.、在系统初始化配置文件/etc/init.d/rcS中挂载mdev要用到的sysfs文件系统和tmpfs文件系统 ,这个在我们介绍根文件系统时已经看到:

 然后启动/sbin目录下的mdev应用对系统的设备进行搜索(红色部分)。

Mount virtual filesystem

/bin/mount -t proc procfs /proc
/bin/mount -n -t sysfs sysfs /sys
/bin/mount -n -t usbfs usbfs /proc/bus/usb
/bin/mount -t ramfs ramfs /dev

Make dir

/bin/mkdir -p /dev/pts
/bin/mkdir -p /dev/shm
/bin/mkdir -p /var/log
/bin/mount -n -t devpts none /dev/pts -o mode=0622
/bin/mount -n -t tmpfs tmpfs /dev/shm

Make device node

echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s

a – " mdev -s " 的含义是扫描 /sys 中所有的类设备目录,如果在目录中有含有名为“dev”的文件,且文件中包含的是设备号,则mdev就利用这些信息为该设备在/dev下创建设备节点文件;

b – “echo /sbin/mdev > /proc/sys/kernel/hotplug” 的含义是当有热插拔事件产生时,内核就会调用位于 /sbin 目录的 mdev 。这时 mdev 通过环境变量中的 ACTION 和DEVPATH,来确定此次热插拔事件的动作以及影响了 /sys 中的那个目录。接着,会看这个目录中是否有“dev”的属性文件。如果有就利用这些信息为这个设备在 /dev 下创建设备节点文件。

4、在设备驱动程序中加上对类设备接口的支持,即在驱动程序加载和卸载函数中实现设备文件的创建与销毁,这个在我们前面Linux 字符设备驱动结构(二)—— 自动创建设备节点 可以看到实例,下面在介绍一个

 例如在之前篇幅的按键驱动中添加(红色部分):

#include <linux/device.h> //设备类用到的头文件
staticint device_major = DEVICE_MAJOR; //用于保存系统动态生成的主设备号
staticstruct class *button_class; //定义一个类
 
static int __init button_init(void)
{
    //注册字符设备,这里定义DEVICE_MAJOR=0,让系统去分配,注册成功后将返回动态分配的主设备号
    device_major = register_chrdev(DEVICE_MAJOR, DEVICE_NAME,&buttons_fops);
 
    if(device_major< 0)
    {
        printk(DEVICE_NAME " register faild!/n");
        return device_major;
    }
 
    //注册一个设备类,使mdev可以在/dev/目录下建立设备节点
    button_class =class_create(THIS_MODULE, DEVICE_NAME);
 
    if(IS_ERR(button_class))
    {
        printk(DEVICE_NAME" create class faild!/n");
        return-1;
    }
 
    //创建一个设备节点,取名为DEVICE_NAME(即my2440_buttons)
    //注意2.6内核较早版本的函数名是class_device_create,现该为device_create
    device_create(button_class,NULL,MKDEV(device_major,0),NULL,DEVICE_NAME);
 
    return 0;
}
 
static void __exit button_exit(void)
{
    //注销字符设备
    unregister_chrdev(device_major, DEVICE_NAME);
 
    //删除设备节点,注意2.6内核较早版本的函数名是class_device_destroy,现该为device_destroy
    device_destroy(button_class,MKDEV(device_major,0));
 
    //注销类
    class_destroy(button_class);
}

5、至于mdev的配置文件/etc/mdev.conf,这个可有可无,只是设定设备文件的一些规则。我这里就不管他了,让他为空好了。

6、完成以上步骤后,重新编译文件系统,下载到开发板上,启动开发板后进入开发板的/dev目录查看,就会有很多系统设备节点在这里产生了,我们就可以直接使用这些设备节点了。

猜你喜欢

转载自blog.csdn.net/seaship/article/details/110087444