>  기사  >  시스템 튜토리얼  >  Linux 장치 모델에 대한 자세한 설명(6)_버스

Linux 장치 모델에 대한 자세한 설명(6)_버스

WBOY
WBOY앞으로
2024-02-12 13:42:131015검색

1. 개요

Linux 장치 모델에서 버스는 프로세서와 기타 장치를 연결하는 특별한 유형의 장치입니다. 장치 모델의 구현을 용이하게 하기 위해 커널은 시스템의 각 장치가 버스에 연결되어야 한다고 규정합니다. 이 버스는 내부 버스, 가상 버스 또는 플랫폼 버스일 수 있습니다.

Linux 장치 모델에 대한 자세한 설명(6)_버스

커널은 include/linux/device.h에 정의된 structbus_type 구조를 통해 버스를 추상화합니다. 이 기사에서는 이 구조에 초점을 맞춰 Linux 커널의 버스 기능과 관련 구현 논리를 설명합니다. 마지막으로 몇 가지 표준 버스(예: 플랫폼)를 간략하게 소개하고 해당 버스의 사용 및 사용 시나리오를 소개합니다.

2. 기능 설명

평소처럼 기능을 소개하기 전에 몇 가지 핵심 데이터 구조를 소개합니다. 버스 모듈의 경우 핵심 데이터 구조는 structbus_type 구조입니다. 또한 우리가 소개할 서브시스템과 관련된 구조도 있습니다.

2.1 구조체 버스 유형
으아아아

버스의 이름인 name은 sysfs에 디렉터리 형태로 존재하게 됩니다. 예를 들어 sysfs에서는 플랫폼 버스가 "/sys/bus/platform"으로 나타납니다.

dev_name, 이 이름은 "Linux 장치 모델(5)_device 및 장치 드라이버"에 설명된 구조체 장치 구조의 init_name과 관련됩니다. 일부 장치(예: 대용량 USB 장치)의 경우 설계자는 이름을 지정하는 데 신경을 쓰지 않으며 커널도 이러한 게으름을 지원하고 장치 이름을 공백으로 둘 수 있습니다. 이러한 방식으로 장치가 커널에 등록되면 장치 모델의 핵심 논리는 "버스->dev_name+장치 ID" 형식을 사용하여 해당 장치의 이름을 생성합니다.

bus_attrs, dev_attrs, drv_attrs, 일부 기본 속성은 커널에 추가될 때 버스, 장치 또는 device_driver에 해당 속성을 자동으로 추가할 수 있습니다.

dev_root, 커널 설명에 따르면 dev_root 장치는 버스의 기본 상위 장치(상위로 사용하는 기본 장치)이지만 실제 커널 구현에서는 서브 시스템이라는 기능에만 관련되어 있으며, 나중에 소개하겠습니다.

match, 특정 버스 운전사가 구현한 콜백 함수입니다. 버스에 속한 장치 또는 device_driver가 커널에 추가되면 커널은 이 인터페이스를 호출합니다. 새로 추가된 장치 또는 device_driver가 나머지 절반과 일치하면 이때 인터페이스는 0이 아닌 값을 반환합니다. 버스 모듈의 로직은 후속 처리를 수행합니다.

uevent, 특정 버스 드라이버가 구현한 콜백 함수입니다. 버스에 속한 장치가 추가, 제거 또는 다른 방식으로 작동되면 버스 모듈의 핵심 로직이 이 인터페이스를 호출하여 버스 드라이버가 환경 변수를 수정할 수 있습니다.

probe 및 제거, 이 두 콜백 함수는 device_driver의 콜백 함수와 매우 유사하지만 그 존재는 매우 의미가 있습니다. 프로브(실제로 초기화)에 의해 지정된 장치가 필요한 경우 장치가 있는 버스가 초기화되었고 올바르게 작동할 수 있는지 확인해야 한다고 상상할 수 있습니다. 이를 위해서는 device_driver의 프로브를 실행하기 전에 버스 프로브를 실행해야 합니다. 제거 과정이 반대가 됩니다.
참고 1: 일부 버스(예: 플랫폼 버스)의 경우 가상 버스 자체이므로 초기화할 필요가 없으며 이러한 버스의 드라이버를 직접 사용할 수 있으므로 모든 버스에 프로브 및 제거 인터페이스가 필요한 것은 아닙니다. 두 콜백 함수 모두 비워 둘 수 있습니다.

종료, 정지, 재개는 조사 및 제거와 유사한 원리를 갖습니다. 전원 관리와 관련된 구현은 당분간 설명하지 않겠습니다.

pm, 전원 관리와 관련된 로직은 아직 설명하지 않겠습니다.

iommu_ops, 아직 설명되지 않았습니다.

p, struct subsys_private 유형의 포인터에 대해서는 나중에 섹션에서 설명하겠습니다.

2.2 구조체 subsys_private

이 구조는 device_driver의 struct driver_private과 유사합니다. "Linux 장치 모델(5)_장치 및 장치 드라이버" 장에서 언급되었지만 자세히 설명하지는 않습니다.

要说明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: };

看到结构内部的字段,就清晰多了,没事不要乱起名字嘛!什么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);

该功能的执行逻辑如下:

  • 为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);

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的处理逻辑:

  • 드라이버의 구조체 Driver_private 포인터(priv)를 위한 공간을 할당하고, priv->klist_devices, priv->driver, priv->kobj.kset 및 기타 변수를 초기화하고 포인터를 device_driver
  • 의 p에 저장합니다.
  • 드라이버의 kset(priv->kobj.kset)를 버스의 드라이버 kset(bus->p->drivers_kset)으로 설정합니다. 이는 모든 드라이버 kobject가 버스->p->drivers_kset(send/sys/bus/ xxx/드라이버 디렉터리)
  • 드라이버 이름을 매개변수로 사용하여 kobject_init_and_add 인터페이스를 호출하여 sysfs에 드라이버의 kobject를 등록합니다. 이는 /sys/bus/xxx/drivers/ 디렉토리(예: /sys/bus/spi/drivers/spidev
  • )에 반영됩니다.
  • 버스의 klist_drivers 연결 목록에 드라이버를 저장하고 drivers_autoprobe 값에 따라 드라이버_attach를 호출하여 프로브
  • 를 호출할지 선택합니다.
  • driver_create_file 인터페이스를 호출하고 sysfs의 드라이버 디렉터리에 uevent 속성
  • 을 생성하세요.
  • Driver_add_attrs 인터페이스를 호출하고 sysfs
  • 의 드라이버 디렉터리에 있는 버스->drv_attrs 포인터에 의해 정의된 기본 속성을 생성합니다.
  • 동시에 Suppress_bind_attrs 플래그에 따라 sysfs의 드라이버 디렉터리에 바인딩 및 바인딩 해제 속성을 생성할지 여부를 결정합니다. (자세한 내용은 "Linux 장치 모델(5)_장치 및 장치 드라이버"의 소개를 참조하세요.)

3.3 드라이버 프로브

"Linux 장치 모델(5)_장치 및 장치 드라이버"에서는 드라이버의 프로브 타이밍과 프로세스를 소개했습니다. 대부분의 로직은 주로 버스 모듈, 주로 버스_프로브_장치 및 드라이버_부착 인터페이스의 구현에 의존합니다. 마찬가지로, 이 두 인터페이스는 drivers/base/base.h에서 선언되고 drivers/base/bus.c에서 구현됩니다.

이 두 구조의 동작은 유사하며 논리는 매우 간단합니다. 즉, 버스가 위치한 버스를 검색하고 동일한 이름을 가진 device_driver(또는 장치)가 있는지 비교합니다. 드라이버에 바인딩되지 않습니다(참고: 이는 매우 중요합니다. 이를 통해 동일한 드라이버는 동일한 이름(나중에 플랫폼 장치 설명에서 언급됨)을 가진 여러 장치를 구동할 수 있으며 device_driver의 프로브 인터페이스가 호출됩니다. .

4. 기타

4.1 서브시스템에 대해 다시 이야기해보자

이전 Linux 커널 버전(예: Wowo에서 사용하는 커널의 linux2.6.23 버전 사용)에서 sysfs 아래의 모든 최상위 디렉터리(일부 보조 디렉터리 포함)는 sub-system Registered 형식으로 subsystem_register 인터페이스를 호출합니다. 다음과 같이 커널에:

/sys/bus/

/sys/devices/

/sys/devices/system/

/sys/block

/sys/kernel/

/sys/slab/

당시 subsystem_register의 구현은 매우 간단했습니다. kset_register를 호출하여 kset을 생성하는 것이었습니다. 우리는 kset이 kobject의 모음이고 sysfs의 디렉토리 형태로 제공된다는 것을 알고 있습니다.

커널의 새 버전(예: "Linux 커널 분석" 기사 시리즈에서 참조된 linux3.10.29)에서는 하위 시스템의 구현이 많이 변경되었습니다. 예를 들어 subsystem_register 인터페이스가 제거되었습니다(그러나 /sys/device/system 하위 시스템과 호환되어야 합니다. 시스템은 drivers/base/bus.c에서 해당 기능을 구현하기 위해 subsys_register의 내부 인터페이스를 추가합니다. 이러한 변경 사항에 따라 이제 하위 시스템을 등록하는 방법에는 두 가지가 있습니다.

방법 1: 해당 초기화 함수에서 kset_create_and_add 인터페이스를 호출하여 다음을 포함하여 해당 하위 시스템을 생성합니다.

  • 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. };

name,interface的名称。

subsys,interface所属的bus。

node,用于将interface挂到bus中。

add_dev/remove_dev, 두 개의 콜백 함수, subsys 인터페이스의 핵심 함수. 버스 아래에 장치가 추가되거나 삭제되면 버스 코어는 그 아래에 있는 모든 subsys 인터페이스의 add_dev 또는 Remove_dev 콜백을 호출합니다. 디자이너는 "특정 기능"에 해당하는 드라이버 바인딩 등과 같은 두 가지 콜백 함수에서 필요한 기능을 구현할 수 있습니다.

subsys 인터페이스의 구현 로직은 비교적 간단하므로 여기서는 자세히 설명하지 않습니다. "drivers/base/bus.c"의 해당 코드를 참조하세요. 또한 나중에 cpufreq 프레임워크를 분석할 때 subsys 인터페이스를 사용하는 예를 접하게 되며 그 실제적인 의미를 더 깊이 이해하게 됩니다.

위 내용은 Linux 장치 모델에 대한 자세한 설명(6)_버스의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 lxlinux.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제