Linux内核之dev devfs udev sysfs及关系

Linux 下对设备的管理方式主要有/devsysfs两种,前者是将设备注册为设备节点放入/dev目录下,而后者是在linux2.6内核后引入的新的文件系统。

1. /dev

关于/dev的管理方式,也经历了几代,下面介绍/dev管理方式的发展:

1.1 静态/dev文件:

在Linux中,老的设备管理方式是将设备通过设备节点放入/dev目录下,每个设备节点是/dev根目录下的一个文件,那么。如何区分这些设备节点,为了对这些设备节点进行命名,Linux通过主次设备号来指定不同的设备节点。

有了主次设备号,如何指定主次设备号成了一个开发人员必须面临的问题。如果开发人员不打算将设备驱动程序与外界共享,那么指定什么号码都可以,只要她与当前设备内核使用的其他主设备不冲突即可。然而,如果开发人员想让驱动程序与外界共享(大多数Linux开发人员常常采用这一方法),那么这样随意指定设备号进行了,因为用户和其他开发人员并不知道哪个设备号对应于改设备,因此开发人员必须联系linux内核开发人员分配一个真实主设备好,这样在整个linux世界中,只有这个特定设备号才会被关联到那个特定的设备号(即每一个设备对应一个唯一的设备号),而这个设备号也被汇入Linux的发行版本的/dev目录中。

基于这样的处理方式,就会产生以下问题。

  • 由于不断涌入的新设备,设备号会慢慢耗尽。
  • 这样的申请分配方式对于设备的管理比较麻烦。
  • 由于“正式”设备好不断汇入/dev目录。哪怕该设备在某些硬件上并不存在,这就导致很多设备号指向并存在的一些设备,在后期的/dev目录下甚至有上万个设备号。

2. Devfs

linux kernel 2.4版本后引入devfs,devfs是一个虚拟的为念系统个,相比与静态的/dev文件主要有两点改进:

  • 允许使用自定的设备名称来注册设备节点,同时它兼容老的设备号,例如我们注册一个设备节点/dev/mydev
  • 所有的设备都由内核在系统启动时期创建并注册到/dev目录下,这就意味这/dev不在被成百个“无用”的设备节点充斥。

3. Udev

Devfs解决静态/dev管理的很多问题,但是它任然存在一定缺陷,基于此,在linux kernel 2.6.x版本后,Linux引入了udev.从而对devfs进行改进。udev是一个对/dev下设备节点进行动态管理的用户空间程序,它通过自身的守护进程和自定义的一些列规则来处理设备的加载,移除和热插拔等活动。

udev 以守护进程的形式运行,通过侦听内核发出来的 uevent来管理/dev目录下的设备文件。不像之前的设备管理工具,udev 在用户空间 (user space) 运行,而不在内核空间 (kernel space) 运行。

相比与devfs ,它的主要改进如下:

  • 传统的devfs命名不够灵活,设备名称不可预知,而udev支持设备的固定命名。
    例如如果现在有两个硬盘,在devfs 中,他们们对应的设备节点分别是/dev/sda/dev/sdb ,那么我么就不知道硬盘对应于sda哪个又是sdb ,而udev 提供了存储设备的固定命名,任何硬盘根据其唯一的文件系统id ,磁盘名称及硬件链接的物理位置来进行识别。
  • 设备在热插拔的时候,用户态程序应该有办法得到通知。
    udev运行在用户空间中,设备在热插拔时候,会通过netlink(linux 中内核空间和用户空间进程之间通信的方式)通知udev ,因此用户空间程序可以得到通知了,同时Udev运行在用户空间还可以减少内存的使用。
  • devfs代码不灵活,只显示存在的设备列表,而有时候我们希望看到暂时不存在的设备名字
  • major,minor 快被分配光了,我们需要考虑动态分配方法,而devfs不能支持。
    而当设备较多的时候,不能动态分配节点给设备注册造成很大的麻烦,需要不停尝试不同的设备节点以检查是否冲突。

3.1 使用 udev 的好处:

我们都知道,所有的设备在 Linux 里都是以设备文件的形式存在。在早期的 Linux 版本中,/dev目录包含了所有可能出现的设备的设备文件。很难想象 Linux 用户如何在这些大量的设备文件中找到匹配条件的设备文件。现在 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 添加 / 删除设备文件的过程。

在这里插入图片描述

3.2 udev工作流程

在这里插入图片描述

3.3 相关术语:

  1. 设备文件:由于本文以较通俗的方式讲解 udev,所以设备文件是泛指在 /dev/下,可被应用程序用来和设备驱动交互的文件。而不会特别地区分设备文件、设备节点或者设备特殊文件。
  2. devfsdevfs是 Linux 早期的设备管理工具,已经被udev取代。
  3. sysfssysfs是 Linux 2.6 内核里的一个虚拟文件系统 (/sys)。它把设备和驱动的信息从内核的设备模块导出到用户空间 (userspace)。从该文件系统中,Linux 用户可以获取很多设备的属性。
  4. devpath:本文的 devpath是指一个设备在 sysfs文件系统 (/sys)下的相对路径,该路径包含了该设备的属性文件。udev 里的多数命令都是针对devpath操作的。例如:sdadevpath/block/sdasda2devpath/block/sda/sda2
  5. 内核设备名称:设备在 sysfs里的名称,是 udev 默认使用的设备文件名。

3.4 使用 udev 进行动态内核设备管理

内核几乎可以添加或删除运行系统中的任何设备。设备状态的更改(无论插入还是移除设备)需要传播给用户空间。插入及识别设备后需要对其进行配置。某个设备已识别状态的任何更改都需要通知给此设备的用户。udev 可提供所需的基础结构来动态维护
/dev目录中的设备节点文件和符号链接。udev 规则提供了将外部工具插入内核设备事件处理的方式。因而,您可以通过添加在内核设备处理过程中执行的特定脚本,来自定义 udev 设备处理方式,或者可以在设备处理期间请求并导入其他数据进行评估。

3.4.1 /dev 目录

/dev 目录中的设备节点提供对相应的内核设备的访问。使用 udev 时,/dev 目录反映内核的当前状态。每个内核设备都有相应的设备文件。如果设备从系统断开,则删除此设备节点。

/dev 目录的内容保存在临时文件系统中,所有文件都是在每个系统启动时提供的。手动创建或修改的文件在重引导时是有意不保存的。无论可使用 systemd-tmpfiles 创建的相应内核设备状态如何,静态文件和目录都始终应位于/dev目录中。配置文件位于/usr/lib/tmpfiles.d//etc/tmpfiles.d/ 中。有关详细信息,请参见 systemd-tmpfiles(8) 手册页。

3.4.2 内核 uevents 和 udev

必需的设备信息由 sysfs 文件系统导出。对于内核检测到并已初始化的设备,将创建一个带有该设备名称的目录。它包含带有特定于设备属性的属性文件。

每次添加或删除设备时,内核都会发送 uevent 来向udev通知更改。一旦启动,udev 守护程序便会读取并分析/usr/lib/udev/rules.d/*.rules/etc/udev/rules.d/*.rules文件中的所有规则,并将它们保留在内存中。如果更改、添加或去除了规则文件,守护程序可以使用 udevadm control --reload 命令重新装载这些规则在内存中的表示。

每个接收到的事件都根据所提供的规则集进行匹配。这些规则可以增加或更改事件环境键、为要创建的设备节点请求特定名称、添加指向该节点的符号链接或者添加设备节点创建后运行的程序。从内核 netlink 套接字接收驱动程序核心 uevent

3.4.3 驱动程序、内核模块和设备

设备的内核总线驱动程序探测。对于每个检测到的设备,内核都会在驱动程序核心将 uevent 发送到 udev 守护程序时创建内部设备结构。总线设备通过特殊格式的ID来标识自己,这可以识别设备的类型。通常,这些 ID 由供应商和产品ID以及其他特定于子系统的值组成。每个总线都有自己对于这些ID的方案,称为 MODALIAS。内核获取设备信息,由此组成一个 MODALIAS ID 字符串,并将该字符串与事件一起发送。对于 USB 鼠标,如下所示:

MODALIAS=usb:v046DpC03Ed2000dc00dsc00dp00ic03isc01ip02

每个设备驱动程序都带有它可以处理的设备的已知别名列表。这个列表包含在内核模块文件中。程序 depmod 读取ID列表并在内核的/lib/modules目录中为所有当前可用的模块创建文件 modules.alias。使用这种基础结构,模块的装载就如为每个带有 MODALIAS 键的事件调用 modprobe 一样简单。如果调用 modprobe $MODALIAS,它将组成该设备的设备别名与模块提供的别名相匹配。如果找到匹配的项,则装载该模块。所有这些操作均由 udev 自动触发。

3.4.4 导和启动设备设置

udev 守护程序运行之前的引导进程中发生的所有设备事件都会丢失,因为处理这些事件的基础结构保存在 root 文件系统中,并且此时不可用。为了弥补此损失,内核提供了一个uevent文件,该文件位于 sysfs 文件系统每个设备的设备目录中。通过将 add 写入到该文件,内核将再次发送引导时丢失的相同事件。/sys 触发器中所有 uevent 文件的简单循环将再次触发所有事件来创建设备节点并执行设备设置。

例如,在引导期间出现的 USB 鼠标可能不会由早期引导逻辑初始化,因为驱动程序在那时不可用。此设备发现的事件丢失并且不能为该设备查找内核模块。您无需手动搜索连接的设备,udev 会在根文件系统可用后向内核请求所有设备事件,这样USB鼠标设备的事件就会再次运行。现在它在装入的 root 文件系统上找到内核模块,因此可以初始化 USB 鼠标。

在用户空间中,设备冷插入序列和运行时期间发现的设备之间没有明显的区别。在这两种情况下,使用相同的规则来匹配并且运行相同的配置程序。

3.4.5 监视正在运行的 udev 守护程序

程序 udevadm monitor 可以用于将驱动程序核心事件和 udev 事件处理的计时可视化。

uos@uos-PC:~$ sudo udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[4224592.359213] change   /devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0 (drm)
UDEV  [4224592.360312] change   /devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0 (drm)
KERNEL[4224592.362848] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010/input/input28/mouse0 (input)
UDEV  [4224592.363398] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010/input/input28/mouse0 (input)
KERNEL[4224592.395695] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010/input/input28/event3 (input)
UDEV  [4224592.396294] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010/input/input28/event3 (input)
KERNEL[4224592.419703] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010/input/input28 (input)
KERNEL[4224592.419788] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010/hidraw/hidraw0 (hidraw)
KERNEL[4224592.419803] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010 (hid)
KERNEL[4224592.419816] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010 (hid)
KERNEL[4224592.419829] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0 (usb)
KERNEL[4224592.419846] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0 (usb)
UDEV  [4224592.420271] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010/input/input28 (input)
UDEV  [4224592.420823] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010/hidraw/hidraw0 (hidraw)
KERNEL[4224592.420841] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2 (usb)
KERNEL[4224592.420856] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2 (usb)
UDEV  [4224592.421182] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010 (hid)
UDEV  [4224592.421452] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:093A:2510.0010 (hid)
UDEV  [4224592.421799] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0 (usb)
UDEV  [4224592.422062] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0 (usb)

UEVENT 行显示内核已经通过 netlink 发送的事件。UDEV 行显示已经完成的 udev 事件处理程序。计时以微秒为单位显示。UEVENT UDEV 之间的时间是 udev 用于处理此事件或者udev守护程序延迟执行从而同步此事件与相关以及已运行的事件的时间。例如,硬盘分区的事件总是等待主磁盘设备事件完成,因为分区事件可能依赖于主磁盘事件从硬件查询的数据。

udevadm monitor --env 显示完整的事件环境:

KERNEL[4224693.719653] add      /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2
SUBSYSTEM=usb
DEVNAME=/dev/bus/usb/001/023
DEVTYPE=usb_device
PRODUCT=93a/2510/100
TYPE=0/0/0
BUSNUM=001
DEVNUM=023
SEQNUM=5288
MAJOR=189
MINOR=22

KERNEL[4224693.723087] change   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2 (usb)
ACTION=change
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2
SUBSYSTEM=usb
DEVNAME=/dev/bus/usb/001/023
DEVTYPE=usb_device
DRIVER=usb
PRODUCT=93a/2510/100
TYPE=0/0/0
BUSNUM=001
DEVNUM=023
SEQNUM=5289
MAJOR=189
MINOR=22

KERNEL[4224693.723116] add      /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2/1-2.2:1.0 (usb)

udev 也将消息发送给 syslog。用于控制将哪些消息发送到系统日志的默认系统日志优先级在 udev 配置文件/etc/udev/udev.conf中指定。可以使用 udevadm control --log_priority=LEVEL/NUMBER 更改正在运行的守护程序的日志优先级。

3.4.6 使用 udev 规则影响内核设备事件处理

udev 规则可以与内核添加到事件本身的属性或者内核导出到 sysfs 的任何信息匹配。规则还可以从外部程序请求其他信息。系统会将事件与目录/usr/lib/udev/rules.d/(适用于默认规则)和 /etc/udev/rules.d(系统特定的配置)中提供的所有规则进行匹配。

规则文件中的每一行至少包含一个键值对。有两种类型的键,匹配键和指派键。如果所有匹配键与它们的值匹配,则应用此规则并将指派键指派给特定的值。匹配规则可以指定设备节点的名称、添加指向该节点的符号链接或者运行作为事件处理一部分的特定程序。如果找不到匹配的规则,则使用默认设备节点名来创建设备节点。udev 手册页中描述了有关规则语法和提供用来与数据匹配或导入数据的键的详细信息。以下示例规则提供了 udev 规则语法的基本介绍。这些示例规则全部取自 udev 默认规则集 /usr/lib/udev/rules.d/50-udev-default.rules

4. sysfs

sysfsLinux2.6引入的一种虚拟文件系统,挂载于/sys目录下,这个文件系统吧实际链接到系统上的设备,总线及其对应的驱动程序组织成分级的文件。从而将设备的层次结构映射到用户空间中,用户空间可以通过修改sysfs 中文件属性来修改设备属性值,从而与内核设备交互。

sysfs 本质上是对 统一设备模型 中各结构的映射。这在前文对统一设备模型的叙述中也略有提到。换句话说,sysfs 本质上就是通过 VFS 的接口去读写kobject的层次结构后动态建立的内存文件系统。

4.1 sys目录架构

进入到 sys 目录下,可以看到如下文件:

uos@uos-PC:/sys$ ls -alh
总用量 0
dr-xr-xr-x  13 root root   0 1119 02:40 .
drwxr-xr-x  17 root root 271 1113 00:42 ..
drwxr-xr-x   2 root root   0 1119 02:40 block
drwxr-xr-x  47 root root   0 1119 02:40 bus
drwxr-xr-x  77 root root   0 1119 02:40 class
drwxr-xr-x   4 root root   0 1119 02:40 dev
drwxr-xr-x  32 root root   0 1119 02:40 devices
drwxr-xr-x   6 root root   0 1119 02:40 firmware
drwxr-xr-x   9 root root   0 1119 02:40 fs
drwxr-xr-x   2 root root   0 16 14:26 hypervisor
drwxr-xr-x  14 root root   0 1119 02:40 kernel
drwxr-xr-x 225 root root   0 1119 02:40 module
drwxr-xr-x   3 root root   0 1119 02:40 power

device / driver / bus / class 四者之间存在着这样的关系:

  • driver 用于驱动 device ,其保存了所有能够被它所驱动的设备链表。
  • bus 是连接 CPU device 的桥梁,其保存了所有挂载在它上面的设备链表和驱动这些设备的驱动链表。
  • class 用于描述一类 device ,其保存了所有该类 device 的设备链表。

4.2 sys 目录介绍

/sys 硬件设备的驱动程序信息

block bus class dev devices firmware fs kernel module power

/sys/devices (/sys文件系统最重要的目录结构)
该目录下是全局设备结构体系,包含所有被发现的注册在各种总线上的各种物理设备。一般来说,所有的物理设备都按
其在总线上的拓扑结构来显示,但有两个例外,即platform devices和system devices。platform devices
一般是挂在芯片内部的高速或者低速总线上的各种控制器和外设,它们能被CPU直接寻址;system devices不是外
设,而是芯片内部的核心结构,比如CPU,timer等,它们一般没有相关的驱动,但是会有一些体系结构相关的代码来
配置它们。

/sys/dev
该目录下有字符设备(block)和块设备(char)两个子目录,里面全是以主次设备号(major:minor)命名的链接文
件,链接到/sys/devices。

/sys/class (按功能分类设备)
该目录下包含所有注册在kernel里面的设备类型,每个设备类型表达具有一种功能的设备。每个设备类型子目录下是
具体设备的符号链接,这些链接指向/sys/devices/…下的具体设备。设备类型和设备并没有一一对应的关系,一个
物理设备可能具备多种设备类型;一个设备类型只表达具有一种功能的设备,比如:系统所有输入设备都会出现
在/sys/class/input之下,而不论它们是以何种总线连接到系统的。(/sys/class也是构成linux统一设备模型的
一部分)

/sys/block (从linux2.6.26版本开始已经移到了/sys/class/block)
代表着系统中当前被发现的所有块设备。按照功能来说防止在/sys/class下会更合适,但由于历史遗留因素而一直存
在于/sys/block,但从linux2.6.22内核开始这部分就已经标记为过去时,只有打开了
CONFIG_SYSFS_DEPRECATED配置编译才会有这个目录存在,并且其中的内容在从linux2.6.26版本开始已经正式
移到了/sys/class/block,旧的接口/sys/block为了向后兼容而保留存在,但其中的内容已经变为了指向它们
在/sys/devices/中真实设备的符号链接文件。

/sys/bus (按总线类型分类设备)
一般来说每个子目录(总线类型)下包含两个子目录,一个是devices,另一个是drivers;其中devices下是这个总
线类型下的所有设备,这些设备都是符号链接,它们分别指向真正的设备(/sys/devices/…下);而drivers下是所
有注册在这个总线上的驱动,每个driver子目录下 是一些可以观察和修改的driver参数。 (它也是构成linux统一
设备模型的一部分)

/sys/module
该目录包含所有被载入Kernel的模块,无论这些模块是以内联(inlined)方式编译到内核映像文件中还是编译为外模
块(.ko文件)

/sys/fs
该目录用来描述系统中所有的文件系统,包括文件系统本身和按照文件系统分类存放的已挂载点。

/sys/kernel
该目录下存放的是内核中所有可调整的参数

/sys/firmware
该目录下包含对固件对象(firmware object)和属性进行操作和观察的接口,即这里是系统加载固件机制的对用户空
间的接口.(关于固件有专用于固件加载的一套API)

/sys/power
该目录下有几个属性文件可以用于控制整个机器的电源状态,如向其中写入控制命令让机器关机/重启等等。
  • /sys/devices:这是系统中所有设备存放的目录,也就是系统中的所有设备在 sysfs 中的呈现、表达,也是 sysfs 管理设备的最重要的目录结构,该目录下是全局设备结构体系,包含所有被发现的注册在各种总线上的各种物理设备。一般来说,所有的物理设备都按其在总线上的拓扑结构来显示,但有两个例外即platform devicessystem devices
    所有在总线上被发现的物理设备,比如网卡、显卡、ACPI、虚拟设备(ttybonding等),是内核对系统中所有设备的分层次表达模型。

  • /sys/block :块设备的存放目录,这是一个过时的接口,按照 sysfs 的设计理念,系统所有的设备都存放在/sys/devices 目录下,所以/sys/block 目录下的文件通常是链接到/sys/devices 目录下的文件。

  • /sys/bus:这是系统中的所有设备按照总线类型分类放置的目录结构,/sys/devices 目录下每一种设备都是挂在某种总线下的,譬如i2c设备挂在I2C总线下。同样,/sys/bus 目录下的文件通常也是链接到了/sys/devices 目录。
    该目录下的每个子目录都是kernel支持并且已经注册了的总线类型。这是内核设备按照总线类型分层放置的目录结构,/sys/devices中的所有设备都是连接于某种总线之下的,bus子目录下的每种具体总线之下可以找到每个具体设备的符号链接,一般来说每个子目录(总线类型)下包含两个子目录,一个是 devices,另一个是drivers

    • 其中devices下是这个总线类型下的所有设备,这些设备都是符号链接,它们分别指向真正的设备(/sys/devices/下);
    • drivers下是所有注册在这个总线上的驱动,每个driver子目录下 是一些可以观察和修改的driver参数。
  • /sys/class:这是系统中的所有设备按照其功能分类放置的目录结构,同样该目录下的文件也是链接到了/sys/devices 目录。按照设备的功能划分组织在/sys/class 目录下,譬如/sys/class/leds目录中存放了所有的 LED 设备,/sys/class/input 目录中存放了所有的输入类设备。

  • /sys/dev:这是按照设备号的方式放置的目录结构,同样该目录下的文件也是链接到了/sys/devices 目录。该目录下有很多以主设备号:次设备号(major:minor)命名的文件,这些文件都是链接文件,链接到/sys/devices 目录下对应的设备。

  • /sys/firmware:描述了内核中的固件,这里是系统加载固件机制的对用户空间的接口,关于固件有专用于固件加载的一套API,在 LDD3 一书中有关于内核支持固件加载机制的更详细的介绍;

  • /sys/fs:用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点。

  • /sys/kernel:这里是内核中所有可调参数的位置。

  • /sys/module:这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件中还是编译为外模块(.ko文件),都可能出现在/sys/module中。即module目录下包含了所有的被载入kernel的模块。

  • /sys/power:这里是系统中电源选项,有一些属性可以用于控制整个系统的电源状态,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机/重启等等。

5. udev和sysfs 的关系

sysfs是对devfs改进。两者之间的区别与联系为:实际上用户的工具udev就是利用sysfs提供的信息来实现的:udev会根据sysfs里面的设备信息创建/dev目录下的相应设备节点。

6. udev和devfs的区别

devfs(设备文件系统)是由Linux2.4内核引入的,它的出现主要使得设备驱动程序能够自主管理自己的设备文件。具体来说,devfs具有如下优点:

  • 可以通过程序在设备初始化时在/dev目录下创建设备文件,卸载设备时将它删除。
  • 设备驱动程序可以指定设备名、所有者和权限位,用户空间程序仍可以修改所有者和权限位。
  • 不再需要为设备驱动程序分配主设备号以及处理次设备号,在程序中可以直接给register_chrdev()传递0主设备号以动态获得可用的主设备号,并在devfs_register()中指定次设备号。

创建设备目录、设备文件以及删除函数如下:

devfs_handle_t devfs_mk_dir(devfs_handle_t dir, const char *name, void *info);
devfs_handle_t devfs_register(devfs_handle_t dir, const char *name, unsigned int flags, unsigned int major, unsigned int minor, umode_t mode
void devfs_unregister(devfs_handle_t de);

使用devfs的例子如下:

static devfs_handle_t devfs_handle;
static int __init xxx_init(void)
{
int ret;
int I;
/*在内核中注册设备*/
ret = register_chrdev(XXX_major, DEVICE_NAME, &xxx_fops);
if( ret < 0)
{
   printk(DEVICE_NAME “ can’t register major number\n”);
   return ret;
}
/*创建设备文件*/
devfs_handle = devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT, xxx_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &xxx_fops, NULL);printk(DEVICE_NAME “ initialized\n”);
return 0;
}
static void __exit xxx_exit(void)
{
devfs_unregister(devfs_handle);/*撤消设备文件*/
unregister_chrdev(XXX_MAJOR, DEVICE_NAME);/*注销设备*/
}
 
module_init(xxx_init);
module_exit(xxx_exit);

在最新的Linux 2.6的内核中, devfs已经被抛弃,udev取代了它。udev取代devfs的几点原因如下:

  • devfs所做的工作被确信可以在用户态来完成
  • 一些bug相当长的时间内未被修复
  • devfs的维护者和作者停止了对代码的维护工作。
  • udev完全在用户态工作,利用设备加入或移除内核所发送的热插拔事件来工作。在热插拔时,设备的详细信息会由内核输入到位于/syssysfs文件系统。udev的设备名策略、权限控制和事件处理都是在用户态下完成的,它利用sysfs中的信息来进行创建设备文件节点工作。
  • 由于udev根据系统中硬件设备的状态动态更新设备文件、进行设备文件的创建和删除等,因此,在使用udev后,在/dev目录下就只包含系统中真正存在的设备了。
  • devfsudev的另一个显著区别在于:采用devfs,当一个并不存在的/dev节点被打开的时候,devfs能自动加载对应的驱动,而udev则不能。这是因为udev的设计者认为Linux应该在设备被发现的时候加载驱动模块,而不是当它被访问的时候。 udev的设计者认为devfs所提供的打开/dev节点时自动加载驱动的功能对于一个配置正确的计算机是多余的。系统中所有的设备都应该产生热插拔事件并加载恰当的驱动,而udev能注意到这点并且为它创建对应的设备节点。

使用udev的例子如下:

#include <linux/device.h>static struct class * xxx_class;
static int __init xxx_init(void)
{
int ret;
int i;
/*在内核中注册设备*/
ret = register_chrdev(XXX_major, DEVICE_NAME, &xxx_fops);
if( ret < 0)
{
   printk(DEVICE_NAME “ can’t register major number\n”);
   return ret;
}
//注册一个类,使mdev可以在"/dev/"目录下面建立设备节点
xxx_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(led_class))
{
   printk("Err: failed in EmbedSky-leds class. \n");
   return -1;
}
//创建一个设备节点,节点名为DEVICE_NAME
class_device_create(xxx_class, NULL, MKDEV(xxx_major, 0), NULL, DEVICE_NAME);printk(DEVICE_NAME “ initialized\n”);
return 0;
}
static void __exit xxx_exit(void)
{
unregister_chrdev(XXX_MAJOR, DEVICE_NAME);/*注销设备*/
class_device_destroy(xxx_class, MKDEV(xx_major, 0));//删掉设备点
class_destroy(xxx_class);               //注销类
}
 
module_init(xxx_init);
module_exit(xxx_exit);

mdevbusybox自带的一个简化版的udev,适合于嵌入式的应用埸合。其具有使用简单的特点。它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所需的节点文件。在以busybox为基础构建嵌入式linux的根文件系统时,使用它是最优的选择。

7. /dev、/sys/devices 和 /sys/dev 之间区别

  1. /dev:设备文件存储目录,应用程序通过对这些文件的读写和控制,可以访问实际的设备;

  2. /sys/devices 目录,按照设备挂接的总线类型,组织成层次结构,保存了系统所有的设备;是文件系统管理设备的最重要的目录结构;
    这是内核设备\color{red}{按总线类型分层放置的目录结构}devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分;

  3. /sys/dev 下有两个子目录,blockchar,存放的是\color{red}{块设备和字符设备的主次号码},形式为(major:minor),它\color{red}{指向 /sys/devices 目录下的设备}

  4. sysfs的挂载点是/sys目录, sysfs是一个虚拟的文件系统(还有其它的虚拟文件系统,例如usbfsprocfs),sysfs导出了内核的数据结构。

  5. /sys/dev//sys/devicessysfs按面向对象管理的思想来组织,sysfs最主要是用来描绘Linux kernel 2.6中的设备驱动模型,用户态的后台程序(udev)会动态地周期性的扫描/sys目录中的属性项来自动管理设备文件(也称为设备节点),从而在/dev目录会建立或者删除对应的设备文件。

  6. /dev 下放的是设备文件,是由应用层mknod创建的文件。如果底层驱动对mknod的设备号有对应的驱动,如open等函数,那么应用层open "/dev/**"时,就会调用到底层的驱动。说白了,/dev下放的是内核和应用层交互的文件,让应用层去open,write,poll等。

  7. /sys 是个文件系统,你写内核代码时,如果有调用kobj_init等函数,就会在/sys下的相应目录生成相应文件。 它的作用是将内核注册的设备、驱动、BUS连成一个树形结构。 另外,应用层也可以通过读写/sys下的文件和内核进行交互(ktype)。 说白了/sys就是一个树形结构,让你明白内核都有哪些驱动和设备已经bus,方便电源管理。

  8. /dev下的文件是真实的设备,由UDEV在运行时创建。

  9. /sys/class 则是由kernel在运行时导出的,目的是通过文件系统暴露出硬件的层级关系。

    The files in /dev are actual devices files which UDEV creates at run
    time. The directory /sys/class is exported by the kernel at run time,
    exposing the hierarchy of the hardware through sysfs.

  10. /dev 下放的是设备文件,是由应用层mknod创建的文件。如果底层驱动对mknod的设备号有对应的驱动,如open等函数,那么应用层open "/dev/**"时,就会调用到底层的驱动。说白了,/dev下放的是内核和应用层交互的文件,让应用层去open,write,poll等。

  11. /sys 是个文件系统,你写内核代码时,如果有调用kobj_init等函数,就会在/sys下的相应目录生成相应文件。 它的作用是将内核注册的设备、驱动、BUS连成一个树形结构。 另外,应用层也可以通过读写/sys下的文件和内核进行交互(ktype)。 说白了/sys就是一个树形结构,让你明白内核都有哪些驱动和设备已知bus,方便电源管理。

sysfs.rst

Top Level Directory Layout
~~~~~~~~~~~~~~~~~~~~~~~~~~

The sysfs directory arrangement exposes the relationship of kernel
data structures.

The top level sysfs directory looks like::

    block/
    bus/
    class/
    dev/
    devices/
    firmware/
    net/
    fs/

devices/ contains a filesystem representation of the device tree. It maps
directly to the internal kernel device tree, which is a hierarchy of
struct device.

bus/ contains flat directory layout of the various bus types in the
kernel. Each bus's directory contains two subdirectories::

	devices/
	drivers/

devices/ contains symlinks for each device discovered in the system
that point to the device's directory under root/.

drivers/ contains a directory for each device driver that is loaded
for devices on that particular bus (this assumes that drivers do not
span multiple bus types).

fs/ contains a directory for some filesystems.  Currently each
filesystem wanting to export attributes must create its own hierarchy
below fs/ (see ./fuse.txt for an example).

dev/ contains two directories char/ and block/. Inside these two
directories there are symlinks named <major>:<minor>.  These symlinks
point to the sysfs directory for the given device.  /sys/dev provides a
quick way to lookup the sysfs interface for a device from the result of
a stat(2) operation.

More information can driver-model specific features can be found in
Documentation/driver-api/driver-model/.

udev:  
-----------------------------  
udev is a device management framework that replaced the devfs facility in the Linux 2.6 kernel. 
It is composed of some kernel services and the udevd daemon. The kernel informs the udevd daemon when certain events happen. 
The udevd daemon is configured to respond to some events with actions.
Always, it means adding/removing device file under /dev dynamically.  
  
* When the kernel detects that a device has been added or removed, a uevent is sent to  
the udevd daemon through a netlink socket  
* When udevd receives the uevent, it matches its configured rules against the available  
device attributes provided in sysfs  
* If a match is found, one or more actions (e.g., create device node, remove device node,  
install firmware, etc.) are taken  

8. proc

在Linux中查看各种状态,其实质是查看内核中相关进程的数据结构中的项,通过工具将其格式化后输出出来。但是内核的数据是绝对不能随意查看或更改的,至少不能直接去修改。所以,在linux上出现了伪文件系统/proc,它是内核中各属性或状态向外提供访问和修改的接口。

/proc 正在运行的内核信息映射,主要输出的信息为:进程信息、内存资源信息、磁盘分区信息等。

/proc下文件基本都是只读的,除了/proc/sys目录,它是可写的(查看和修改内核的运行参数)

/proc下数字命令的目录就是对于PID的进程目录

/proc/cmdline 启动时传递给kernel的参数信息(就是bootargs信息)
/proc/cpuinfo cpu的信息
/proc/crypto 内核使用的所有已安装的加密密码及细节
/proc/devices 已经加载的设备并分类
/proc/dma 已注册使用的ISA DMA频道列表
/proc/execdomains Linux 内核当前支持的execution domains
/proc/fb 帧缓冲设备列表,包括数量和控制它的驱动
/proc/filesystems 内核当前支持的文件系统类型
/proc/interrupts x86架构中的每个IRQ中断数
/proc/iomem 每个物理设备当前在系统内存中的映射
/proc/ioports 一个设备的输入输出所使用的注册端口范围
/proc/kcore 代表系统的物理内存,存储为核心文件格式,里边显示的是字节数,等于RAM大小加上4kb
/proc/kmsg 记录内核生成的信息,可以通过/sbin/klogd或/bin/dmesg来处理
/proc/loadavg 根据过去一段时间内CPU和IO的状态得出的负载状态,与uptime命令有关
/proc/locks 内核锁住的文件列表
/proc/mdstat 多硬盘,RAID配置信息(md=multiple disks)
/proc/meminfo RAM使用的相关信息
/proc/misc 其他的主要设备(设备号为10)上注册的驱动
/proc/modules 所有加载到内核的模块列表
/proc/mounts 系统中使用的所有挂载
/proc/partitions 分区中的块分配信息
/proc/pci 系统中的PCI设备列表
/proc/slabinfo 系统中所有活动的 slab 缓存信息
/proc/stat 所有的CPU活动信息
/proc/uptime 系统已经运行了多久
/proc/swaps 交换空间的使用情况
/proc/version Linux内核版本和gcc版本
/proc/bus 系统总线(Bus)信息,例如pci/usb等
/proc/driver 驱动信息
/proc/fs 文件系统信息
/proc/ide ide设备信息
/proc/irq 中断请求设备信息
/proc/net 网卡设备信息
/proc/scsi scsi设备信息
/proc/tty tty设备信息
/proc/net/dev 显示网络适配器及统计信息
/proc/vmstat 虚拟内存统计信息
/proc/vmcore 内核panic时的内存映像
/proc/diskstats 取得磁盘信息
/proc/schedstat kernel调度器的统计信息
/proc/zoneinfo 显示内存空间的统计信息,对分析虚拟内存行为很有用

以下是/proc目录中进程N的信息
/proc/N/cmdline 进程启动命令
/proc/N/cwd 链接到进程当前工作目录
/proc/N/environ 进程环境变量列表
/proc/N/exe 链接到进程的执行命令文件
/proc/N/fd 包含进程相关的所有的文件描述符 (ls /proc//fd | wc -l 查看某个进程打开多少FD)
/proc/N/maps 与进程相关的内存映射信息
/proc/N/mem 指代进程持有的内存,不可读
/proc/N/root 链接到进程的根目录
/proc/N/stat 进程的状态
/proc/N/statm 进程使用的内存的状态
/proc/N/status 进程状态信息,比stat/statm更具可读性
/proc/self 链接到当前正在运行的进程
系统在什么情况下才会使用SWAP?

实际上,并不是等所有的物理内存都消耗完毕之后,才去使用swap的空间,什么时候使用是由swappiness 参数值控制。

[root@rhce ~]# cat /proc/sys/vm/swappiness

60

该值默认值是60.

swappiness=0的时候表示最大限度使用物理内存,然后才是 swap空间,

swappiness=100的时候表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面。

如何修改swappiness参数?

--临时性修改:

[root@rhce ~]# sysctl vm.swappiness=10

vm.swappiness = 10

[root@rhce ~]# cat /proc/sys/vm/swappiness

10

这里我们的修改已经生效,但是如果我们重启了系统,又会变成60.

--永久修改:

在/etc/sysctl.conf 文件里添加如下参数:

vm.swappiness=10

或者:

[root@rhce ~]# echo 'vm.swappiness=10' >>/etc/sysctl.conf

sysctl.conf 配置 https://www.jianshu.com/p/87ec508be2c5
swapoff -a && swapon -a 然后执行 sysctl -p 命令,使上述修改生效。

refer to

  1. https://documentation.suse.com/zh-cn/sles/12-SP4/html/SLES-all/cha-udev.html
  2. sysfs、udev 和 它们背后的 Linux 统一设备模型
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值