Linux驱动开发—设备模型框架 kobject创建属性文件

文章目录

    • 什么是属性文件?
      • [1. sysfs 与 kobject](#1. sysfs 与 kobject)
      • [2. 属性文件的作用](#2. 属性文件的作用)
      • [3. 属性文件的基本操作](#3. 属性文件的基本操作)
      • [4. 典型的属性文件用例](#4. 典型的属性文件用例)
      • [5. 创建属性文件的步骤](#5. 创建属性文件的步骤)
      • 6.示例代码
      • 7.效果图
    • [使用 ATTR 宏定义优化](#使用 ATTR 宏定义优化)
    • 优化关键点解析
      • [1. 数据结构定义](#1. 数据结构定义)
      • [2. 属性定义](#2. 属性定义)
      • [3. 属性的读写方法](#3. 属性的读写方法)
      • [4. sysfs 操作接口](#4. sysfs 操作接口)

什么是属性文件?

在 Linux 内核中,属性文件 (Attribute Files)指的是通过 sysfs 文件系统暴露给用户空间的文件,这些文件通常用于在用户空间与内核空间之间进行简单的数据交换或配置。

1. sysfs 与 kobject

  • sysfs 文件系统 是 Linux 内核中的一种虚拟文件系统,它将内核对象 (kobjects) 的属性映射为文件,通常位于 /sys 目录下。这些文件通过 sysfs 文件系统暴露给用户,可以被用户空间的程序读取或写入。

  • kobject 是内核对象管理机制的核心,sysfs 文件系统通过 kobject 来组织和管理这些属性文件。

2. 属性文件的作用

  • 读取内核状态:属性文件可以暴露内核对象的状态或统计信息,例如设备的当前状态、计数器值等。

  • 配置内核参数:用户可以通过写入属性文件来修改内核对象的参数,配置设备或调试内核模块。

3. 属性文件的基本操作

  • 读取操作 (show) :用户空间读取属性文件的内容,触发内核中的 show 函数,返回文件的内容。

  • 写入操作 (store) :用户空间写入属性文件的内容,触发内核中的 store 函数,更新内核对象的状态或参数。

4. 典型的属性文件用例

一个典型的例子是 /sys/class/net/eth0/ 目录下的属性文件,它们允许用户查看或配置网络接口 eth0 的一些属性,比如 speed, mtu, operstate 等。

例如:

  • 读取速度cat /sys/class/net/eth0/speed
  • 配置 MTUecho 1500 > /sys/class/net/eth0/mtu

5. 创建属性文件的步骤

当您在内核模块中定义一个 kobject 时,通常会定义一组 attribute 结构体,并将它们关联到 sysfs 文件系统中。每个属性文件在 sysfs 中对应一个 attribute 结构体,通常会包含以下信息:

  • name: 属性文件的名称。
  • mode: 文件的权限,例如 0444 表示只读,0666 表示可读可写。
  • show 函数: 定义了如何显示属性文件的内容。
  • store 函数: 定义了如何处理用户写入的内容。

6.示例代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>

#define DISABLE 0
#define ENABLE 1

// 创建自己的kobj ,附带两个属性
struct mykobj
{
    struct kobject kobj;
    int reg_count;
    int reg_status;
};

struct mykobj *mykobject01;

static void dynamic_kobj_release(struct kobject *kobj)
{
    struct mykobj *mykobject01 = container_of(kobj, struct mykobj, kobj);
    kfree(mykobject01);
}

static struct attribute attr1 = {
    .name = "reg_count",
    .mode = 0666,
};

static struct attribute attr2 = {
    .name = "reg_status",
    .mode = 0666,
};

ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    if (strcmp(attr->name, "reg_count") == 0)
        return sprintf(buf, "%d\n", mykobject01->reg_count);
    if (strcmp(attr->name, "reg_status") == 0)
        return sprintf(buf, "%d\n", mykobject01->reg_status);

    return 0;
}

ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
    int val;

    if (strcmp(attr->name, "reg_count") == 0)
    {
        sscanf(buf, "%d", &val);
        mykobject01->reg_count = val;
    }
    else if (strcmp(attr->name, "reg_status") == 0)
    {
        sscanf(buf, "%d", &val);
        mykobject01->reg_status = val;
    }

    return count;
}

static const struct sysfs_ops my_sysfs_ops = {
    .show = my_show,
    .store = my_store,
};

static struct attribute *attrs[] = {&attr1, &attr2, NULL};

static struct kobj_type my_type = {
    .release = dynamic_kobj_release,
    .sysfs_ops = &my_sysfs_ops,
    .default_attrs = attrs,
};

static int __init mykobj_init(void)
{
    int ret = 0;

    mykobject01 = kzalloc(sizeof(struct mykobj), GFP_KERNEL);
    if (!mykobject01)
        return -ENOMEM;

    mykobject01->reg_count = 2;
    mykobject01->reg_status = DISABLE;

    ret = kobject_init_and_add(&mykobject01->kobj, &my_type, NULL, "%s", "mykobj_root");
    if (ret)
    {
        kobject_put(&mykobject01->kobj);
        return ret;
    }

    return 0;
}

static void __exit mykobj_exit(void)
{
    kobject_put(&mykobject01->kobj);
}

module_init(mykobj_init);
module_exit(mykobj_exit);

MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple example of making kobject with sysfs attributes");

7.效果图

属性文件是内核与用户空间进行交互的重要方式之一。它们通常用于在内核模块中暴露一些简单的、可配置的参数,便于用户空间的应用程序进行读取或配置。这种机制在驱动开发、调试以及设备配置中广泛使用。

使用 ATTR 宏定义优化

上述暴露出一个问题,如果某个设备属性文件过多,如何优化if的次数?这样会让读写文件很臃肿,最好的方法,应该是一个属性对应一个show和store

可以使用ATTR宏定义来实现这个功能

__ATTR用法解析

__ATTR() 是 Linux 内核中的一个宏,用于简化属性文件(attribute file)的定义。它用于定义一个 struct attribute 结构体,同时设置该属性的名称、权限以及关联的 showstore 操作函数。

1. __ATTR() 宏的定义

#define __ATTR(_name, _mode, _show, _store) { \
    .attr = { .name = __stringify(_name), .mode = (_mode) }, \
    .show   = (_show), \
    .store  = (_store), \
}

2. __ATTR() 宏的参数

  • _name :属性的名称,将出现在 /sys 文件系统中的文件名。
  • _mode :属性文件的权限模式,通常是八进制数,比如 06440666 等,表示文件的读写权限。
  • _show:属性文件的读取函数指针,当用户读取该文件时将调用此函数。
  • _store:属性文件的写入函数指针,当用户写入该文件时将调用此函数。

3.优化示例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

#define DISABLE 0
#define ENABLE 1

// 创建自己的kobj ,附带两个属性
struct mykobj
{
    struct kobject kobj;
    int reg_count;
    int reg_status;
};

struct mykobj *mykobject01;

static void dynamic_kobj_release(struct kobject *kobj)
{
    struct mykobj *mykobject01 = container_of(kobj, struct mykobj, kobj);
    kfree(mykobject01);
}


/*
 struct kobj_attribute {
	struct attribute attr;
	ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf);
	ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count);
};

*/

//reg_count_show 属性
static ssize_t reg_count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    pr_info("Reading reg_count: %d\n", mykobject01->reg_count);
    return sprintf(buf, "%d\n", mykobject01->reg_count);
}
static ssize_t reg_count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
    sscanf(buf, "%d", &mykobject01->reg_count);
    return count;
}

struct kobj_attribute  reg_count_attr= __ATTR(reg_count,0664,reg_count_show,reg_count_store);

//reg_status 属性
static ssize_t reg_status_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    pr_info("Reading reg_status: %d\n", mykobject01->reg_status);
    return sprintf(buf, "%d\n", mykobject01->reg_status);
}
static ssize_t reg_status_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
    sscanf(buf, "%d", &mykobject01->reg_status);
    return count;
}
struct kobj_attribute  reg_status_attr= __ATTR(reg_status,0664,reg_status_show,reg_status_store);

ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    
    struct kobj_attribute *kobj_attr  = container_of(attr,struct kobj_attribute,attr);
    return kobj_attr->show(kobj,kobj_attr,buf);
}

ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{

    struct kobj_attribute *kobj_attr  = container_of(attr,struct kobj_attribute,attr);
    kobj_attr->store(kobj,kobj_attr,buf,count);
    return 0;
}

static const struct sysfs_ops my_sysfs_ops = {
    .show = my_show,
    .store = my_store,
};


static struct attribute *attrs[] = {&reg_status_attr.attr, &reg_count_attr.attr, NULL};

static struct kobj_type my_type = {
    .release = dynamic_kobj_release,
    .sysfs_ops = &my_sysfs_ops,
    .default_attrs = attrs,
};

static int __init mykobj_init(void)
{
    int ret = 0;

    mykobject01 = kzalloc(sizeof(struct mykobj), GFP_KERNEL);
    if (!mykobject01)
        return -ENOMEM;

    mykobject01->reg_count = 2;
    mykobject01->reg_status = DISABLE;

    ret = kobject_init_and_add(&mykobject01->kobj, &my_type, NULL, "%s", "mykobj_root");
    if (ret)
    {
        kobject_put(&mykobject01->kobj);
        return ret;
    }

    return 0;
}

static void __exit mykobj_exit(void)
{
    kobject_put(&mykobject01->kobj);
}

module_init(mykobj_init);
module_exit(mykobj_exit);

MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple example of making kobject with sysfs attributes");

优化关键点解析

这段代码的目的是创建一个内核对象(kobject),并通过 sysfs 暴露两个属性(reg_countreg_status)供用户空间访问。用户可以通过读取或写入 /sys 目录下的文件来查看和修改这些属性。

1. 数据结构定义

c 复制代码
struct mykobj
{
    struct kobject kobj;
    int reg_count;
    int reg_status;
};

struct mykobj *mykobject01;
  • mykobj 结构体包含了一个 kobject 对象以及两个整型成员变量 reg_countreg_statuskobject 是内核对象管理的基础,它提供了 sysfs 的接口。

2. 属性定义

c 复制代码
struct kobj_attribute reg_count_attr = __ATTR(reg_count, 0664, reg_count_show, reg_count_store);
struct kobj_attribute reg_status_attr = __ATTR(reg_status, 0664, reg_status_show, reg_status_store);
  • 使用 __ATTR 宏定义了两个 kobj_attribute 类型的属性:reg_count_attrreg_status_attr。该宏将属性名称、访问权限和读写方法关联起来。

3. 属性的读写方法

c 复制代码
static ssize_t reg_count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    pr_info("Reading reg_count: %d\n", mykobject01->reg_count);
    return sprintf(buf, "%d\n", mykobject01->reg_count);
}

static ssize_t reg_count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
    sscanf(buf, "%d", &mykobject01->reg_count);
    return count;
}
  • reg_count_showreg_count_storereg_count 属性的读写方法。
    • reg_count_show: 读取 reg_count 的值,并将其格式化输出到 buf
    • reg_count_store: 从 buf 读取用户输入的值并更新 reg_count

4. sysfs 操作接口

c 复制代码
static const struct sysfs_ops my_sysfs_ops = {
    .show = my_show,
    .store = my_store,
};
  • sysfs_ops 结构体定义了如何处理 sysfs 文件的读写操作。my_showmy_store 实现了通用的属性读写逻辑,通过 container_of 获取属性对应的 kobj_attribute,然后调用具体的 showstore 方法。
相关推荐
Lary_Rock2 小时前
RK3576 LINUX RKNN SDK 测试
linux·运维·服务器
云飞云共享云桌面4 小时前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
Peter_chq4 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
一坨阿亮5 小时前
Linux 使用中的问题
linux·运维
dsywws6 小时前
Linux学习笔记之vim入门
linux·笔记·学习
幺零九零零7 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
小林熬夜学编程8 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法
程思扬9 小时前
为什么Uptime+Kuma本地部署与远程使用是网站监控新选择?
linux·服务器·网络·经验分享·后端·网络协议·1024程序员节
sun0077009 小时前
拷贝 cp -rdp 和 cp -a
linux·运维·服务器
wowocpp9 小时前
ubuntu 22.04 server 安装 anaconda3
linux·运维·ubuntu