Linux设备模型4 kobj_attribute

2023-11-13

在上文Linux设备模型-3-_Kobject 实例中,我们介绍到如何使用default attribute。Default attribute使用很方便,但不够灵活。比如上篇文章在Kobject一节中提到的那个例子,name和val这两个attribute使用同一个show/store函数来访问,如果attribute非常多,show/store函数里的分支就会很凌乱。

为了解决这个问题,我们可以参考内核提供的kobj_attribute。在内核里,kobj_attibute是这样定义的:

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);
};

每一个attribute会对应自己的show/store函数,这样就极大的提高了灵活性。可是,在上一篇文章中我们的认知是,sysfs是通过kobject里的kobj_type->sysfs_ops来读写attribute的,那如果要利用kobj_attribute中的show/store来读写attribute的话,就必须在kobj_type->sysfs_ops里指定。Linux内核提供了一个默认的kobj_type类型dynamic_kobj_ktype来实现上述的操作。

/* default kobject attribute operations */
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,char *buf)
{
    struct kobj_attribute *kattr;
    ssize_t ret = -EIO;

    kattr = container_of(attr, struct kobj_attribute, attr);
    if (kattr->show)
        ret = kattr->show(kobj, kattr, buf);

    return ret;
}

static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,const char *buf, size_t count)
{
    struct kobj_attribute *kattr;
    ssize_t ret = -EIO;

    kattr = container_of(attr, struct kobj_attribute, attr);
    if (kattr->store)
        ret = kattr->store(kobj, kattr, buf, count);
    return ret;
}

const struct sysfs_ops kobj_sysfs_ops = {
    .show   = kobj_attr_show,
    .store  = kobj_attr_store,
};

static void dynamic_kobj_release(struct kobject *kobj)
{
    pr_debug("kobject: (%p): %s\n", kobj, __func__);
    kfree(kobj);
}

static struct kobj_type dynamic_kobj_ktype = {
    .release    = dynamic_kobj_release,
    .sysfs_ops  = &kobj_sysfs_ops,
};

kobj_attribute是内核提供给我们的一种更加灵活的处理attribute的方式,但是它还不够。只有当我们使用kobject_create来创建kobject时,使用kobj_attribute才比较方便,但大部分情况下,我们是把kobject内嵌到自己的结构里,此时就无法直接使用内核提供的dynamic_kobj_ktype,因此,我们需要创建自己的kobj_attribute。

本文接下来将围绕一个实例来看看如何创建自己的kobj_attribute。这个sample code是基于上篇文章kobject中的例子修改而来的,看过那个例子的读者应该会比较轻松。

首先,我们需要定义自己的attribute:

struct my_attribute {
        struct attribute attr;

        ssize_t (*show)(struct my_kobj *obj, struct my_attribute *attr,char *buf);
        ssize_t (*store)(struct my_kobj *obj, struct my_attribute *attr,const char *buf, size_t count);
};

在my_attribute里,我们的show/store直接操作my_kobj,这样更加方便。

参考Linux内核,kobj_type里的sysfs_ops这样定义:

static ssize_t my_attr_show(struct kobject *kobj, struct attribute *attr,char *buf)
{
    struct my_attribute *my_attr;
    ssize_t ret = -EIO;

    my_attr = container_of(attr, struct my_attribute, attr);
    if (my_attr->show)
        ret = my_attr->show(container_of(kobj, struct my_kobj, kobj),
                my_attr, buf);
    return ret;
}

static ssize_t my_attr_store(struct kobject *kobj, struct attribute *attr,const char *buf, size_t count)
{
    struct my_attribute *my_attr;
    ssize_t ret = -EIO;

    my_attr = container_of(attr, struct my_attribute, attr);
    if (my_attr->store)
        ret = my_attr->store(container_of(kobj, struct my_kobj, kobj),my_attr, buf, count);
    return ret;
}

下面就可以分别对name和val两个attribute定义自己的show/store。name这个attribute是只读的,只要为它定义show即可。

ssize_t name_show(struct my_kobj *obj, struct my_attribute *attr, char *buffer)
{
    return sprintf(buffer, "%s\n", kobject_name(&obj->kobj));
}
ssize_t val_show(struct my_kobj *obj, struct my_attribute *attr, char *buffer)
{
    return sprintf(buffer, "%d\n", obj->val);
}
ssize_t val_store(struct my_kobj *obj, struct my_attribute *attr,const char *buffer, size_t size)
{
    sscanf(buffer, "%d", &obj->val);

    return size;
} 

接下来,利用内核提供的宏__ATTR来初始化my_attribute,并建立attribute数组。

struct my_attribute name_attribute = __ATTR(name, 0444, name_show, NULL);
struct my_attribute val_attribute = __ATTR(val, 0666, val_show, val_store);

struct attribute *my_attrs[] = {
    &name_attribute.attr,
    &val_attribute.attr,
    NULL,
}; 

其中,宏__ATTR的定义如下:

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

在module_init里,我们调用sysfs_create_files来把attribute增加到sysfs中。

retval = sysfs_create_files(&obj->kobj,(const struct attribute **)my_attrs);
if (retval) {
    // ...
} 

在kobject对应的目录里,还可以创建子目录,Linux内核里是用attribute_group来实现。在本例中,我们可以这么做:

struct attribute_group my_group = {
    .name     = "mygroup",
    .attrs    = my_attrs,
}; 

然后在module_init里调用sysfs_create_group来添加。

retval = sysfs_create_group(&obj->kobj, &my_group);
if (retval) {
    // ...
}

本例创建的attribute_group中包含的attribute也是my_attrs,所以在子目录mygroup下的文件和mykobj目录下的文件完全一致。

最后我们得到的目录结构是这样的。

mykobj/
    |-- mygroup
    |   |-- name
    |   `-- val
    |-- name
    `-- val

完成这个实例之后,你可以用命令echo 2 > /sys/mykobj/val来修改mykobj下的val文件,可以观察到/sys/mykobj/mygroup/val的内容也会变成2,反之亦然。

详细代码如下:

#include <linux/device.h>       /* 包含内核对象模型的数据结构的定义及辅助函数 */
#include <linux/module.h>       
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>         /* 包含 kcalloc(),kzalloc()函数等 */
 
MODULE_AUTHOR("Tupelo Shen");
MODULE_LICENSE("Dual BSD/GPL");
 
/*
 * 定义container_of的简便方法
 */
#define to_my_kobj(data)        container_of(data, struct my_kobj, kobj)
 
/*
 * 自定义内核对象结构
 */
struct my_kobj {
    int val;
    struct kobject kobj;
};

// 自定义属性
struct my_attribute {
    struct attribute attr;
 
    ssize_t (*show)(struct my_kobj *obj, struct my_attribute *attr, char *buf);
    ssize_t (*store)(struct my_kobj *obj, struct my_attribute *attr,
        const char *buf, size_t count);
};

/* 
 * 分别为name和val两个属性定义自己的show/store函数。
 * name这个属性是只读的,只要为它定义show即可
 */
ssize_t name_show(struct my_kobj *obj, struct my_attribute *attr, char *buffer)
{
    return sprintf(buffer, "%s\n", kobject_name(&obj->kobj));
}
ssize_t val_show(struct my_kobj *obj, struct my_attribute *attr, char *buffer)
{
    return sprintf(buffer, "%d\n", obj->val);
}
ssize_t val_store(struct my_kobj *obj, struct my_attribute *attr,
    const char *buffer, size_t size)
{
    sscanf(buffer, "%d", &obj->val);
 
    return size;
}

/*
 * 定义name和val属性
 */
struct my_attribute name_attribute = __ATTR(name, 0444, name_show, NULL);
struct my_attribute val_attribute = __ATTR(val, 0666, val_show, val_store);

struct attribute *my_attrs[] = {
    &name_attribute.attr,
    &val_attribute.attr,
    NULL,
};

/*
 * 用户空间调用读写函数时的属性显示和存储函数
 */
static ssize_t my_attr_show(struct kobject *kobj, struct attribute *attr, 
    char *buffer)
{
    struct my_attribute *my_attr;
    ssize_t ret = -EIO;

    my_attr = container_of(attr, struct my_attribute, attr);
    if(my_attr->show)
        ret = my_attr->show(container_of(kobj,struct my_kobj, kobj), my_attr, 
                            buffer);
    return ret;
}
 
static ssize_t my_attr_store(struct kobject *kobj, struct attribute *attr, 
    const char *buffer, size_t size)
{
    struct my_attribute *my_attr;
    ssize_t ret = -EIO;

    my_attr = container_of(attr, struct my_attribute, attr);
    if(my_attr->store)
        ret = my_attr->store(container_of(kobj, struct my_kobj, kobj), my_attr, 
            buffer, size);
    return ret;
}
 
struct sysfs_ops my_sysfsops = {
    .show = my_attr_show,
    .store = my_attr_store,
};

struct attribute_group my_group = {
    .name     = "mygroup",
    .attrs    = my_attrs,
}; 

/*
 * 定义两个内核对象,它们的层次结构如下: 
 *
 *   mykobj/
 *   |-- mygroup
 *   | |-- name
 *   | `-- val
 *   |-- name
 *   `-- val
 */ 
struct my_kobj *obj;
struct kobj_type my_type;
 
void obj_release(struct kobject *kobj)
{
    struct my_kobj *my_kobj_free = to_my_kobj(kobj);
    printk(KERN_INFO "my_kobj_release\n");
    kfree(my_kobj_free);
}

/*
 * 初始化内核对象,完成创建并添加
 */ 
static int __init my_kobj_init(void)
{
    int retval = 0;

    printk(KERN_INFO "mykobj_init\n");
 
    obj = (struct my_kobj *)kzalloc(sizeof(struct my_kobj), GFP_KERNEL);
    if (!obj) {
        return -ENOMEM;
    }
    obj->val = 1;    
 
    my_type.release = obj_release;
    // my_type.default_attrs = my_attrs;
    my_type.sysfs_ops = &my_sysfsops;

    // 父节点-在这儿为/sys/kernel/
    kobject_init_and_add(&obj->kobj, &my_type, kernel_kobj, "mykobj");

    // 创建多个属性文件name和val
    retval = sysfs_create_files(&obj->kobj,(const struct attribute **)my_attrs);
    if (retval)
    {
        printk(KERN_INFO "create obj attribute failed!\n");
        kobject_put(&obj->kobj);
        return retval;
    }

    // 对于一个给定的对象,创建属性组
    retval = sysfs_create_group(&obj->kobj, &my_group);
    if (retval) {
        printk(KERN_INFO "create obj attribute group failed!\n");
    }

    return retval;
}
 
static void __exit my_kobj_exit(void)
{
    printk(KERN_INFO "mykobj_exit\n");
     
    kobject_del(&obj->kobj);
    kobject_put(&obj->kobj);
 
    return;
}
 
module_init(my_kobj_init);
module_exit(my_kobj_exit);

补充:从file找到sysfs的属性文件

操作上,从VFS的fs到sysfs经历了file_operations域,kernfs_ops域和最终的sys_ops域的转换。

上图参考:

Linux设备驱动模型框架分析(五)——LDDM的展现:sysfs_newdye的博客-CSDN博客

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux设备模型4 kobj_attribute 的相关文章

随机推荐

  • SQL INSERT INTO 语句

    INSERT INTO 语句用于向表中插入新记录 语法 指定列插入数据 INSERT INTO table name colnum1 colnum2 column3 VLAUES value1 value2 value3 不指定列插入数据
  • java - JVM CPU100%,问题排查

    前段时间我们新上了一个新的应用 因为流量一直不大 集群QPS大概只有5左右 写接口的rt在30ms左右 因为最近接入了新的业务 业务方给出的数据是日常QPS可以达到2000 大促峰值QPS可能会达到1万 所以 为了评估水位 我们进行了一次压
  • FormData实现文件上传

    应用场景 FormData Ajax技术实现文件上传 1 FormData使用 FormData是一个构造函数 首先new FormData 得到一个FormData对象 可以直接使用 直接console会是一个空白的对象 有append方
  • 未解决-联想拯救者r7000 CTRL+C复制键无法使用

    情况描述 突然不能使用 不知道是什么操作导致不能使用 也不知道什么操作解决了问题 发生次数 四次以上 判断过程 鼠标右键复制可以使用 外接键盘复制可以使用 无QQ 微信等热键冲突 网页 记事本都无法使用 ctrlA ctrlX ctrlV可
  • 手把手教你画活动图,再无难搞的流程分析

    上次介绍了 用例图这样画 3步让你做需求分析有理有据 这次聊聊活动图 也许你对活动图并不了解 不过 说起流程图 想必你不会陌生 你可以暂且把活动图 看成 UML 中的流程图 都知道 做产品要分析流程 可怎么把流程理清楚呢 当然不能凭空想象
  • ANN神经网络入门——分类问题(MATLAB)

    写在前面 本篇博客的鸢尾花分类程序来源于博客http www cnblogs com heaad archive 2011 03 07 1976443 html 在上述博客中 作者主要介绍了以下三部分内容 1 神经网络基本原理 2 AFor
  • Angular和RxJS:添加REST API后端

    本文是SitePoint Angular 2 教程的第3部分 该教程有关如何使用Angular CLI创建CRUD应用程序 在本文中 我们将更新我们的应用程序以与REST API后端进行通信 更喜欢使用分步视频课程学习Angular 退房
  • 上传本地新项目到SVN服务器

    1 在一个已有检出的项目文件夹 如下的文件夹 1 就是我从svn检出的项目 中 在空白处 右键 gt TortoiseSVN gt Repo browser 这样就到了svn服务器的目录了 打开远程地址目录 2 创建一个文件夹给即将上传的项
  • 一文拿下弱网测试:3大弱网模拟工具的配置、场景弱网原因分析、面试题目……

    软件质量不是口头说说 要实际做起来 正文开始 如果app没有对各种网络异常进行兼容处理 那么用户可能在日常生活中遇到APP闪退 ANR 数据丢失等问题 因此 app网络测试 特别是弱网测试尤为重要 本文梳理了app网络测试要点和弱网测试常用
  • 2023年1月16日--2023年1月22日(osg+glsl+socket+ue, 本周20小时,合计1879小时,剩余8121小时)

    目前 ue视频教程进行到了zccs 预1 mysql 7 1 tf1 4 11 o s s 12 2 蓝图反射 1 9 moba 1 5 webapp 2 4 mmoarpg 00A 04 socket 2 57 Opengl 5 9 GL
  • 十三、传智书城项目设计

    项目源代码及sql脚本 一 项目概述 近年来 随着Internet的迅速崛起 互联网已成为收集信息的最佳渠道并逐步进入传统的流通领域 于是电子商务开始流行起来 越来越多的商家在网上建起在线商店 向消费者展示出一种新颖的购物理念 网上购物系统
  • yolo中iou_thres的含义及作用

    yolo中iou thres的含义及作用 iou thres 参数用于控制非极大值抑制 NMS 中的边界框合并阈值 较大的 iou thres 值会导致更多的边界框被合并 从而减少最终输出的边界框数量 是否将 iou thres 设置得更大
  • 图论进阶指南-银河(差分约束/DAG/tarjan)

    测评地址 题目大意 第一行给出两个整数N和M 之后M行 每行三个整数T A B 表示一对恒星 A B 之间的亮度关系 恒星的编号从1开始 如果T 1 说明A和B亮度相等 如果T 2 说明A的亮度小于B的亮度 如果T 3 说明A的亮度不小于B
  • 【STM32】串口初步使用

    本文只作为学习笔记 对串口进行一个简单的介绍 正确的使用方式还需要进行实际的调试 通信的类型 同步 异步 单工 双工 串行 并行 STM32的串口通信 配置片上外设的控制寄存器 通信双方进行相同的配置 约定共同的起始位 停止位 校验位 使得
  • Postman接口测试 —— 设置全局变量、参数传递、断言

    在能熟练使用postman运行接口请求后 会遇到一些问题 例如 我们的web网站一共有几十个接口 测试的时候如果要切换环境 这个时候要每个接口都要修改url的根路径 一个一个的改也太麻烦了 还有时候我们经常需要用到上一个接口的返回值 来作为
  • jQuery隔行变色

  • vant d的地址组件中文操作手册

    vant d的 学习手册 中文操作文档 开发指南 快速上手 基础组件 Empty 空状态 业务组件 地址列表 地址编辑 Area 省市区选择 Card 卡片 Contact 联系人 Coupon 优惠券选择器 GoodsAction 商品导
  • Oracle性能调整的误区

    共享服务器模式 MTS 集群技术 Clustering RAC 分区 并行处理 主要是并行查询 Oracle提供的这些特性确实是用来进行性能改善的 但我们往往忽略了对自身应用特性的分析 它们是否适合于我们 最近 通过对这方面知识的深入了解
  • 简析TCP的三次握手与四次分手原理

    简析TCP的三次握手与四次分手 TCP建立连接过程 第一次握手 建立连接 客户端发送连接请求报文段 将SYN位置为1 Sequence Number为x 然后 客户端进入SYN SEND状态 等待服务器的确认 第二次握手 服务器收到SYN报
  • Linux设备模型4 kobj_attribute

    在上文Linux设备模型 3 Kobject 实例中 我们介绍到如何使用default attribute Default attribute使用很方便 但不够灵活 比如上篇文章在Kobject一节中提到的那个例子 name和val这两个a