• 20150225 IMX257 设备驱动模型之sysfs文件系统知识点整合(二)


    20150225 IMX257 设备驱动模型之sysfs文件系统知识点整合(二)

    2012-02-25 李海沿

    前天我们实现了一个简单的sysfs的kobject的驱动程序,可是有没有发现很多东西都不懂,原因就是在我们对sysfs和kobject的工作原理不懂,虽然我一直不提倡整天接触那些乏味的知识点,也一直不喜欢谈论太多的知识点,但是有的时候,理论知识是实践的基础,有些基础的知识点还是不得不提,下面进入正题。

    一、sysfs介绍

    在linux2.6内核以后,引入了一个新的文件系统sysfs,它挂载于/sys目录下,跟devfs一样它也是一个虚拟文 件系统,也是用来对系统的设备进行管理的,它把实际连接到系统上的设备和总线组织成一个分级的文件,用户空间的程序同样可以利用这些信息以实现和内核的交 互,该文件系统是当前系统上实际设备树的一个直观反应,它是通过kobject子系统来建立这个信息的,当一个kobject被创建的时候,对应的文件和 目录也就被创建了,位于/sys下的相关目录下,既然每个设备在sysfs中都有唯一对应的目录,那么也就可以被用户空间读写了。用户空间的工具udev 就是利用了sysfs提供的信息来实现所有devfs的功能的,但不同的是udev运行在用户空间中,而devfs却运行在内核空间,而且udev不存在 devfs那些先天的缺陷。很显然,sysfs将是未来发展的方向。

    sysfs把连接在系统上的设备和总线组织成为一个分级的目录及文件,它们可以由用户空间存取,向用户空间导出内核数据结构以及它们的属性,这其中就包括设备的主次设备号。新的设备文件系统udev的工作过程就依赖于sysfs文件系统的这些功能特点。udev文件系统在用户空间工作,它可以根据sysfs文件系统导出的信息(设备号(dev)等),动态建立和删除设备文件,而不再需要使用mknod来手动建立设备文件,也不必为查找设备号(尤其是驱动中动态申请产生的设备号)而头疼。

        kobject内核对象提供了基本的对象管理能力,是linux 2.6设备模型的核心结构,每个在内核中注册的kobject对象都对应于sysfs文件系统的一个目录,这可以通过以下函数调用体现,实现流程:

    kobject_add() -> kobject_add_varg() -> kobject_add_internal() -> create_dir() -> sysfs_create_dir()

    sysfs文件系统下的所有目录,其最终的建立过程都是通过注册添加kobject到linux设备层次来实现的,举两个例子:

    1.class目录

    class_register -> __class_register -> kset_register -> kobject_add_internal

        2.device目录

    device_register -> device_add -> kobject_add

    因此,对应于一个设备,在sysfs文件系统下不是一个文件,而是一个目录,目录名字的来源是kobject对象的name域。目录中会有两个文件和若干目录,这两个文件是设备的属性文件。我们可以看看device_add函数的实现,其中有:

    912 error = device_create_file(dev, &uevent_attr); //创建属性文件

    913 if (error)

    914 goto attrError;

    915

    916 if (MAJOR(dev->devt)) { //读取处设备的主设备号

    917 error = device_create_file(dev, &devt_attr); //写入设备号相关的属性文件

    918 if (error)

    919 goto ueventattrError;

    从上面程序中得知,将会在sysfs文件系统的对应设备目录下创建两个属性文件。

        下面我们呢再来分析一下这两个结构体 uevent_attr 和 devt_att到底是什么样子

    311 static struct device_attribute uevent_attr =

    312 __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store _uevent);

    428 static struct device_attribute devt_attr =

    429 __ATTR(dev, S_IRUGO, show_dev, NULL);

    再看看_ATTR:

    48 #define __ATTR(_name,_mode,_show,_store) {

    49 .attr = {.name = __stringify(_name), .mode = _mode },

    50 .show = _show,

    51 .store = _store,

    52 }

    可以发现,翻译一下,其实我们uevent_att属性就是:

    Static struct device_attribute uevent_attr = {

    .attr = {

    .name = uevent , //设备属性文件名

    .mode = S_IRUGO | S_IWUSR, //权限

    }

    .show = show_uevent, //读取设备属性文件

    .store = store _uevent, //写入设备属性文件

    }

    同理,我们也可以得到 devt_attr 的结构:

    Static struct device_attribute devt_attr = {

    .attr = {

    .name = dev , //设备属性文件名

    .mode = S_IRUGO, //权限

    }

    .show = show_dev, //读取设备属性文件

    .store = NULL,

    }

    相应的读取和写入操作,不过就是对这些属性的读取和修改罢了。

    二、udev介绍

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

    下面我们来了解一下,udev如何根据sysfs来自动创建设备节点。

    对于已经编入内核的驱动程序,当被内核检测到的时候,会直接在 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 。

    上面的步骤完成后,那些已经存在并且已经内建驱动的设备就可以使用了,那么以模块驱动的设备呢?

        大多数 Linux 发行版通过 /etc/modules.conf 配置文件来处理模块加载,对某个设备节点的访问导致相应的内核模块被加载。对 udev 这个方法就行不通了,因为在模块加载前,设备节点根本不存在。为了解决这个问题,在 LFS-Bootscripts 软件包里加入了 modules 启动脚本,以及

    /etc/sysconfig/modules 文件。通过在 modules 文件里添加模块名,就可以在系统启动的时候加载这些模块,这样 udev 就可以检测到设备,并创建相应的设备节点了。如果插入的设备有一个驱动程序模块但是尚未加载,Hotplug 软件包就有用了,它就会响应上述的内核总线驱动热插拔事件并加载相应的模块,为其创建设备节点,这样设备就可以使用了。

        udev是一种工具,它能够根据系统中的硬件设备的状况动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下,使用udev 后,在/dev下面只包含系统中真实存在的设备。它于硬件平台无关的,位于用户空间,需要内核sysfs和tmpfs的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间。

    易见udev设备文件的优点:

    1.udev完全在用户态工作,利用设备加入或移除时内核所发送的热插拔事件。在热插拔时,设备的详细信息会由内核输出到位于/sys的sysfs

    文件系统。udev的设备命名策略权限控制都在用户态完成的,它利用sysfs信息来进行创建设备文件节点。

    2.udev根据系统中的硬件设备的状态动态更新设备文件,进行设备文件的创建和删除等。

    注:使用udev,/dev目录下就会只包含系统中真正存在的设备。

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

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

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

    注:所有在 sysfs 中显示的设备都可以由 udev 来创建节点。如果内核中增加了其它设备的支持,

    udev 也就自动地可以为它们工作了。在init初始化之前,udev 可以被放入 initramfs 之中,并在每个设备被发现的时候运行。

    也可以让udev 工作在一个真的根分区被加载之后根据 /sys 的内容创建的初始 /dev 目录之中

    工作流程

    三、devfs介绍

    linux下有专门的文件系统用来对设备进行管理,devfs和sysfs就是其中两种。

    在2.6内核以前一直使用的是devfs,devfs挂载于/dev目录下,提供了一种类似于文件的方法来管理位于/dev目录下的所有设备,我们知道 /dev目录下的每一个文件都对应的是一个设备,至于当前该设备存在与否先且不论,而且这些特殊文件是位于根文件系统上的,在制作文件系统的时候我们就已 经建立了这些设备文件,因此通过操作这些特殊文件,可以实现与内核进行交互。

    但是devfs文件系统有一些缺点,例如:不确定的设备映射,有时一个设备映 射的设备文件可能不同,例如我的U盘可能对应sda有可能对应sdb;没有足够的主/辅设备号,当设备过多的时候,显然这会成为一个问题;/dev目录下 文件太多而且不能表示当前系统上的实际设备;命名不够灵活,不能任意指定等等。

    四、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 。

    注:可以用命令查看其中的信息, udevinfo -a -p /sys/block/sda

    声明,作为一个初学者,很多东西都不懂,本文并非原创,而是将我学习时参考的大牛文章进行整理而来的,下面是部分文章的链接地址,大家可以进入看看原文。

    部分文章出处:

    http://www.360doc.com/content/11/1218/16/1299815_173168170.shtml

    http://www.ibm.com/developerworks/cn/linux/l-cn-udev/

  • 相关阅读:
    判断一个表里面有没有相同的数据
    ASP.NET面试题公司必考<1>
    jQuery 实现三级联动
    javascript 面试大全
    Javascript 实现倒计时跳转页面代码
    SQL删除重复数据只保留一条 .
    编写SQL语句查询出每个各科班分数最高的同学的名字,班级名称,课程名称,分数
    Silverlight 和javascript 之间的调用
    delphi 开放数组参数
    SPCOMM控件在Delphi7.0串口通信中的应用
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4299227.html
Copyright © 2020-2023  润新知