Heim > System-Tutorial > LINUX > Detaillierte Erläuterung des Linux-Gerätemodells (6)_Bus

Detaillierte Erläuterung des Linux-Gerätemodells (6)_Bus

WBOY
Freigeben: 2024-02-12 13:42:13
nach vorne
1103 Leute haben es durchsucht

1. Übersicht

Im Linux-Gerätemodell ist Bus ein besonderer Gerätetyp. Es handelt sich um einen Kanal, der den Prozessor und andere Geräte verbindet. Um die Implementierung des Gerätemodells zu erleichtern, schreibt der Kernel vor, dass jedes Gerät im System mit einem Bus verbunden sein muss. Dieser Bus kann ein interner Bus, ein virtueller Bus oder ein Plattformbus sein.

Detaillierte Erläuterung des Linux-Gerätemodells (6)_Bus

Der Kernel abstrahiert den Bus über die Struktur struct bus_type, die in include/linux/device.h definiert ist. Dieser Artikel konzentriert sich auf diese Struktur, um die Funktionen von Bus im Linux-Kernel und die zugehörige Implementierungslogik zu beschreiben. Abschließend stellen wir kurz einige Standardbusse (z. B. Plattform) vor und stellen deren Verwendung und Nutzungsszenario vor.

2. Funktionsbeschreibung

Wie üblich stellen wir einige Kerndatenstrukturen vor, bevor wir Funktionen einführen. Für das Busmodul ist die Kerndatenstruktur die Struktur struct bus_type. Es gibt auch eine Struktur im Zusammenhang mit dem Subsystem, die wir ebenfalls vorstellen werden.

2.1 struct bus_type
 1: /* inlcude/linux/device.h, line 93 */
 2: struct bus_type {
 3:     const char *name;
 4:     const char *dev_name;
 5:     struct device       *dev_root;
 6:     struct bus_attribute    *bus_attrs;
 7:     struct device_attribute *dev_attrs;
 8:     struct driver_attribute *drv_attrs;
 9:  
 10:    int (*match)(struct device *dev, struct device_driver *drv);
 11:    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
 12:    int (*probe)(struct device *dev);
 13:    int (*remove)(struct device *dev);
 14:    void (*shutdown)(struct device *dev);
 15:  
 16:    int (*suspend)(struct device *dev, pm_message_t state);
 17:    int (*resume)(struct device *dev);
 18:  
 19:    const struct dev_pm_ops *pm;
 20:  
 21:    struct iommu_ops *iommu_ops;
 22:  
 23:    struct subsys_private *p;
 24:    struct lock_class_key lock_key;
 25: };
Nach dem Login kopieren

Name, der Name des Busses, liegt in Form eines Verzeichnisses in sysfs vor. Beispielsweise erscheint der Plattformbus in sysfs als „/sys/bus/platform“.

dev_name, dieser Name bezieht sich auf den init_name in der Strukturgerätestruktur, die in „Linux-Gerätemodell (5)_Gerät und Gerätetreiber“ beschrieben wird. Bei einigen Geräten (z. B. Massen-USB-Geräten) machen sich Designer einfach nicht die Mühe, ihnen einen Namen zu geben, und der Kernel unterstützt diese Faulheit auch und lässt zu, dass der Gerätename leer bleibt. Wenn das Gerät beim Kernel registriert wird, verwendet die Kernlogik des Gerätemodells auf diese Weise die Form „Bus->Gerätename+Geräte-ID“, um einen Namen für ein solches Gerät zu generieren.

bus_attrs, dev_attrs, drv_attrs, einige Standardattribute, können automatisch entsprechende Attribute zu Bus, Gerät oder Gerätetreiber hinzufügen, wenn sie dem Kernel hinzugefügt werden.

dev_root, laut den Kernel-Kommentaren ist das dev_root-Gerät das standardmäßige übergeordnete Gerät des Busses (Standardgerät, das als übergeordnetes Gerät verwendet wird), aber in der tatsächlichen Implementierung des Kernels bezieht es sich nur auf eine Funktion namens Subsystem. was später vorgestellt wird.

match, eine Rückruffunktion, die von einem bestimmten Bustreiber implementiert wird. Wenn ein zum Bus gehörendes Gerät oder ein Gerätetreiber zum Kernel hinzugefügt wird, ruft der Kernel diese Schnittstelle auf. Wenn das neu hinzugefügte Gerät oder der Gerätetreiber mit seiner anderen Hälfte übereinstimmt, gibt die Schnittstelle zu diesem Zeitpunkt einen Wert ungleich Null zurück des Busmoduls Die Logik führt die weitere Verarbeitung durch.

uevent, eine Rückruffunktion, die von einem bestimmten Bustreiber implementiert wird. Wenn ein zum Bus gehörendes Gerät hinzugefügt, entfernt oder anderweitig bearbeitet wird, ruft die Kernlogik des Busmoduls diese Schnittstelle auf, damit der Bustreiber die Umgebungsvariablen ändern kann.

Probe und Remove, diese beiden Rückruffunktionen sind denen in device_driver sehr ähnlich, aber ihre Existenz ist sehr bedeutungsvoll. Sie können sich vorstellen, dass Sie, wenn Sie ein per Probe spezifiziertes Gerät (eigentlich Initialisierung) benötigen, sicherstellen müssen, dass der Bus, an dem sich das Gerät befindet, initialisiert wurde und ordnungsgemäß funktionieren kann. Dies erfordert die Ausführung seines Bus-Tests vor der Ausführung des Device_Driver-Tests. Der Vorgang des Entfernens wird umgekehrt.
Hinweis 1: Nicht alle Busse erfordern Probe- und Entfernungsschnittstellen, da es sich bei einigen Bussen (z. B. Plattformbus) um einen virtuellen Bus handelt. Er muss nicht initialisiert werden und kann daher direkt verwendet werden Beide Rückruffunktionen können leer gelassen werden.

Herunterfahren, Anhalten und Fortsetzen haben ähnliche Prinzipien zum Prüfen und Entfernen. Die Implementierung im Zusammenhang mit der Energieverwaltung wird vorerst nicht erläutert.

pm, die Logik im Zusammenhang mit der Energieverwaltung wird noch nicht erklärt.

iommu_ops, noch nicht erklärt.

p, ein Zeiger vom Typ struct subsys_private, wir werden ihn später in einem Abschnitt erklären.

2.2 struct subsys_private

Diese Struktur ähnelt der Struktur drivers_private in device_driver. Sie wird im Kapitel „Linux-Gerätemodell (5)_Gerät und Gerätetreiber“ erwähnt, aber nicht im Detail erläutert.

要说明subsys_private的功能,让我们先看一下该结构的定义:

 1: /* drivers/base/base.h, line 28 */
 2: struct subsys_private {
 3:     struct kset subsys;
 4:     struct kset *devices_kset;
 5:     struct list_head interfaces;
 6:     struct mutex mutex;
 7:  
 8:     struct kset *drivers_kset;
 9:     struct klist klist_devices;
 10:    struct klist klist_drivers;
 11:    struct blocking_notifier_head bus_notifier;
 12:    unsigned int drivers_autoprobe:1;
 13:    struct bus_type *bus;
 14:  
 15:    struct kset glue_dirs;
 16:    struct class *class;
 17: };
Nach dem Login kopieren

看到结构内部的字段,就清晰多了,没事不要乱起名字嘛!什么subsys啊,看的晕晕的!不过还是试着先理解一下为什么起名为subsys吧:

按理说,这个结构就是集合了一些bus模块需要使用的私有数据,例如kset啦、klist啦等等,命名为bus_private会好点(就像device_driver模块一样)。不过为什么内核没这么做呢?看看include/linux/device.h中的struct class结构(我们会在下一篇文章中介绍class)就知道了,因为class结构中也包含了一个一模一样的struct subsys_private指针,看来class和bus很相似啊。

想到这里,就好理解了,无论是bus,还是class,还是我们会在后面看到的一些虚拟的子系统,它都构成了一个“子系统(sub-system)”,该子系统会包含形形色色的device或device_driver,就像一个独立的王国一样,存在于内核中。而这些子系统的表现形式,就是/sys/bus(或/sys/class,或其它)目录下面的子目录,每一个子目录,都是一个子系统(如/sys/bus/spi/)。

好了,我们回过头来看一下struct subsys_private中各个字段的解释:

subsys、devices_kset、drivers_kset是三个kset,由”Linux设备模型(2)_Kobject”中对kset的描述可知,kset是一个特殊的kobject,用来集合相似的kobject,它在sysfs中也会以目录的形式体现。其中subsys,代表了本bus(如/sys/bus/spi),它下面可以包含其它的kset或者其它的kobject;devices_kset和drivers_kset则是bus下面的两个kset(如/sys/bus/spi/devices和/sys/bus/spi/drivers),分别包括本bus下所有的device和device_driver。

interface是一个list head,用于保存该bus下所有的interface。有关interface的概念后面会详细介绍。

klist_devices和klist_drivers是两个链表,分别保存了本bus下所有的device和device_driver的指针,以方便查找。

drivers_autoprobe,用于控制该bus下的drivers或者device是否自动probe,”Linux设备模型(5)_device和device driver”中有提到。

bus和class指针,分别保存上层的bus或者class指针。

2.3 功能总结

根据上面的核心数据结构,可以总结出bus模块的功能包括:

  • bus的注册和注销
  • 本bus下有device或者device_driver注册到内核时的处理
  • 本bus下有device或者device_driver从内核注销时的处理
  • device_drivers的probe处理
  • 管理bus下的所有device和device_driver

3. 内部执行逻辑分析

3.1 bus的注册

bus的注册是由bus_register接口实现的,该接口的原型是在include/linux/device.h中声明的,并在drivers/base/bus.c中实现,其原型如下:

 1: /* include/linux/device.h, line 118 */
 2: extern int __must_check bus_register(struct bus_type *bus);
Nach dem Login kopieren

该功能的执行逻辑如下:

  • 为bus_type中struct subsys_private类型的指针分配空间,并更新priv->bus和bus->p两个指针为正确的值
  • 初始化priv->subsys.kobj的name、kset、ktype等字段,启动name就是该bus的name(它会体现在sysfs中),kset和ktype由bus模块实现,分别为bus_kset和bus_ktype
  • 调用kset_register将priv->subsys注册到内核中,该接口同时会向sysfs中添加对应的目录(如/sys/bus/spi)
  • 调用bus_create_file向bus目录下添加一个uevent attribute(如/sys/bus/spi/uevent)
  • 调用kset_create_and_add分别向内核添加devices和device_drivers kset,同时会体现在sysfs中
  • 初始化priv指针中的mutex、klist_devices和klist_drivers等变量
  • 调用add_probe_files接口,在bus下添加drivers_probe和drivers_autoprobe两个attribute(如/sys/bus/spi/drivers_probe和/sys/bus/spi/drivers_autoprobe),其中drivers_probe允许用户空间程序主动出发指定bus下的device_driver的probe动作,而drivers_autoprobe控制是否在device或device_driver添加到内核时,自动执行probe
  • 调用bus_add_attrs,添加由bus_attrs指针定义的bus的默认attribute,这些attributes最终会体现在/sys/bus/xxx目录下

3.2 device和device_driver的添加

我们有在”Linux设备模型(5)_device和device driver”中讲过,内核提供了device_register和driver_register两个接口,供各个driver模块使用。而这两个接口的核心逻辑,是通过bus模块的bus_add_device和bus_add_driver实现的,下面我们看看这两个接口的处理逻辑。

这两个接口都是在drivers/base/base.h中声明,在drivers/base/bus.c中实现,其原型为:

 1: /* drivers/base/base.h, line 106 */
 2: extern int bus_add_device(struct device *dev);
 3:  
 4: /* drivers/base/base.h, line 110 */ 
 5: extern int bus_add_driver(struct device_driver *drv);
Nach dem Login kopieren

bus_add_device的处理逻辑:

  • 调用内部的device_add_attrs接口,将由bus->dev_attrs指针定义的默认attribute添加到内核中,它们会体现在/sys/devices/xxx/xxx_device/目录中

  • 调用sysfs_create_link接口,将该device在sysfs中的目录,链接到该bus的devices目录下,例如:

    xxx# ls /sys/bus/spi/devices/spi1.0 -l
    lrwxrwxrwx root root 2014-04-11 10:46 spi1.0 -> ../../../devices/platform/s3c64xx-spi.1/spi_master/spi1/spi1.0
    其中/sys/devices/…/spi1.0,为该device在sysfs中真正的位置,而为了方便管理,内核在该设备所在的bus的xxx_bus/devices目录中,创建了一个符号链接

  • 调用sysfs_create_link接口,在该设备的sysfs目录中(如/sys/devices/platform/alarm/)中,创建一个指向该设备所在bus目录的链接,取名为subsystem,例如:

    xxx # ls /sys/devices/platform/alarm/subsystem -l
    lrwxrwxrwx root root 2014-04-11 10:28 subsystem -> ../../../bus/platform

  • 最后,毫无疑问,要把该设备指针保存在bus->priv->klist_devices中

bus_add_driver的处理逻辑:

  • Weisen Sie Platz für den Treiberstrukturzeiger „driver_private“ (priv) zu, initialisieren Sie priv->klist_devices, priv->driver, priv->kobj.kset und andere Variablen und speichern Sie den Zeiger auf p von device_driver
  • Setzen Sie das Kset des Treibers (priv->kobj.kset) auf das Kset der Bustreiber (bus->p->drivers_kset), was bedeutet, dass sich alle Treiber-Kobjects unter bus->p->drivers_kset (send/sys/bus/) befinden. xxx/drivers-Verzeichnis)
  • Rufen Sie mit dem Namen des Treibers als Parameter die Schnittstelle kobject_init_and_add auf, um das Kobject des Treibers in sysfs zu registrieren, das sich im Verzeichnis /sys/bus/xxx/drivers/ widerspiegelt, z. B. /sys/bus/spi/drivers/spidev
  • Speichern Sie den Treiber in der verknüpften Liste klist_drivers des Busses und wählen Sie basierend auf dem Wert von drivers_autoprobe
  • aus, ob Driver_attach für Probe aufgerufen werden soll
  • Rufen Sie die Schnittstelle „driver_create_file“ auf und erstellen Sie das uevent-Attribut
  • im Verzeichnis des Treibers in sysfs
  • Rufen Sie die Schnittstelle „driver_add_attrs“ auf und erstellen Sie das durch den Zeiger „bus->drv_attrs“ definierte Standardattribut im Verzeichnis des Treibers in sysfs
  • Entscheiden Sie gleichzeitig gemäß dem Flag „suppress_bind_attrs“, ob Bindungs- und Bindungsattribute im Verzeichnis des Treibers in sysfs erstellt werden sollen (Einzelheiten finden Sie in der Einführung in „Linux-Gerätemodell (5)_Gerät und Gerätetreiber“).

3.3 Treibersonde

In „Linux-Gerätemodell (5)_Gerät und Gerätetreiber“ haben wir das Probe-Timing und den Prozess des Treibers eingeführt. Der größte Teil der Logik hängt von der Implementierung des Busmoduls ab, hauptsächlich von den Schnittstellen bus_probe_device und drivers_attach. Ebenso werden diese beiden Schnittstellen in drivers/base/base.h deklariert und in drivers/base/bus.c implementiert.

Das Verhalten dieser beiden Strukturen ist ähnlich und die Logik ist sehr einfach: Suchen Sie nach dem Bus, an dem sie sich befinden, und vergleichen Sie, ob ein Gerätetreiber (oder Gerät) mit demselben Namen vorhanden ist. Wenn ja, das Gerät ist nicht an einen Treiber gebunden (Hinweis: Dies ist sehr wichtig. Dadurch kann derselbe Treiber mehrere Geräte mit demselben Namen steuern (was später in der Beschreibung des Plattformgeräts erwähnt wird) und die Sondenschnittstelle von device_driver wird aufgerufen .

4. Verschiedenes

4.1 Lassen Sie uns noch einmal über das Subsystem sprechen

In der alten Linux-Kernelversion (am Beispiel der von Wowo verwendeten Linux2.6.23-Version des Kernels) rufen alle Verzeichnisse der obersten Ebene (einschließlich einiger sekundärer Verzeichnisse) unter sysfs die Schnittstelle subsystem_register in Form von Subsystem Registered auf zum Kernel, wie zum Beispiel:

/sys/bus/

/sys/devices/

/sys/devices/system/

/sys/block

/sys/kernel/

/sys/slab/

Die Implementierung von subsystem_register war damals sehr einfach, nämlich kset_register aufzurufen, um ein kset zu erstellen. Wir wissen, dass kset eine Sammlung von kobjects ist und in Form eines Verzeichnisses in sysfs dargestellt wird.

In der neuen Version des Kernels (z. B. linux3.10.29, auf die in der Artikelserie „Linux-Kernel-Analyse“ verwiesen wird) hat sich die Implementierung des Subsystems stark geändert, zum Beispiel: Die Schnittstelle subsystem_register wurde entfernt (aber um mit dem Subsystem /sys/device/system kompatibel sein. Das System fügt in drivers/base/bus.c eine interne Schnittstelle von subsys_register hinzu, um die entsprechenden Funktionen zu implementieren. Aufgrund dieser Änderungen gibt es nun zwei Möglichkeiten, das Subsystem zu registrieren:

Methode 1: Rufen Sie in ihren jeweiligen Initialisierungsfunktionen die Schnittstelle kset_create_and_add auf, um das entsprechende Subsystem zu erstellen, einschließlich:

  • bus子系统,/sys/bus/,buses_init(drivers/base/bus.c)
  • class子系统,/sys/class
  • kernel子系统,/sys/kernel
  • firmware子系统,/sys/firmware
  • 等等

其中bus子系统就是本文所讲的Bus模块,而其它的,我们会在后续的文章中陆续讲述。这个方式和旧版本内核使用kset_register接口的方式基本一样。

方式二,在bus模块中,利用subsys_register接口,封装出两个API:subsys_system_register和subsys_virtual_register,分别用于注册system设备(/sys/devices/system/)和virtual设备(/sys/devices/virtual/)。 而该方式和方式一的区别是:它不仅仅创建了sysfs中的目录,同时会注册同名的bus和device。

4.2 system/virtual/platform

在Linux内核中,有三种比较特殊的bus(或者是子系统),分别是system bus、virtual bus和platform bus。它们并不是一个实际存在的bus(像USB、I2C等),而是为了方便设备模型的抽象,而虚构的。

system bus是旧版内核提出的概念,用于抽象系统设备(如CPU、Timer等等)。而新版内核认为它是个坏点子,因为任何设备都应归属于一个普通的子系统(New subsystems should use plain subsystems, drivers/base/bus.c, line 1264),所以就把它抛弃了(不建议再使用,它的存在只为兼容旧有的实现)。

virtaul bus是一个比较新的bus,主要用来抽象那些虚拟设备,所谓的虚拟设备,是指不是真实的硬件设备,而是用软件模拟出来的设备,例如虚拟机中使用的虚拟的网络设备(有关该bus的描述,可参考该链接处的解释:https://lwn.net/Articles/326540/)。

platform bus就比较普通,它主要抽象集成在CPU(SOC)中的各种设备。这些设备直接和CPU连接,通过总线寻址和中断的方式,和CPU交互信息。

我们会在后续的文章中,进一步分析这些特殊的bus,这里就暂时不详细描述了。

4.3 subsys interface

subsys interface是一个很奇怪的东西,除非有一个例子,否则很难理解。代码中是这样注释的:

Interfaces usually represent a specific functionality of a subsystem/class of devices.

字面上理解,它抽象了bus下所有设备的一些特定功能。

kernel使用struct subsys_interface结构抽象subsys interface,并提供了subsys_interface_register/subsys_interface_unregister用于注册/注销subsys interface,bus下所有的interface都挂载在struct subsys_private变量的“interface”链表上(具体可参考2.2小节的描述)。struct subsys_interface的定义如下:

1. /**
2. \* struct subsys_interface - interfaces to device functions
3. \* @name: name of the device function
4. \* @subsys: subsytem of the devices to attach to
5. \* @node: the list of functions registered at the subsystem
6. \* @add_dev: device hookup to device function handler
7. \* @remove_dev: device hookup to device function handler
8. *
9. \* Simple interfaces attached to a subsystem. Multiple interfaces can
10. \* attach to a subsystem and its devices. Unlike drivers, they do not
11. \* exclusively claim or control devices. Interfaces usually represent
12. \* a specific functionality of a subsystem/class of devices.
13. */
14. struct subsys_interface {
15. const char *name;
16. struct bus_type *subsys;
17. struct list_head node;
18. int (*add_dev)(struct device *dev, struct subsys_interface *sif);
19. int (*remove_dev)(struct device *dev, struct subsys_interface *sif);
20. };
Nach dem Login kopieren

name,interface的名称。

subsys,interface所属的bus。

node,用于将interface挂到bus中。

add_dev/remove_dev, zwei Rückruffunktionen, die Kernfunktionen der Subsys-Schnittstelle. Wenn ein Gerät unter dem Bus hinzugefügt oder gelöscht wird, ruft der Buskern den Rückruf „add_dev“ oder „remove_dev“ aller darunter liegenden Subsys-Schnittstellen auf. Designer können in diesen beiden Rückruffunktionen die erforderlichen Funktionen implementieren, z. B. die Bindung des Treibers entsprechend der „spezifischen Funktionalität“ usw.

Die Implementierungslogik der Subsys-Schnittstelle ist relativ einfach und wird hier nicht im Detail beschrieben. Einzelheiten finden Sie im entsprechenden Code in „drivers/base/bus.c“. Darüber hinaus werden wir bei der späteren Analyse des CPUFreq-Frameworks auf Beispiele für die Verwendung der Subsys-Schnittstelle stoßen und dann deren praktische Bedeutung besser verstehen.

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des Linux-Gerätemodells (6)_Bus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:lxlinux.net
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage