Linux kobjects and sysfs filesystem

  • 了解linux kobject, kobj_type, and kset.

1.Introduction

  Linux设备模型的核心是使用Bus、Class、Device、Driver四个核心数据结构,将大量的、不同功能的硬件设备(以及驱动该硬件设备的方法),以树状结构的形式,进行归纳、抽象,从而方便Kernel的统一管理。

  而硬件设备的数量、种类是非常多的,这就决定了Kernel中将会有大量的有关设备模型的数据结构。这些数据结构一定有一些共同的功能,需要抽象出来统一实现,否则就会不可避免的产生冗余代码。这就是Kobject诞生的背景。

  目前为止,Kobject主要提供如下功能:

  • 通过parent指针,可以将所有Kobject以层次结构的形式组合起来。
  • 使用一个引用计数(reference count),来记录Kobject被引用的次数,并在引用次数变为0时把它释放(这是Kobject诞生时的唯一功能)。
  • 和sysfs虚拟文件系统配合,将每一个Kobject及其特性,以文件的形式,开放到用户空间。

Note:在Linux中,Kobject几乎不会单独存在。它的主要功能,就是内嵌在一个大型的数据结构中,为这个数据结构提供一些底层的功能实现。 Linux driver开发者,很少会直接使用Kobject以及它提供的接口,而是使用构建在Kobject之上的设备模型接口。

1.1.kobject, kobj_type,kset 关系

  The device model provides a single mechanism for representing devices and describing their topology in the system.

  kobject(kernel object) is heart of device model represented by a structure struct kobject. It provides basic facilities like reference counting, a name, and a parent pointer, enabling the creation of a hierarchy of objects. Kobjects are usually embedded in other structures and are generally not interesting on their own.A struct kobject represents a kernel object, maybe a device or so, such as the things that show up as directory in the sysfs filesystem.

  Kobjects are associated with a specific type, called a ktype, short for kernel object type. They are represented by represented by struct kobj_type.

  Kobjects are then grouped into sets, called ksets, which are represented by struct kset.Ksets provide two functions. First, their embedded kobject acts as a base class for a group of kobjects. Second, ksets aggregate together related kobjects. In sysfs, kobjects are the individual directories in the filesystem.

在这里插入图片描述
1.2. kobject

1.2.1.kobject structure

  A struct kobject represents a kernel object, maybe a device or so, such as the things that show up as directory in the sysfs filesystem.

#include <linux/kobject.h>
struct kobject {
    const char      *name; /* kobject对象的名字,对应sysfs中的目录名 */
    struct list_head    entry; /* 在kset中的链表节点 */
    struct kobject      *parent; /* 用于构建sysfs中kobjects的层次结构,指向父目录 */
    struct kset     *kset; /* 所属kset */
    struct kobj_type    *ktype; /* 特定对象类型相关,用于跟踪object及其属性 */
    struct sysfs_dirent *sd; /* 指向该目录的dentry私有数据 */
    struct kref     kref; /* kobject的引用计数,初始值为1 */
    unsigned int state_initialized:1; /* kobject是否初始化,由kobject_init()设置 */
    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; /* 是否忽略uevent事件 */
};
  • name表示kobject对象的名字,对应sysfs下的一个目录。
  • entry是kobject中插入的head_list结构,
  • parent是指向当前kobject父对象的指针,体现在sys结构中就是包含当前kobject对象的目录对象,
  • kset表示当前kobject对象所属的集合,
  • ktype表示当前kobject的类型。
  • sd用于表示VFS文件系统的目录项,是设备与文件之间的桥梁,sysfs中的符号链接就是通过kernfs_node内的联合体实现的。
  • kref是对kobject的引用计数,当引用计数为0时,就回调之前注册的release方法释放该对象。
  • state_initialized:1初始化标志位,在对象初始化时被置位,表示对象是否已经被初始化。
  • state_in_sysfs:1表示kobject对象在sysfs中的状态,在对应目录中被创建则置1,否则为0。
  • state_add_uevent_sent:1是添加设备的uevent事件是否发送标志,添加设备时会向用户空间发送uevent事件,请求新增设备。
  • state_remove_uevent_sent:1是删除设备的uevent事件是否发送标志,删除设备时会向用户空间发送uevent事件,请求卸载设备

1.2.2.kobject 函数集

 /* include/linux/kobject.h */
  extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
  extern __printf(3, 4) __must_check
  int kobject_add(struct kobject *kobj, struct kobject *parent,
          const char *fmt, ...);
  extern __printf(4, 5) __must_check
  int kobject_init_and_add(struct kobject *kobj,
               struct kobj_type *ktype, struct kobject *parent,
               const char *fmt, ...);
  
  extern void kobject_del(struct kobject *kobj);  //注销kobject
  
  extern struct kobject * __must_check kobject_create(void);
  extern struct kobject * __must_check kobject_create_and_add(const char *name,
                          struct kobject *parent);
                                                                                                             
  extern int __must_check kobject_rename(struct kobject *, const char *new_name);
  extern int __must_check kobject_move(struct kobject *, struct kobject *);
  
  extern struct kobject *kobject_get(struct kobject *kobj);
  extern struct kobject * __must_check kobject_get_unless_zero(
	                          struct kobject *kobj);
  extern void kobject_put(struct kobject *kobj);

1.2.3. 创建kobject

  Kobject必须动态分配,而不能静态定义或者位于堆栈之上,分配方法有两种。

1.2.3.1.通过kmalloc自行分配(一般是跟随上层数据结构分配),并在初始化后添加到kernel。这种方法涉及如下接口:

 1: /* include/linux/kobject.h, line 85 */
 2: extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
 3: extern __printf(3, 4) __must_check
 4: int kobject_add(struct kobject *kobj, struct kobject *parent,
 5:                 const char *fmt, ...);
 6: extern __printf(4, 5) __must_check
 7: int kobject_init_and_add(struct kobject *kobj,
 8:             struct kobj_type *ktype, struct kobject *parent,
 9:             const char *fmt, ...);

1). 初始化一个kobject对象:

  • void kobject_init(struct kobject *kobj, struct kobj_type *ktype);

该函数完成对kobj的初始化:

  • 初始化kobj->kref引用计数为初始值1;
  • 初始化kobj->entry空链表头;
  • kobj->ktype = ktype;
  • 然后将kobj->state_initialized置为1,表示该kobject已初始化。

2). 初始化之后,通过kobject_add()将kobj添加到系统中:

  • int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, …);

或者使用如下接口(初始化并注册kobject):

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, …);

参数含义和上述接口相同。

1.2.3.1.2. 使用kobject_create创建

  Kobject模块可以使用kobject_create自行分配空间,并内置了一个ktype(dynamic_kobj_ktype),用于在计数为0是释放空间。代码如下:

 1: /* include/linux/kobject.h, line 96 */
 2: extern struct kobject * __must_check kobject_create(void);
 3: extern struct kobject * __must_check kobject_create_and_add(const char *name,
 4:             struct kobject *parent);
  • kobject_create,该接口为kobj分配内存空间,并以dynamic_kobj_ktype为参数,调用kobject_init接口,完成后续的初始化操作。

  • kobject_create_and_add,是kobject_create和kobject_add的组合,不再说明。

  • dynamic_kobj_release,直接调用kfree释放kobj的空间。

1.2.4.kref

  A struct kref is an object that handles reference counting. It is defined as

include/linux/kref.h:
struct kref {
        atomic_t refcount;
};

  and available methods are:

void kref_init(k) k->refcount = 1;
void kref_get(k) k->refcount++;
void kref_put(k,release) if (!–k->refcount) release(k);

  kref成员是object对象的引用计数,初始值为1,通过kref_get()和kref_put()可对该计数进行增减操作。kref_get()和kref_put()是内核中通用的引用计数的操作,针对kobject,使用下面两个封装函数:

struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);

  为方便跟踪一个kobject,在使用kobject之前,都要使用kobject_get()将参数kobj的kref加1,该函数返回kobj自身的指针。kobject_put()则将kref减1,当kref减到0时,则kobject_release()被调用来释放kobject资源。

  kobject_release()会调用ktype的release方法做实际的释放操作,并释放为name分配的内存。如果有必要,还会向用户态发送KOBJ_REMOVE事件,并通过kobject_del()将kobject从sysfs中删除。

1.3. kobj_type

  A struct kobj_type structure describes the behavior of kobjects. A kobj_type structure describes the type of object that embeds a kobject by means of ktype field. Every structure that embeds a kobject needs a corresponding kobj_type, which will control what happens when the kobject is created and destroyed, and when attributes are read or written to. Every kobject has a field of the type struct kobj_type, which stands for kernel object type:

 139 struct kobj_type {                                                                                     
  140     void (*release)(struct kobject *kobj);
  141     const struct sysfs_ops *sysfs_ops;
  142     struct attribute **default_attrs;
  143     const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
  144     const void *(*namespace)(struct kobject *kobj);
  145     void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
  146 };  
  • kobj_type必须实现release方法,该方法由kobject的外层结构来定义,用来释放特定模块相关的kobject资源。

  • default_attrs定义了一系列默认属性,default_attrs是一个二级指针,可以对每个kobject设置多个默认属性(最后一个属性用NULL填充)。

  The key to sysfs attributes is the kobject’s kobj_type pointer. When we looked at kobject types before, we passed over a couple of sysfs-related entries. One, called default_attrs, describes the attributes that all kobjects of this type should have; it is a pointer to an array of pointers to attribute structures:

    struct attribute {
	char			*name;
	struct module 		*owner;
	mode_t			mode;
    };
    
   struct sysfs_ops {
	ssize_t	(*show)(struct kobject *kobj, struct attribute *attr, 
                        char *buffer);
	ssize_t	(*store)(struct kobject *kobj, struct attribute *attr, 
			const char *buffer, size_t size);
    };

  如下所示,定义struct kobj_type,然后调用kobject_init_and_add 添加到系统中。

static struct kobj_type foo_ktype = {
	.sysfs_ops = &foo_sysfs_ops,
	.release = foo_release,
	.default_attrs = foo_default_attrs,
};

static const struct sysfs_ops foo_sysfs_ops = {
	.show = foo_attr_show,
	.store = foo_attr_store,
};

static struct attribute *foo_default_attrs[] = {
	&foo_attribute.attr,
	&baz_attribute.attr,
	&bar_attribute.attr,
	NULL, 
};
retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);

1.4.kset

  kset表示一组kobject的集合,kobject通过kset组织成层次化的结构,所有属于该kset的kobjetc结构的parent指针指向kset包含的kobject对象,构成一个父子层次关系这些kobject可以是不同或相同的类型(kobj_type)。sysfs中的设备组织结构很大程度上都是根据kset进行组织的,比如"/sys/drivers"目录就是一个kset对象,包含系统中的驱动程序对应的目录,驱动程序的目录又kobject表示。比如在平台设备模型中,当我们注册一个设备或驱动到平台总线,其实是将对应的kobject挂接到platform总线的kset上,每种总线都是维护两条链表(两个kset),一条用于链接挂接在上面的驱动(驱动kset),一条用于链接挂接在上面的设备(设备kset)。

include/linux/kobject.h:
168 struct kset {
169     struct list_head list;
170     spinlock_t list_lock;
171     struct kobject kobj;
172     const struct kset_uevent_ops*uevent_ops;  
173 }; 

  list_head(169)还是那个用来挂在链表上的结构。kobj(171)是归属于该kset的所有的kobject的共有parent,这个parent就是体现内核设备组织结构的关键。
在这里插入图片描述
kset 和kobject关系:
在这里插入图片描述
1.4.1.Kset的初始化、注册

  Kset是一个特殊的kobject,因此其初始化、注册等操作也会调用kobject的相关接口,除此之外,会有它特有的部分。另外,和Kobject一样,kset的内存分配,可以由上层软件通过kmalloc自行分配,也可以由Kobject模块负责分配,具体如下。

 1: /* include/linux/kobject.h, line 166 */
 2: extern void kset_init(struct kset *kset);
 3: extern int __must_check kset_register(struct kset *kset);
 4: extern void kset_unregister(struct kset *kset);
 5: extern struct kset * __must_check kset_create_and_add(const char *name,
 6:             const struct kset_uevent_ops *u,
 7:             struct kobject *parent_kobj);
  • kset_init,该接口用于初始化已分配的kset,主要包括调用kobject_init_internal初始化其kobject,然后初始化kset的链表。需要注意的时,如果使用此接口,上层软件必须提供该kset中的kobject的ktype。

  • kset_register,先调用kset_init,然后调用kobject_add_internal将其kobject添加到kernel。

  • kset_unregister,直接调用kobject_put释放其kobject。当其kobject的引用计数为0时,即调用ktype的release接口释放kset占用的空间。

  • kset_create_and_add,会调用内部接口kset_create动态创建一个kset,并调用kset_register将其注册到kernel。

小结:
  Linux内核大量使用面向对象的设计思想,用面向对象语言常用的UML类图分析Linux设备管理的"类"之间的关系:
在这里插入图片描述

2.Sysfs system

2.1.Introduction

  The Linux kernel provides a virtual file system called sysfs. By providing virtual files, sysfs is able to export information about various kernel sub-systems, hardware devices and associated device drivers from the kernel’s device model to user space. To further explore sysfs, dive deep into this article.

  There is a need to provide information related to each process, to the user space, which can then be used by programs such as ps. The /proc file system was created for this purpose. Through the proc file system, each process has its directory in the /proc folder. It was originally designed to provide process related information to user space. Adding directories and files to the /proc file system is easier; so, many kernel sub-systems started using this file system for displaying information to the user space. It is also used to take inputs from the user space to control settings inside the kernel modules. But the /proc file system is getting cluttered with lots of non-process related information.

  From the Linux 2.5 development cycle, a new interface called the /sys file system has been introduced. Sysfs is a RAM based file system. It is designed to export the kernel data structures and their attributes from the kernel to the user space, which then avoids cluttering the /proc file system.

  The advantages of sysfs over procfs are as follows:

  • A cleaner, well-documented programming interface
  • Automatic clean-up of directories and files, when the device is removed from the system
  • The enforced one item per file rule, which makes for a cleaner user interface

2.2.Sysfs mounting

  By default, sysfs is compiled in the Linux kernel. It is dependent on CONFIG_SYSFS being enabled in the kernel configuration. If sysfs is not already mounted, then you can do so by using the following command:

mount -t sysfs sysfs /sys

2.3.How kobjects get sysfs entries

  Sysfs is a non-persistent virtual filesystem that provides a global view of the system and exposes the kernel object’s hierarchy (topology) by means of their kobjects. Each kobjects shows up as a directory, and files in a directory representing kernel variables, exported by the related kobject. These files are called attributes, and can be read or written.

  There are two functions which are used to set up a kobject. If you use kobject_init() by itself, you will get a standalone kobject with no representation in sysfs. If, instead, you use kobject_register() (or call kobject_add() separately), a sysfs directory will be created for the kobject; no other effort is required on the programmer’s part.

  The name of the directory will be the same as the name given to the kobject itself. The location within sysfs will reflect the kobject’s position in the hierarchy you have created. In short: the kobject’s directory will be found in its parent’s directory, as determined by the kobject’s parent field. If you have not explicitly set the parent field, but you have set its kset pointer, then the kset will become the kobject’s parent. If there is no parent and no kset, the kobject’s directory will become a top-level directory within sysfs, which is rarely what you really want.

2.4. Directories

  The sysfs file system is mounted on /sys. The top-level directories are shown. Following is a brief description of some of these directories:

  • /sys/block
    This directory contains entries for each block device in the system. Symbolic links point to the physical device that the device maps to in the physical device tree.

  • /sys/bus
    This directory contains subdirectories for each physical bus type supported in the kernel. Each bus type has two subdirectories: devices and drivers. The devices directory lists devices discovered on that type of bus. The drivers directory contains directories for each device driver registered with the bus type. Driver parameters can be viewed and manipulated.

  • /sys/class
    This directory contains every device class registered with the kernel. Device classes describe a functional type of device. Examples include input devices, network devices, and block devices.

  • /sys/devices
    This directory contains the global device hierarchy of all devices on the system. This directory also contains a platform directory and a system directory. The platform directory contains peripheral devices specific to a particular platform such as device controllers. The system directory contains non-peripheral devices such as CPUs and APICs.

  • /sys/firmware
    This directory contains subdirectories with firmware objects and attributes.

  • /sys/module
    This directory contains subdirectories for each module that is loaded into the kernel.

  • /sys/power
    The system power state can be controlled from this directory. The disk attribute controls the method by which the system suspends to disk. The state attribute allows a process to enter a low power state.

  • /sys/kernel
    这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于sysctl(/proc/sys/kernel) 接口中。

  Each of these directories corresponds to a kobject, some of which are exported as kernel symbols. These are:

  • kernel_kobj which corresponds to /sys/kernel
  • power_kobj for /sys/power
  • firmware_kobj which is for /sys/firmware, exported in the drivers/base/firmware.c source
    file
  • hypervisor_kobj for /sys/hypervisor, exported in the drivers/base/hypervisor.c
  • fs_kobj which corresponds to /sys/fs, exported in the fs/namespace.c file

  However, class/, dev/, devices/, are created during the boot by the devices_init function in drivers/base/core.c in kernel source, block/ is created in block/genhd.c, and bus/ is created as a kset in drivers/base/bus.c.

  When a kobject directory is added to sysfs (using kobject_add), where it is added depends on the kobject’s parent location. If its parent pointer is set, it is added as a subdirectory inside the parent’s directory. If the parent pointer is NULL, it is added as a subdirectory inside kset->kobj. If neither parent nor kset fields are set, it maps to the root level directory in sysfs (/sys).

2.4.1./sys/kernel

  kernel/ksysfs.c:
  241 static int __init ksysfs_init(void)
  242 {
  243     int error;
  244 
  245     kernel_kobj = kobject_create_and_add("kernel", NULL);
  246     if (!kernel_kobj) {
  247         error = -ENOMEM;
  248         goto exit;
  249     }
  250     error = sysfs_create_group(kernel_kobj, &kernel_attr_group);
  251 }

  如果要在/sys/kernel创建文件,调用kobject_create_and_add,其中第二个参数添加为 kernel_kobj。

2.5.Function

int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr);

int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr) ;
int sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr) ;

 //用于一次创建多个属性节点
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); 
void sysfs_remove_group(struct kobject * kobj, const struct attribute_group * grp);

int sysfs_create_groups(struct kobject *kobj, const struct attribute_group **groups); 
void sysfs_remove_groups(struct kobject * kobj, const struct attribute_group  **groups);

int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name);
void sysfs_remove_link(struct kobject * kobj, const char * name) ;

void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr)

2.5.1.Allow sysfs attribute files to be pollable

  Here we will see how not to make CPU wasting polling to sense sysfs attributes data availability.The idea is to use the poll or select system calls to wait for the attribute’s content to change. Thepatch to make sysfs attributes pollable was created by Neil Brown and Greg Kroah-Hartman. The kobject manager (the driver which has access to the kobject) must support notification to allow poll or select to return (be released) when the content changes. The magic function that does the trick comes from the kernel side, and is sysfs_notify().

sysfs_notify 函数:
  实质是调用sysfs_notify_dirent(),用来唤醒在读写属性文件(sysfs节点)时因调用select()或poll()而阻塞的用户进程。

3.Example:

kobject-example.c:

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

static int foo;
static int baz;
static int bar;
static struct kobject *example_kobj;

static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", foo);
}

static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
		const char *buf, size_t count)
{
	int ret;
	ret = kstrtoint(buf, 10, &foo);
	if (ret < 0)
		return ret;
	return count;
}

static struct kobj_attribute foo_attribute = 
	__ATTR(foo, 0664, foo_show, foo_store);

static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
	int var;
	if (strcmp(attr->attr.name, "baz") == 0)
		var = baz;
	else
		var = bar;
	return sprintf(buf, "%d\n", var);
}

static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
		const char *buf, size_t count)
{
	int var, ret;
	ret = kstrtoint(buf, 10, &var);
	if (ret < 0)
		return ret;
	if (strcmp(attr->attr.name, "baz") == 0)
		baz = var;
	else
		bar = var;
	return count;
}

static struct kobj_attribute baz_attribute = 
	__ATTR(baz, 0664, b_show, b_store);

static struct kobj_attribute bar_attribute = 
	__ATTR(bar, 0664, b_show, b_store);

static struct attribute *attrs[] = {
	&foo_attribute.attr,
	&baz_attribute.attr,
	&bar_attribute.attr,
	NULL,
};

static struct attribute_group attr_group = {
	.attrs = attrs,
};

static int __init example_kobject_init(void)
{
	int retval;

	example_kobj = kobject_create_and_add("kobject_albert", kernel_kobj);
	if (!example_kobj)
		return -ENOMEM;

	retval = sysfs_create_group(example_kobj, &attr_group);
	if (retval)
		kobject_put(example_kobj);

	return retval;	
}

static void __exit example_kobject_exit(void)
{
	kobject_put(example_kobj);
}

module_init(example_kobject_init);
module_exit(example_kobject_exit);
MODULE_LICENSE("GPL v2");

Kset-example.c:

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

struct foo_obj {
	struct kobject kobj;
	int foo;
	int baz;
	int bar;
};
#define to_foo_obj(x) container_of(x, struct foo_obj, kobj)

struct foo_attribute {
	struct attribute attr;
	ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
	ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
};
#define to_foo_attr(x) container_of(x, struct foo_attribute, attr)

static ssize_t foo_attr_show(struct kobject *kobj,
			     struct attribute *attr,
			     char *buf)
{
	struct foo_attribute *attribute;
	struct foo_obj *foo;

	attribute = to_foo_attr(attr);
	foo = to_foo_obj(kobj);

	if (!attribute->show)
		return -EIO;

	return attribute->show(foo, attribute, buf);
}

static ssize_t foo_attr_store(struct kobject *kobj,
			      struct attribute *attr,
			      const char *buf, size_t len)
{
	struct foo_attribute *attribute;
	struct foo_obj *foo;

	attribute = to_foo_attr(attr);
	foo = to_foo_obj(kobj);

	if (!attribute->store)
		return -EIO;

	return attribute->store(foo, attribute, buf, len);
}

static const struct sysfs_ops foo_sysfs_ops = {
	.show = foo_attr_show,
	.store = foo_attr_store,
};

static void foo_release(struct kobject *kobj)
{
	struct foo_obj *foo;

	foo = to_foo_obj(kobj);
	kfree(foo);
}


static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
			char *buf)
{
	return sprintf(buf, "%d\n", foo_obj->foo);
}

static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
			 const char *buf, size_t count)
{
	int ret;

	ret = kstrtoint(buf, 10, &foo_obj->foo);
	if (ret < 0)
		return ret;

	return count;
}
static struct foo_attribute foo_attribute =
	__ATTR(foo, 0664, foo_show, foo_store);

static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
		      char *buf)
{
	int var;

	if (strcmp(attr->attr.name, "baz") == 0)
		var = foo_obj->baz;
	else
		var = foo_obj->bar;
	return sprintf(buf, "%d\n", var);
}

static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
		       const char *buf, size_t count)
{
	int var, ret;

	ret = kstrtoint(buf, 10, &var);
	if (ret < 0)
		return ret;

	if (strcmp(attr->attr.name, "baz") == 0)
		foo_obj->baz = var;
	else
		foo_obj->bar = var;
	return count;
}

static struct foo_attribute baz_attribute =
	__ATTR(baz, 0664, b_show, b_store);
static struct foo_attribute bar_attribute =
	__ATTR(bar, 0664, b_show, b_store);


static struct attribute *foo_default_attrs[] = {
	&foo_attribute.attr,
	&baz_attribute.attr,
	&bar_attribute.attr,
	NULL, 
};

static struct kobj_type foo_ktype = {
	.sysfs_ops = &foo_sysfs_ops,
	.release = foo_release,
	.default_attrs = foo_default_attrs,
};

static struct kset *example_kset;
static struct foo_obj *foo_obj;
static struct foo_obj *bar_obj;
static struct foo_obj *baz_obj;

static struct foo_obj *create_foo_obj(char *name)
{
	struct foo_obj *foo;
	int retval;

	foo = kzalloc(sizeof(*foo), GFP_KERNEL);
	if (!foo)
		return NULL;
	
	foo->kobj.kset = example_kset;  
	retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
	if (retval) {
		kobject_put(&foo->kobj);
		return NULL;
	}

	kobject_uevent(&foo->kobj, KOBJ_ADD);
	return foo;
}


static void destroy_foo_obj(struct foo_obj *foo)
{
	kobject_put(&foo->kobj);
}

static int __init example_init(void)
{
	example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
	if (!example_kset)
		return -ENOMEM;

	foo_obj = create_foo_obj("foo");
	if (!foo_obj)
		goto foo_error;

	bar_obj = create_foo_obj("bar");
	if (!bar_obj)
		goto bar_error;

	baz_obj = create_foo_obj("baz");
	if (!baz_obj)
		goto baz_error;

	return 0;

baz_error:
	destroy_foo_obj(bar_obj);
bar_error:
	destroy_foo_obj(foo_obj);
foo_error:
	kset_unregister(example_kset);
	return -EINVAL;
}

static void __exit example_exit(void)
{
	destroy_foo_obj(baz_obj);
	destroy_foo_obj(bar_obj);
	destroy_foo_obj(foo_obj);
	kset_unregister(example_kset);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");

Debug:

$:/sys/kernel/kset_example$ ll
total 0
drwxr-xr-x  5 root root 0  9月 24 11:26 ./
drwxr-xr-x 14 root root 0  9月 24 11:26 ../
drwxr-xr-x  2 root root 0  9月 24 11:27 bar/
drwxr-xr-x  2 root root 0  9月 24 11:27 baz/
drwxr-xr-x  2 root root 0  9月 24 11:27 foo/

参考:
Documentation/filesystems/sysfs.txt
https://lwn.net/Articles/54651/
https://opensourceforu.com/2015/05/talking-to-the-kernel-through-sysfs/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux Device Driver (3edtion)原版 1. An Introduction to Device Drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 The Role of the Device Driver 2 Splitting the Kernel 4 Classes of Devices and Modules 5 Security Issues 8 Version Numbering 10 License Terms 11 Joining the Kernel Development Community 12 Overview of the Book 12 2. Building and Running Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Setting Up Your Test System 15 The Hello World Module 16 Kernel Modules Versus Applications 18 Compiling and Loading 22 The Kernel Symbol Table 28 Preliminaries 30 Initialization and Shutdown 31 Module Parameters 35 Doing It in User Space 37 Quick Reference 39 3. Char Drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 The Design of scull 42 Major and Minor Numbers 43 Some Important Data Structures 49 ,ldr3TOC.fm.4587 Page v Thursday, January 20, 2005 9:30 AMvi | Table of Contents Char Device Registration 55 open and release 58 scull’s Memory Usage 60 read and write 63 Playing with the New Devices 70 Quick Reference 70 4. Debugging Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Debugging Support in the Kernel 73 Debugging by Printing 75 Debugging by Querying 82 Debugging by Watching 91 Debugging System Faults 93 Debuggers and Related Tools 99 5. Concurrency and Race Conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 Pitfalls in scull 107 Concurrency and Its Management 107 Semaphores and Mutexes 109 Completions 114 Spinlocks 116 Locking Traps 121 Alternatives to Locking 123 Quick Reference 130 6. Advanced Char Driver Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 ioctl 135 Blocking I/O 147 poll and select 163 Asynchronous Notification 169 Seeking a Device 171 Access Control on a Device File 173 Quick Reference 179

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值