/sys/初窥门径

本文详细解读Linux内核的/sys目录,介绍其子目录功能,如块设备、总线管理、设备类型等。重点讲解sysfs、kobject、kset和热插拔事件,以及如何通过这些机制管理内核资源和驱动设备,如I2C和PCI设备的示例。
摘要由CSDN通过智能技术生成

1. /sys/目录:

在Linux系统中,/sys目录是一个虚拟文件系统,用于提供对内核数据结构的访问接口。它包含了大量的子目录和文件,每个子目录和文件都对应着内核中的一个数据结构或者控制接口。下面是一些常见的子目录及其作用:

- /sys/block:用于管理块设备(如硬盘、U盘等),包括设备的大小、分区、挂载等信息。

- /sys/bus:用于管理总线,比如PCI、USB等。

- /sys/class:用于管理设备的类型,比如输入设备、声卡、网络设备等。

- /sys/devices:用于管理物理设备,包括CPU、内存、硬盘、网卡等。

- /sys/fs:用于管理文件系统,包括各种文件系统的挂载点、属性等信息。

- /sys/kernel:用于管理内核参数、模块、时间等。

- /sys/power:用于管理系统的电源状态。

- /sys/virtual:用于管理虚拟设备,比如虚拟网卡、虚拟文件系统等。 通过读取和修改这些子目录和文件,可以实现对系统各种资源的管理和调整。

sysfs组织结构,进入sysfs目录中。有block bus class dev devices firmware fs kernel module power这些目录。具体代表看名字差不多就可以看出。在层次结构上,假如有一个设备A。将有一个名称为A的目录。A设备是在B总线上。那A设备应该在bus目录下的B总线下

2. 基类目录:

sys目录下首先有些基目录,比如iic、pci啥的,这些基目录实则在驱动代码中对照着一个Kset集合,大概长这个亚子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

/**

 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.

 *

 * A kset defines a group of kobjects. They can be individually

 * different "types" but overall these kobjects all want to be grouped

 * together and operated on in the same manner. ksets are used to

 * define the attribute callbacks and other common events that happen to

 * a kobject.

 *

 * @list: the list of all kobjects for this kset

 * @list_lock: a lock for iterating over the kobjects

 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)

 * @uevent_ops: the set of uevent operations for this kset. These are

 * called whenever a kobject has something happen to it so that the kset

 * can add new environment variables, or filter out the uevents if so

 * desired.

 */

struct kset {

    struct list_head list;                  //这个链表存放这个kset关联的所有kobject

    spinlock_t list_lock;                   //维护此链表的锁

    struct kobject kobj;                    //内嵌的kobject。这样kset本身也是一个kobject也被表现为一个目录

    struct kset_uevent_ops *uevent_ops;     //支持热插拔事件的函数集

};

这里面内嵌了一个“struct kobject kobj”,这里不是指针,是包含,所包含的东西就是基类,比如iic、pci这种,以后的每个“struct kobject kobj”结构都会关联这个kset,表示层次默认在此基类之下,即每个“struct kobject kobj”对应的目录应该默认是基目录的子目录

可以理解为kset是一个顶层容器

kset链表中的kobject对象的parent指针一般都指向kset内嵌的kobject对象

kset_uevent_ops是热插拔事件:

热插拔事件是用内核空间发送到用户空间的通知。表明内核中的某些配置已经发生变化。用户空间则会根据这些信息做相应的处理。例如,U盘插入USB系统时,会产生一个热插拔事件,内核会捕捉到这个热插拔事件,然后调用/sbin/hotplug程序,该程序通知加载驱动程序来相应U盘的插入动作。

热插拔函数集的定义在include/linux/koject.h中

3. 驱动设备目录:

每个在内核中注册的kobject都对应于sysfs文件系统中的一个目录,一般肯定是在基目录之下的,kobject长这个亚子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

struct kobject {

    const char        *name;                        //kobject的名称

    struct list_head    entry;                      //kobject结构链表

    struct kobject        *parent;                  //父kobject结构体

    struct kset        *kset;                       //kset集合

    struct kobj_type    *ktype;                     //kobject的类型描述符

    struct sysfs_dirent    *sd;                     //sysfs文件目录

    struct kref        kref;                        //kobject引用计数

    unsigned int state_initialized:1;               //kobject是否初始化

    unsigned int state_in_sysfs:1;                  //是否已经加入sysfs

    unsigned int state_add_uevent_sent:1;

    unsigned int state_remove_uevent_sent:1;

    unsigned int uevent_suppress:1;

};

里面有很多东西都是关联(指针)而不是包含(内嵌),比如kset、parent、ktype,表明这个结构层次并非在这些指向结构之上,

parent指针:也是同一种类型,如果这个父指针指向了一个具体的kobj,那么会代表着在实际目录中就会在这个父目录之下

kset指针:如果上一个指针没有指向具体的父指针,那么好了,本结构对应的目录就要位于基目录之下了,如果父指针指定了,那么就按照parent的指向来

ktype指针:这个表示这个结构的一些特性,这种特性或许是独一无二的,或许是很普遍的

sd指针:指向了sysfs_dirent结构体,改结构体在sysfs中表示的就是这个kobject,从sysfs文件系统内部看来,这个结构表示kobject的一个inode结构体

4. 对象属性:

长这个亚子:

1

2

3

4

5

struct kobj_type {

    void (*release)(struct kobject *kobj);          //释放函数(驱动编写时提供),此函数会被kobject_put函数调用

    struct sysfs_ops *sysfs_ops;                    //属性文件的操作函数(只有读和写操作)

    struct attribute **default_attrs;               //属性数组

};

其中包含了文件操作函数,这个只有读和写

还包含了属性数组指针,可以有丰富的属性条目

1

2

3

4

5

6

7

8

9

struct attribute {

    const char      *name;

    umode_t         mode;

#ifdef CONFIG_DEBUG_LOCK_ALLOC

    bool            ignore_lockdep:1;

    struct lock_class_key   *key;

    struct lock_class_key   skey;

#endif

};

5. 字符设备举例:

1

2

3

4

5

6

7

8

struct cdev {

    struct kobject kobj;

    struct module *owner;

    const struct file_operations *ops;

    struct list_head list;

    dev_t dev;

    unsigned int count;

};

比如字符设备的结构里面就是包含了这样一个“struct kobject kobj”结构,即本字符设备包括了sys文件目录中的资源

6. pci举例

在此设备中包含了:

1

2

3

4

5

6

7

8

/* pci_slot represents a physical slot */

struct pci_slot {

    struct pci_bus *bus;                    /* The bus this slot is on */

    struct list_head list;                  /* node in list of slots on this bus */

    struct hotplug_slot *hotplug;           /* Hotplug info (migrate over time) */

    unsigned char number;                   /* PCI_SLOT(pci_dev->devfn) */

    struct kobject kobj;

};

即一个pci插槽包含了一个这样的“struct kobject kobj”结构

6.1. pci设备文件系统扩展:

在Linux的PCI文件系统中,/sys/device/pci目录下的每个设备子目录都包含了该设备的资源信息,其中包括了该设备所使用的所有BAR(Base Address Register)的相关信息。每个BAR都代表了设备在地址空间中所占用的一段内存或者IO空间。

在/sys/device/pci/<bus>/<dev>.<func>/resource文件中,可以找到该设备所使用的所有BAR的地址范围信息。

而在/sys/device/pci/<bus>/<dev>.<func>/resourceX(其中X为对应的BAR号)文件中,则包含了该BAR所代表的地址范围的具体信息,如地址起始位置、大小、寻址方式等。

因此,可以通过在/sys/device/pci目录下查找相应设备的resource文件和对应的resourceX文件,来确定该设备所使用的所有BAR的相关信息。 

7. IIC步骤:

在Linux系统中,I2C设备通常会被挂载到/sys/bus/i2c/devices目录下。

这个目录下的每个子目录都代表着一个I2C设备,其名称为设备的I2C地址。

7.1. 步骤:

1. 在设备树中添加I2C设备节点,并指定设备的I2C地址和总线编号。

2. 在驱动程序中实现I2C设备的注册和初始化,并创建相应的设备节点。

3. 将设备节点挂载到/sys/bus/i2c/devices目录下,以便用户空间程序可以访问设备。

7.2. 数据结构

在实际实现中,可以使用sysfs_create_link()函数来创建设备节点,并使用sysfs_create_dir()函数来创建I2C设备目录。

同时,还需要在驱动程序中实现read()和write()等文件操作方法,以便用户空间程序可以通过/sys接口来访问设备。

sysfs_create_link()函数的作用是在/sys目录下创建一个符号链接文件,用于将一个设备节点链接到另一个设备节点。 这个函数的参数包括要创建的符号链接文件的路径、目标设备节点的路径和目标节点的名称。

sysfs_create_dir()函数的作用是在/sys目录下创建一个新的子目录。这个函数的参数包括要创建的子目录的路径和名称。

创建成功后,可以在这个子目录下创建新的设备节点,以便用户空间程序可以访问设备。 这两个函数在Linux系统中常用于驱动程序的实现中,用于创建设备节点和设备目录,以便用户空间程序可以通过/sys接口来访问设备。 需要注意的是,在使用这两个函数时,需要确保当前用户具有足够的权限来创建和访问/sys目录下的文件和目录。

关键转换在这里: 

1

2

3

4

5

static inline struct i2c_client *kobj_to_i2c_client(struct kobject *kobj)

{

    struct device * const dev = container_of(kobj, struct device, kobj);

    return to_i2c_client(dev);

}

在Linux内核中,I2C设备通常会被表示为一个i2c_client结构体。

这个结构体包含有关设备的信息,例如设备的名称、I2C地址、总线编号等。

同时,这个结构体也包含了一个指向设备驱动程序的指针,以便在发生I2C传输时可以调用相应的驱动程序。

另一方面,Linux内核中还包含了一个kobject结构体,用于表示内核对象。 这个结构体包含了一些基本的信息,例如对象的名称、父对象、引用计数等。

同时,这个结构体还包含了一些回调函数指针,用于实现对象的一些操作,例如创建、销毁、属性访问等。 在Linux系统中,i2c_client结构体通常会通过kobject结构体进行管理。

具体来说,每个i2c_client结构体都会被表示为一个kobject结构体,并且将其添加到/sys/bus/i2c/devices目录下,以便用户空间程序可以访问设备。

同时,这个kobject结构体还可以添加一些属性,例如设备的状态、版本号等,以便用户空间程序可以查询设备的状态信息。

因此,可以说i2c数据结构和kobject的交集就在于它们都用于表示Linux内核中的对象,并通过/sys文件系统提供接口,以便用户空间程序可以访问和管理这些对象。

同时,kobject结构体还提供了一些额外的功能,例如属性访问、事件通知等,可以帮助驱动程序实现更复杂的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值