当前位置:首页>Linux>【Linux内核】sysfs属性与属性组attribute_group内核源码解析

【Linux内核】sysfs属性与属性组attribute_group内核源码解析

  • 2026-08-18 23:10:44
【Linux内核】sysfs属性与属性组attribute_group内核源码解析
第一期字符设备驱动第二期Kobject设备模型基石之后,本文沿着kobject如何在sysfs中暴露自己这条线继续往下走。第二期我们已经知道:每个kobject在sysfs中对应一个目录(kernfs_node)。但上期创建kobject后是空目录,真正让用户空间cat/echo就能读写内核对象状态的,是目录里的属性文件(attribute)

属性文件的批量创建、分组、权限控制、可见性管理,全部由struct attribute_group和它背后的一整套kernfs / sysfs / VFS回调链来承担。本文目标是:从内核源码层面理解,执行cat属性文件到最终调用用户定义的show()这条完整链路

一、核心结构体定义

理解属性组,先要从三个基础结构体入手:

  • attribute(基础的描述)

  • kobj_attribute(增加读写函数)

  • attribute_group(一组属性文件)

1.1 attribute_group 属性组

attribute_group结构体定义了属性组的名称、可见性,以及具体的属性数组指针。其中attribute数组中,每个属性元素都保存用户定义的showstore函数,内核最后会通过一系列回调找到该函数调用。

// 定义在 /include/linux/sysfs.h 文件中struct attribute_group {const char *name;  // 子目录名,若为NULL则直接创建在kobj目录下umode_t (*is_visible)(struct kobject *, struct attribute *, int);umode_t (*is_bin_visible)(struct kobject *, struct bin_attribute *, int);struct attribute **attrs;           // 普通属性 指针数组(NULL结尾)struct bin_attribute **bin_attrs;   // 二进制属性 指针数组(NULL结尾)};struct attribute {const char *name;umode_t mode;};struct kobj_attribute {struct attribute attr;  // 嵌入一个标准属性,包含 char* name 和 short modessize_t (*show)(struct kobject *kobjstruct kobj_attribute *attr,char *buf);ssize_t (*store)(struct kobject *kobjstruct kobj_attribute *attr,const char *bufsize_t count);

};

structdevice_attribute {structattributeattr;  // 嵌入一个标准属性,包含 char* name 和 short modessize_t (*show)(structdevice *devstructkobj_attribute *attr,char *buf);ssize_t (*store)(structdevice *devstructkobj_attribute *attr,constchar *bufsize_tcount);};

grp->name 决定是否建立子目录:

NULL 时属性文件直接落在 kobj 目录下

!NULL 时在该名字下再建一层子目录

attrs 与 bin_attrs 都是指针数组且必须以 NULL 结尾,这是内核遍历的终止条件

1.2 attribute与kobj_attribute的关系

上一小节给出了这两个结构体具体的定义,本小节补充为什么需要对attribute包装?

  • struct attribute:最基础的「文件名 + 权限」描述。

  • struct kobj_attribute:把attribute嵌进去,并追加show/store两个函数指针,通过container_of实现用户函数回调

  • struct device_attribute:面向struct device的同构封装(show第一个参数是struct device*),由于kobject太通用了,开发者通常操作device,使用这个结构体可以少一步转换工作

内核采用「基类 + 子类回调转发」的面向对象手法:sysfs只认struct attribute,具体子系统(kobject / device / driver / bus / class)各自定义带函数的子类,运行时用 container_of 从基类指针反推出子类,再调用用户函数。这是基于kobj_sysfs_ops转发机制实现的

1.3 __ATTR / DEVICE_ATTR 辅助宏

内核源码提供了一组宏,自动完成结构体初始化。宏展开后等价于一份标准的结构体定义:

// include/linux/sysfs.h —— 属性定义辅助宏#define __ATTR(_name_mode_show_store) {           \    .attr = { .name = __stringify(_name),               \              .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \    .show   = _show,                                    \    .store  = _store,                                   \}// 常用便捷宏(无需手写 struct 初始化)#define __ATTR_RO(_name)   { .attr = { .name = __stringify(_name), .mode = 0444 }, .show = _name##_show }#define __ATTR_WO(_name)   { .attr = { .name = __stringify(_name), .mode = 0200 }, .store = _name##_store }#define __ATTR_RW(_name)   { .attr = { .name = __stringify(_name), .mode = 0644 }, .show = _name##_show, .store = _name##_store }// include/linux/device.h —— 面向 struct device 的封装#define DEVICE_ATTR(_name_mode_show_store)        \struct device_attribute dev_attr_##_name = __ATTR(_name_mode_show_store)#define DEVICE_ATTR_RO(_name)  struct device_attribute dev_attr_##_name = __ATTR_RO(_name)#define DEVICE_ATTR_WO(_name)  struct device_attribute dev_attr_##_name = __ATTR_WO(_name)#define DEVICE_ATTR_RW(_name)  struct device_attribute dev_attr_##_name = __ATTR_RW(_name)

1.4 kernfs_node 内核文件系统节点

kernfs_node代表文件系统树中的一个节点(目录、普通文件或符号链接)。它封装了与文件系统交互所需的所有信息,关联到上层的内核对象(如kobject、cgroup等)。对于属性文件而言,其重要成员变量有:

  • name:属性节点的文件名

  • attr:属性节点特有信息,含kernfs_ops,判断属性文件是否支持顺序读取、回调kobject的show函数)

  • priv:指向attribute*,可以通过它反推出用户定义的show和store函数

// 定义在 include/linux/kernfs.h struct kernfs_node {const char          *name;       /* 节点名称(目录名/文件名) */struct rb_noderb;          /* 红黑树节点,用于在父目录中按名称查找 */struct kernfs_node  *parent;     /* 指向父节点的指针 */unsigned inthash;        /* 名称哈希值,加速查找 */const void          *ns;         /* 命名空间标签,用于支持命名空间隔离 */union {struct kernfs_elem_dirdir;     /* 目录特有信息 */struct kernfs_elem_symlink  symlink/* 符号链接特有信息 */struct kernfs_elem_attrattr;    /* 文件(属性)特有信息 */    };void *priv;                  /* 私有数据,常用于存储上层结构指针 */unsigned int flags;          /* 节点标志(类型、状态等) */umode_t mode;            union kernfs_node_idid;struct kernfs_iattrs *iattr/* inode 属性(权限、时间戳等),延迟分配 */atomic_tcount;       /* 引用计数,管理生命周期 */atomic_tactive;      /* 活动引用计数,用于保护正在进行的操作 */};

1.5 kernfs_elem_attr文件属性特有结构体

在kernfs_node的union中,属性节点使用kernfs_elem_attr来保存操作函数与打开状态:

structkernfs_elem_attr {const struct kernfs_ops *ops;struct kernfs_open_node *open;loff_t size;struct kernfs_node *notify_next;};struct kernfs_ops {int (*open)(struct kernfs_open_file *of);int (*release)(struct kernfs_open_file *of);// 省略了一系列函数// seq_show, seq_start, seq_next, seq_stop, read, write, poll, mmap};

二、sysfs_create_group创建属性文件

sysfs_create_group函数在kobj对应的sysfs目录下,根据attribute_group的描述创建一组属性文件。其具体工作内容为:

  • 创建目录:若属性组目录名grp->name不为空,则在kobj目录下创建一个该名字的子目录,属性文件创建在该目录下

  • 创建属性文件:遍历grp->attrs数组,对每个非空属性调用sysfs_add_file创建对应sysfs文件;若is_visible不为空,则调用它确定该属性是否被创建以及对应权限

  • 创建二进制属性文件:遍历grp->bin_attrs数组,创建对应的二进制文件

  • 更新文件判断:若update参数为1,则上述逻辑会先移除已有文件,再创建更新

// 定义在 /fs/sysfs/group.c 文件中int sysfs_create_group(struct kobject *kobjconst struct attribute_group *grp) {return internal_create_group(kobj0grp);}static int internal_create_group(struct kobject *kobjint updateconst struct attribute_group *grp) {struct kernfs_node *kn;kuid_t uid;kgid_t gid;int error;// ...错误检查省略// 用于配置用户id和组id,为了确定文件权限,默认情况下两个都是0// 若kobj->ktype->get_ownership不为空,则内部调用该函数并传出参数uid和gidkobject_get_ownership(kobj, &uid, &gid);    // 1.如果属性组名称存在,则新建子目录,否则获取kobj的目录if (grp->name) {if (update) { // 省略了更新的情况        } else {kn = kernfs_create_dir_ns(kobj->sdgrp->nameS_IRWXU | S_IRUGO | S_IXUGOuidgidkobjNULL);        }    } else {kn = kobj->sd;    }// 2.在 kn 目录下,创建属性文件error = create_files(knkobjuidgidgrpupdate);  }

2.1 内部函数调用链

sysfs_create_group()   => internal_create_group()        => create_files()            => sysfs_add_file_mode_ns()

2.2 create_files 遍历属性数组

该函数完成对属性组结构体attribute_group*的遍历,为每个属性创建文件:

staticint create_files(struct kernfs_node *parentstruct kobject *kobj,kuid_t uidkgid_t gid,const struct attirbute_group *grpint update){struct attribute *const *attr;struct bin_attribute *const *bin_attr;int error = 0i;if (grp->attrs) {for (i = 0attr = grp->attrs; *attr && !errori++, attr++) {umode_t mode = (*attr)->mode;// ...省略部分逻辑error = sysfs_add_file_mode_ns(parent, *attrfalsemodeuidgidNULL);        }    }if (grp->bin_attrs) {for (i = 0bin_attr = grp->bin_attrs; *bin_attri++, bin_attr++) {umode_t mode = (*bin_attr)->attr.mode;// ...省略部分逻辑error = sysfs_add_file_mode_ns(parent, &(*bin_attr)->attrtruemodeuidgidNULL);        }    }return error;}

2.3 sysfs_add_file_mode_ns桥接kernfs

sysfs依赖于内核kernfs内核文件系统。该函数传入kernfs的父目录节点parent、属性attr等参数,在内部调用__kernfs_create_file完成内核节点的创建:

// 定义在 /fs/sysfs/file.c 文件中int sysfs_add_file_mode_ns(struct kernfs_node *parent,const struct attribute *attr,bool is_bin,umode_t mode,kuid_t uid,kgid_t gid,const void *ns){struct lock_class_key *key = NULL;const struct kernfs_ops *ops;struct kernfs_node *kn;loff_t size;/* 根据属性类型设置 kernfs 节点标志 */if (!is_bin) {// 创建kernfs_node时,priv指针指向了目录对应的kobjstruct kobject *kobj = parent->priv;// 创建kobj时,ktype中的sysfs_ops传入了默认参数 kobj_sysfs_ops// 该函数通过传入的attr来调用属性自己的show和store函数const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;// 判断指针是否存在(默认参数 kobj_sysfs_ops 中两个指针都存在)if(sysfs_ops->show && sysfs_ops->store) {if (mode & SYSFS_PREALLOC)ops = &sysfs_file_kfops_rw;elseops = &sysfs_file_kfops_rw;    // ops被设置为该对象        } elseif (sys_ops->show) {// 逻辑相同,判断sys_ops的两个操作函数        }size = PAGE_SIZE;    // ((1UL) << 12)    }
    else {
// ...二进制处理
structbin_ttribute *battr = (void *)attr;
size = battr->size;
    }
/* 创建 kernfs 节点 */kn = __kernfs_create_file (parentattr->name,mode & ~S_IFMT,  /* 去除文件类型位 */uidgid,size,                               &ops,              /* 操作函数 */                               (void *)attr,      /* 私有数据指向 attr */ns,                /* 命名空间 */key);if (IS_ERR(kn))return PTR_ERR(kn);return 0;}

PAGE_SIZE 缓冲:普通属性文件的大小被固定为PAGE_SIZE(通常 4096 字节,即 (1UL) << 12)。这意味着一次show最多返回一页数据——这是sysfs在内存上的约束,后面读路径还会再遇到它

2.4 sysfs_file_kfops_rw操作函数集

该结构体是__kernfs_create_file中传入的ops操作函数,在系统中已预定义,最终会赋值给kernfs_node->attr->ops

staticconststructkernfs_opssysfs_file_kfops_rw = {    .seq_show  =  sysfs_kf_seq_show,    .write     =  sysfs_kf_write,};

2.5 sysfs_kf_seq_show属性文件的默认输出函数

该函数的作用是调用kobject的show函数,并把该文件对应的属性attr传输给kobject的show函数:

staticint sysfs_kf_seq_show(struct seq_file *sfvoid *v){struct kernfs_open_file *of = sf->private;// kernfs_open_file "of" -> kernfs_node "kn" 属性的文件节点 ->// kernfs_node "parent" kobject的文件节点 -> kobject "priv" 属性顶层的kobject// 1. 获取该 kernfs_open_file 对应的kobject    struct kobject *kobj = of->kn->parent->priv;// 获取 kernfs_node->patern->priv->ktype->sysfs_ops 中的show函数// 2. 即 kobject 中的 show 函数const struct sysfs_ops *ops = sysfs_file_ops(of->kn);ssize_t count;char *buf;// 确保seq_file指针中分配的内存剩余空间至少有1页count = seq_get_buf(sf, &buf);if (count < PAGE_SIZE) {seq_commit(sf, -1);return 0;    }memset(buf0PAGE_SIZE);// 3. 调用 kobject 的 show 函数if (ops->show) {count = ops->show(kobjof->kn->privbuf);if (count < 0)return count;    }// 每次写入的大小不能超过一页空间if (count >= (ssize_t)PAGE_SIZE) {printk("fill_read_buffer: %pS returned bad count\n",ops->show);count = PAGE_SIZE - 1;    }seq_commit(sfcount);return 0;}

2.6 __kernfs_create_file物理创建节点

该函数是kernfs的函数,用于在指定父目录下创建新节点并支持命名空间隔离。sysfs基于kernfs实现,创建子目录/文件时都会调用它:

  • parent:父目录的 kernfs 节点

  • name:目录/文件名称

  • mode:访问权限(如 0755)

  • uid / gid:所有者用户/组 ID

  • size:文件大小(属性文件为 PAGE_SIZE)

  • priv:私有数据指针,通常关联 kobject / attr,可通过 kernfs_priv() 获取

  • ns:命名空间隔离,非 NULL 时仅该命名空间进程可见

// 定义在 /fs/kernfs/dir.cstruct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,const char *nameumode_t mode,kuid_t uidkgid_t gidloff_t size,void *privconst void *ns,struct lock_class_key *key) {struct kernfs_node *kn;int rc;// ...省略错误检查/* 1.分配 kernfs_node 结构体 */kn = kernfs_new_node(parentname, (mode & S_IALLUGO) | S_IFDIRuidgidKERNFS_DIR);/* 2.操作接口赋值 */kn->attr.ops = ops;    // kernfs_opskn->attr.size = size;kn->ns = ns;/* 3. 文件属性赋值*/kn->priv = priv;       // 私有数据指向attr属性/* 4.将节点插入到父目录的树中 */rc = kernfs_add_one(kn);if (ops->seq_show)kn->flags |= KERNFS_HAS_SEQ_SHOW;//.../* 返回新节点 */return kn;}EXPORT_SYMBOL_GPL(kernfs_create_dir_ns);

💡is_visible / is_bin_visible 的价值:is_visible 用于确定属性是否被创建及对应权限。内核里它极其重要:同一份驱动要适配不同硬件能力时,可以用它动态隐藏不支持的属性文件。返回0表示不创建该文件,返回非0(通常是 attr->mode)表示以该权限创建。下面给出 drivers/base/topology.c 的真实范例:

/* 真实案例:drivers/base/topology.c 中的 is_visible 回调 */static umode_t topology_is_visible(struct kobject *kobj,struct attribute *attrint unused){/* PPIN 属性:硬件不支持则该文件不可见 */if (attr == &dev_attr_ppin.attr && !topology_ppin(kobj_to_dev(kobj)->id))return 0;                       // 返回 0 = 隐藏该属性文件return attr->mode;                  // 否则返回默认权限}static const struct attribute_group topology_attr_group = {    .attrs      = default_attrs,    .bin_attrs  = bin_attrs,    .is_visible = topology_is_visible,  // 创建前动态决定可见性    .name       = "topology",           // 在设备目录下生成 topology/ 子目录};/* CPU 上线时为该 CPU 设备挂接 topology 接口 */static int topology_add_dev(unsigned int cpu){struct device *dev = get_cpu_device(cpu);return sysfs_create_group(&dev->kobj, &topology_attr_group);}

三、VFS与kernfs关联

属性文件创建后,用户空间通过标准open / read / write系统调用访问它。这中间需要把kernfs节点转化成VFS能识别的inode与file。

3.1 inode 结构体

structinode {/**** 权限和所有权 ****/umode_ti_mode;         /* 文件类型及权限(如 S_IFREG|0644) */kuid_ti_uid;          /* 所有者用户 ID */kgid_ti_gid;          /* 所有者组 ID */unsigned inti_flags;        /* 文件标志(如 FS_IMMUTABLE_FL) *//**** 标识和定位 ****/ino_ti_ino;          /* inode 号,在文件系统中唯一 */union {const unsigned int i_nlink/* 硬链接计数 */unsigned int __i_nlink;    };dev_ti_rdev;         /* 设备号(对设备文件) */loff_ti_size;         /* 文件大小(字节) */struct timespec64 i_atime;     /* 最后访问时间 */struct timespec64 i_mtime;     /* 最后修改时间 */struct timespec64 i_ctime;     /* 最后状态改变时间 *//*** 操作函数表 ***/const struct inode_operations   *i_op;      /* inode 操作 */const struct file_operations    *i_fop;     /* 文件操作(默认) */struct address_space            *i_mapping/* 地址空间映射 */struct address_spacei_data;     /* 内嵌地址空间 */struct Super_block              *i_sb;      /* 指向所属超级块 *//**** 私有数据和关联对象 ****/void          *i_private;       /* 文件系统私有数据 */union {struct hlist_head i_dentry/* 指向该 inode 的 dentry 链表 */struct rcu_headi_rcu;    /* 用于 RCU 释放 */    };// ... 省略部分字段};

3.2 kernfs_init_inode节点→inode 绑定

kernfs_init_inode是kernfs中用于初始化一个新分配VFS inode的关键函数。它将struct inode与对应的struct kernfs_node*关联,并根据节点类型设置合适的操作函数表:

// 定义在 /fs/kernfs/inode.c 文件中static void kernfs_init_inode(struct kernfs_node *knstruct inode *inode) {kernfs_get(kn);inode->i_private = kn;    // inode与内核文件节点关联inode->i_mapping->a_ops = &kernfs_aops;    // 定义文件系统处理页缓存inode->i_op = &kernfs_iops;    // 临时设置通用的inode操作表inode->i_generation = kn->id.generation;    // 唯一标识,表示已经创建set_default_inode_attr(inodekn->mode);    // 设置默认inode属性kernfs_refresh_inode(kninode);           // 刷新属性switch (kernfs_type(kn)) {case KERNFS_DIR:inode->i_op = &kernfs_dir_iops;inode->i_fop = &kernfs_dir_fops;if (kn->flags & KERNFS_EMPTY_DIR)make_empty_dir_inode(inode);break;case KERNFS_FILE:// 将kernfs_file_fops赋值给i_fop// 包含open、read、write等函数inode->i_size = kn->attr.size;inode->i_fop = &kernfs_file_fops;break;case KERNFS_LINK:inode->i_op = &kernfs_symlink_iops;break;default:BUG();    }unlock_new_inode(inode);    // 解锁该inode,正式可以访问}

3.3 kernfs_file_fops默认文件操作

在kernfs_init_inode中,会给属性文件对应的inode->i_fop赋值为默认的文件操作结构体:

conststructfile_operationskernfs_file_fops = {    .read = kernfs_fop_read,    .write = kernfs_fop_write,    .llseek = generic_file_llseek,    .mmap = kernfs_fop_mmap,    .open = kernfs_fop_open,    .release = kernfs_fop_release,    .poll = kernfs_fop_poll,    .fsync = noop_fsync,};

3.4 kernfs_fop_open打开文件

staticint kernfs_fop_open(struct inode *inodestruct file *file) {// 1. 从inode私有数据获取对应的kernfs节点struct kernfs_node *kn = inode->i_private;// 2. 获取节点所属的kernfs根struct kernfs_root *root = kernfs_root(kn);const struct kernfs_ops *ops;struct kernfs_open_file *of;bool has_readhas_writehas_mmap;int error = -EACCES;// ...省略错误检查// 3.从kernfs内核文件中获取kn->attr.ops文件具体的操作函数ops = kernfs_ops(kn);has_read = ops->seq_show || ops->read || ops->mmap;has_write = ops->write || ops->mmap;has_mmap = ops->mmap;// 4.初始化 kernfs_open_file 结构of = kzalloc(sizeof(structkernfs_open_file), GFP_KERNEL);mutex_init(&of->mutex);of->kn = kn;        // 保存kernfs节点指针of->file = file;    // 保存VFS文件对象指针of->atomic_write_len = ops->atoic_write_len;    // 单次写入字节限制// 5.如果操作集要求预分配缓冲区,则分配单次写入字节限制或PAGE_SIZEif (ops->prealloc) {intlen = of->atomic_write_len ?: PAGE_SIZE;of->prealloc_buf = kmalloc(len+1GFP_KERNEL);mutex_init(&of->prealloc_mutex);    }// 6.初始化seq_file接口(若文件支持顺序读取)if (ops->seq_show)error = seq_open(file, &kernfs_seq_ops);elseerror = seq_open(fileNULL);// 7.关联seq_file和ofof->seq_file = file->private_data;of->seq_file->private = of;// 8.设置文件写标志,表示允许带带偏移的鞋操作if (file->f_mode & FMODE_WRITE)file->f_mode |= FMODE_PWRITE;// 9.将of添加到节点打开文件链表error = kernfs_get_open_node(knof);// 10.调用文件特定的open回调if (ops->open) {error = ops->open(of);    }// 11.释放活动kernfs_put_active(kn);return 0;}

3.5 seq_open与seq_file顺序读取框架

seq_open是内核seq_file接口的核心函数,用于初始化一个顺序文件的私有数据,并关联到已打开的VFS file对象:

/* *@file: 已打开的VFS文件对象 *@op:   包含一组回调函数 *返回值: 成功返回0*/int seq_open(struct file *fileconst struct seq_operations *op) {struct seq_file *p;p = kmem_cache_zalloc(seq_file_cacheGFP_KERNEL);// 将分配好的seq_file指针存入私有对象,后续read、write可以通过private操作file->private_data = p;   mutex_init(&p->lock);p->op = op;        // 保存操作集p->file = file;    // 保存file指针file->f_version = 0;file->f_mode &= ~FMODE_PWRITE;return 0;}

seq_file是内核提供的简化顺序文件读写接口,主要用于输出大量文本数据。开发者只需实现一套(start / next / stop / show)回调,seq_file自动处理缓冲区管理、分页与用户态读取。kernfs为其预置了如下操作集:

staticconststructseq_operationskernfs_seq_ops = {    .start = kernfs_seq_start,    .next = kernfs_seq_next,    .stop = kernfs_seq_stop,    .show = kernfs_seq_show,};

3.6 读路径

  • kernfs_fop_read

  • seq_read

  • kernfs_seq_show

staticssize_t kernfs_fop_read(struct file *filechar __user *user_buf,size_t countloff_t *ppos){struct kernfs_open_file *of = kernfs_of(file);// return ((struct seq_file *)file->private_data)->private;// 在__kernfs_create_file函数中,若seq->show则赋值该标志if(of->kn->flags & KERNFS_HAS_SEQ_SHOWreturn seq_read(fileuser_bufcountppos)elsereturn kernfs_file_direct_read(ofuser_bufcountppos);}
ssize_tseq_read(structfile *filechar__user *bufsize_tsizeloff_t *ppos){structseq_file *m = file->private_data;void *p;// 省略大部分代码err = m->op->show(mp);err = copy_to_user(bufm->bufn);}
staticintkernfs_seq_show(structseq_file *sfvoid *v) {structkernfs_open_file *of = sf->private;// ...returnof->kn->attr.ops->seq_show(sfv);   }

kernfs_seq_show只是转发器,它从of->kn->attr.ops取出真正由sysfs注册的seq_showsysfs_kf_seq_show

3.7 回到属性

  • sysfs_kf_seq_show

  • kobj_attr_show

  • 用户show

staticint sysfs_kf_seq_show(structseq_file *sfvoid *v) {struct kernfs_open_file *of = sf->private;struct kobject *kobj = of->kn->parent->priv;const structsysfs_ops *ops = sysfs_file_ops(of->kn);ssize_t count;char *buf;count = seq_get_buf(sf, &buf);if (count < PAGE_SIZE) {seq_commit(sf, -1);return0;    }memset(buf0PAGE_SIZE);if (ops->show) {count = ops->show(kobjof->kn->privbuf);if (count < 0)returncount;    }if (count >= (ssize_tPAGE_SIZE) {count = PAGE_SIZE -1;    }seq_commit(sfcount);return 0;}
staticinlinesize_tseq_get_buf(structseq_file *mchar **bufp) {BUG_ON(m->count > m->size);if (m->count < m->size        *bufp = m->buf + m->count;else        *bufp = NULL;returnm->size - m->count;}
staticconststructsysfs_ops *sysfs_file_ops(structkernfs_node *kn) {structkobject *kobj = kn->parent->priv;if (kn->flags & KERNFS_LOCKEDP)lockdep_assert_held(kn);returnkobj->ktype ? Kobj->ktype->sysfs_ops : NULL;}
staticssize_t kobj_attr_show(struct kobject *kobjstructattribute *attrchar *buf){struct kobj_attribute *kattr;ssize_t ret = -EIO;kattr = container_of(attrstruct kobj_attributeattr);if (kattr->show)ret = kattr->show(kobjkattrbuf);return ret;}

3.8 写路径

读有完整链路,写完全对称

echo val > filekernfs_fop_writesysfs_kf_writekobj_sysfs_ops->storekattr->store

/* 写路径:echo -> kernfs_fop_write -> sysfs_kf_write -> kobj_attr_store */static ssize_t sysfs_kf_write(struct kernfs_open_file *ofchar *bufsize_t count){const struct sysfs_ops *ops = sysfs_file_ops(of->kn);/* kernfs 已保证 buf 以 '\0' 结尾(预分配或 PAGE_SIZE 缓冲) */return ops->store(of->kn->parent->privof->kn->privbufcount);}/* kobj_sysfs_ops 中的 store 转发:从 attribute 反推 kobj_attribute */static ssize_t kobj_attr_store(struct kobject *kobjstruct attribute *attr,const char *bufsize_t count){struct kobj_attribute *kattr;ssize_t ret = -EIO;kattr = container_of(attrstruct kobj_attributeattr);if (kattr->store)ret = kattr->store(kobjkattrbufcount);return ret;}

🔑 读写签名差异:

底层sysfs_opsshow/store第一个参数是struct kobject*kobj_attributeshow/store第一个参数是struct kobject*,第二个参数是struct kobj_attribute*——kobj_attr_showcontainer_of把基类attribute还原成子类,从而把「属性自身」也传给了用户定义的回调,这正是用户能在回调里访问kattr->private或判断是哪个属性的原因

四、小结:四级回调链全景

把全文串起来,内核完成了一系列复杂的回调,最终实现了用户定义函数的调用:

  • file 中保存了 seq_file 结构体,首先调用 seq_file->op->show

  • 从而调用了 kernfs_node 中保存的函数 kernfs_node->attr.ops->seq_show

  • 从而调用了 kobj->ktype->sysfs_ops->show 函数;

  • 最后调用了 kattr->show 函数。

💡 一句话概括整条链:整条路径从 file 回到 kernfs_node,将 attr 作为参数传递,再回到 kobject,然后通过 kobject 中的函数调用 attr 中用户定义的函数。可以压缩为一行:cat /sys/.../foo → vfs_read → kernfs_fop_read → sysfs_kf_seq_show → kobj_attr_show → foo_show()

五、案例:一个完整的属性组驱动模块

理论讲完,下面给出一个最小内核模块,演示定义kobj_attribute → 组织 attribute_group → 调用 sysfs_create_group → 用户态读写的流程。插入后会在 /sys/kernel/mydrv/ 下生成 level(可读写)与 enabled(只读)两个文件:

#include<linux/module.h>#include <linux/kernel.h>#include <linux/kobject.h>#include <linux/sysfs.h>#include <linux/slab.h>/* 驱动私有数据结构,作为属性文件的背后状态 */struct mydrv_ctx {int level;        // 当前档位 0~3bool enabled;      // 开关};/* ---- show / store 回调 ---- */static ssize_t level_show(struct kobject *kobjstruct kobj_attribute *attrchar *buf){struct mydrv_ctx *ctx = dev_get_drvdata(kobj_to_dev(kobj)); // 仅示例return sprintf(buf"%d\n"ctx->level);}static ssize_t level_store(struct kobject *kobjstruct kobj_attribute *attr,const char *bufsize_t count){struct mydrv_ctx *ctx = dev_get_drvdata(kobj_to_dev(kobj));int val;if (kstrtoint(buf10, &val))return -EINVAL;if (val < 0 || val > 3)return -EINVAL;ctx->level = val;return count;}static struct kobj_attribute level_attr = __ATTR(level0644level_showlevel_store);static ssize_t enabled_show(struct kobject *kobjstruct kobj_attribute *attrchar *buf){structmydrv_ctx *ctx = dev_get_drvdata(kobj_to_dev(kobj));returnsprintf(buf"%d\n"ctx->enabled);}static struct kobj_attribute enabled_attr = __ATTR(enabled0444enabled_showNULL);/* ---- 属性组 ---- */static struct attribute *mydrv_attrs[] = {    &level_attr.attr,    &enabled_attr.attr,NULL,                       // 必须以 NULL 结尾};static const struct attribute_group mydrv_group = {    .attrs = mydrv_attrs,// .name = "mydrv",        // 若指定 name,会在 kobj 目录下再建子目录 mydrv/};static struct kobject *mydrv_kobj;static struct mydrv_ctxctx = { .level = 1, .enabled = true };static int __init mydrv_init(void){/* 在 /sys/kernel/ 下创建目录 mydrv */mydrv_kobj = kobject_create_and_add("mydrv"kernel_kobj);if (!mydrv_kobj)return -ENOMEM;/* 创建属性文件 level / enabled */return sysfs_create_group(mydrv_kobj, &mydrv_group);}static void __exit mydrv_exit(void){sysfs_remove_group(mydrv_kobj, &mydrv_group);kobject_put(mydrv_kobj);}module_init(mydrv_init);module_exit(mydrv_exit);MODULE_LICENSE("GPL");

📌 自动建文件的更优写法:若属性属于某个 kobj_type 的默认组,可在 kobj_type.default_groups(或驱动的 dev_groups)里挂上组数组,kobject 注册时由 create_dir() 自动调用 sysfs_create_groups 完成创建,连 sysfs_create_group 都不用显式调用:

/* 让 kobject 注册时自动创建默认属性组(无需手动调用 sysfs_create_group) */static struct attribute *foo_attrs[] = {    &dev_attr_value.attr,    &dev_attr_mode.attr,NULL,};ATTRIBUTE_GROUPS(foo);   /* 生成 foo_groups[] = { &foo_group, NULL } */static const struct kobj_type foo_ktype = {    .release    = foo_release,    .sysfs_ops  = &kobj_sysfs_ops,    .default_groups = foo_groups,   /* create_dir() 时自动 populate_dir + create_groups */};/* 在驱动中更常见:platform_driver 直接挂 dev_groups */static struct platform_drivermydrv = {    .driver = {        .name = "mydev",        .dev_groups = my_groups,    /* 设备 probe 时自动建属性文件 */    },};

六、二进制属性 bin_attribute

6.1 bin_attribute:突破单页限制

普通attribute的show受PAGE_SIZE单页缓冲限制;bin_attribute 的read/write回调带loff_t offsize_t count参数,支持分块、随机偏移读取任意大小数据(典型如硬件寄存器 dump、固件导出)。定义用BIN_ATTR宏,并挂到attribute_group.bin_attrs

/* 硬件寄存器 dump:突破单页 4KB 限制,支持随机偏移读取 */static ssize_t regs_read(struct file *filpstruct kobject *kobj,struct bin_attribute *bin_attr,char *bufloff_t offsize_t count){struct mydev *dev = container_of(kobjstructmydevkobj);size_t regs_size = 0x1000;if (off >= regs_size)               // 越界return 0;if (off + count > regs_size)        // 截断到合法范围count = regs_size - off;memcpy_fromio(bufdev->regs_base + offcount);  // 从 MMIO 空间拷贝return count;}/* BIN_ATTR 宏定义二进制属性(名称, 权限, read, write, size) */static BIN_ATTR(regs0400regs_readNULL0x1000);static struct bin_attribute *my_bin_attrs[] = {    &bin_attr_regs,NULL,};static const struct attribute_group my_group = {    .attrs     = my_attrs,    .bin_attrs = my_bin_attrs,};

6.2 移除与对称释放

属性组的移除必须和创建严格对称,否则会在 sysfs 留下僵尸文件节点:

/* 删除属性组(与创建严格对称,防止 sysfs 残留文件) */void sysfs_remove_group(struct kobject *kobjconst struct attribute_group *grp){struct kernfs_node *parent;if (grp->name) {/* 有 name 时,文件在 name 子目录下 */parent = kernfs_find_and_get(kobj->sdgrp->name);if (!parent)return;    } else {parent = kobj->sd;kernfs_get(parent);    }kernfs_remove_by_name(parentgrp->name ?: "");   // 移除子目录或文件remove_files(parentkobjgrp);                   // 逐一移除属性文件kernfs_put(parent);}

七、系列回顾

【Linux驱动】字符设备驱动内核源码深度解析

Linux 内核】设备模型基石:Kobject内核源码解析

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 16:51:19 HTTP/2.0 GET : https://f.mffb.com.cn/a/505740.html
  2. 运行时间 : 0.240375s [ 吞吐率:4.16req/s ] 内存消耗:4,789.70kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=556e89bfb327d8f296114eb0997f1dfb
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000978s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001715s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000749s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.002898s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001521s ]
  6. SELECT * FROM `set` [ RunTime:0.000601s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001484s ]
  8. SELECT * FROM `article` WHERE `id` = 505740 LIMIT 1 [ RunTime:0.001595s ]
  9. UPDATE `article` SET `lasttime` = 1787302279 WHERE `id` = 505740 [ RunTime:0.036520s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000799s ]
  11. SELECT * FROM `article` WHERE `id` < 505740 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001325s ]
  12. SELECT * FROM `article` WHERE `id` > 505740 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000984s ]
  13. SELECT * FROM `article` WHERE `id` < 505740 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002275s ]
  14. SELECT * FROM `article` WHERE `id` < 505740 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.006744s ]
  15. SELECT * FROM `article` WHERE `id` < 505740 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002839s ]
0.245316s