Linux内核设计与实现---kobject sysfs

kobject sysfs

  • 1 kobject
  • 2 ktype
  • 3 kset
  • 4 subsystem
  • 5 别混淆了这些结构体
  • 6 管理和操作kobject
  • 7 引用计数
    • kref
  • 8 sysfs
    • sysfs中添加和删除kobject
    • 向sysfs添加文件
  • 9 内核事件层

2.6内核增加了一个引人注目的新特性—同一设备模型。设备模型提供了独立的机制专门表示设备,并描述在系统中的拓扑结构。

1 kobject

设备模型的核心部分就是kobject,它由struct kobject,定义于头文件linux/kobject.h中。kobject类似于C#或java这些面向对象语言中的object对象类,提供了诸如计数、名称和父指针等字段,可以创建对象的层次结构。

struct kobject {char			* k_name;char			name[KOBJ_NAME_LEN];struct kref		kref;struct list_head	entry;struct kobject		* parent;struct kset		* kset;struct kobj_type	* ktype;struct dentry		* dentry;
};

k_name指针指向kobject名称,如果名称长度小于KOBJ_NAME_LEN,那么该kobject的名称就存放在name数组中,k_name指向数组头,如果名称长度大于KOBJ_NAME_LEN,则动态分配一个足够大的缓冲区来存放kobject的名称。KOBJ_NAME_LEN当前为20个字节。

parent指针指向kobject的父对象。因此,kobject就会在内核中构造一个对象层次结构,并且可以将多个对象间的关系表项出来。

dentry指针指向dentry结构体,在sysfs中该结构体就表示这个kobject。

kref实现了kobject的引用计数。

kobject通常是嵌入到其他结构体中的,其单独意义其实并不大。当kobject被嵌入到其他结构体中时,该结构体便拥有了kobject提供的标准功能。

2 ktype

kobject对象中的ktype域,该结构体定义于头文件linux/kobject.h中

struct kobj_type {void (*release)(struct kobject *);struct sysfs_ops	* sysfs_ops;struct attribute	** default_attrs;
};

ktype是为了描述一族kobject所具有的普遍特性。因此,不在需要每个kobject都分别定义自己的特性,而是将这些特性在ktype结构体中一次定义,然后所有“同类”的kobject都能共享一样的特性。

release指针指向在kobject引用计数减为零时要被调用的析构函数。该函数负责释放所有的kobject使用的内存和其他相关清理工作。

sysfs_ops变量指向sysfs_ops结构体。该结构体表述了sysfs文件读写是的特性。

default_attrs指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认属性。属性描述了给定对象的特征,如果该kobject被导出到sysfs中,那么这些属性都将相应地作为文件而导出。数组中的最后一项必须为NULL。

3 kset

kobject中的kset域,是kobject对象的集合体。把它看成是一个容器,可将所有相关的kobject对象,比如“全部的块设备”置于一个位置。kset把kobject集中到一个集合中。

kset指针指向kset集合,kset集合由kset结构体表示,定义于头文件linux/kobject.h中

struct kset {struct subsystem	* subsys;struct kobj_type	* ktype;struct list_head	list;struct kobject		kobj;struct kset_hotplug_ops	* hotplug_ops;
};

其中ktype指向kset集合中kobject对象的类型。list连接该集合中所有的kobject对象,kobj指向的kobject对象代表了该集合的基类。hotplug_ops指向一个用于处理集合中kobject对象的热插拔操作的结构体。
subsys指针指向该结构体相关的struct subsystem结构体。

4 subsystem

subsystem在内核中代表高层概念,它是一个或多个kset的大集合。

struct subsystem {struct kset		kset;struct rw_semaphore	rwsem;
};

虽然subsystem结构体只指向一个kset,但是多个kset可以通过其subsys指针指向一个subsystem。这种单向关系意味着不可能仅仅通过一个subsystem结构体就找到所有的kset。

subsystem中的kset字段指向的是subsystem中默认的kset,任rwsem字段是一个读写信号量,该信号量用来对subsystem和它的所有的kset进行并发访问保护。

5 别混淆了这些结构体

这里最重要的是kobject,它由struct kobject表示。kobject为我们引入了诸如计数、父子关系和对象名称等基本对象道具,并且是以一个统一方式提供这些功能。不过kobject本身意义不大,需要被嵌入到其他数据结构中。

kobject与一个特定的ktype对象关联,ktype由struct kobject_type结构体表示,在kobject中ktype字节指向该对象。ktype定义了一些kobject相关的默认特性:析构行为、sysfs行为以及其他一些默认属性。

kset集合由struct kset结构体表示。kset提供了两个功能。第一,其中嵌入的kobject作为kobject组合基类。第二,kset将相关的kobject集合在一起。在sysfs中,这些相关的kobject将以独立的目录出现在文件系统中。

subsystem表示的更为宏观,它是包含kset的集合,由struct subsystem结构体表示。sysfs中的根目录映射的便是subsystem。

这些数据结构的内在关系如下:
在这里插入图片描述

6 管理和操作kobject

多数时候,驱动程序开发者并不直接处理kobject,因为kobject是被嵌入到一些特殊类型结构体中的,而且由相关的 设备驱动程序在幕后管理。

使用kobject的第一步需要先来声明初始化它。kobject通过函数kobject_init进行初始化,该函数定义在文件linux/kobject.h中:

void kobject_init(struct kobject *kobj);

该函数的唯一参数就是需要初始化的kobject对象,在调用初始化函数前,kobject必须清空。如果kobject未被清空,那么只需要调用memset()即可。

在清理后,就可以安全地初始化parent和kset字段。

在这里插入图片描述
初始化后,必须采用kobject_set_name()函数为kobject设置名称:

int kobject_set_name(struct kobject *kobj,const char *fmt,...);

该函数利用了printf()和printk()风格的可变参数队列来为kobject设置名称。

初始化了kobject并设置名称后,还需要为它设置kset字段以及可能的ktype字段(可选)。如果kset没有被提供,那么仅需要设置ktype,否则kset中的ktype字段将优先被使用。

7 引用计数

kobject的主要功能之一就是为我们提供了一个同一的引用计数系统。初始化后,kobject的引用计数设置为1,只要引用计数不为0,那么该对象就会继续保留在内存中。任何包含对象引用的代码首先要增加该对象的引用计数,当代码结束后则减少它的引用计数。增加引用计数称为获得(get)对象的引用,减少引用计数称为释放(put)对象的引用。当引用计数减少到0时,对象便可以被销毁。

增加一个引用计数可通过kobject_get()函数完成:

struct kobject * kobject_get(struct kobject *kobj);

该函数正常情况下返回一个指向kobject的指针,如果失败,返回NULL。

减少引用计数是通过kobject_put()完成:

void kobject_put(struct kobject *kobj);

如果对应的kobject的引用计数减少到0,则与该kobject关联的ktype中的析构函数将被调用。

kref

kobject的引用计数是通过kref结构体实现的,该结构体定义在头文件linux/kref.h中:

struct kref {atomic_t refcount;
};

其中唯一的字段是用来存放引用计数的原子变量。在使用kref之前,必须先通过kref_init来初始化它:

void kref_init(struct kref *kref)
{atomic_set(&kref->refcount,1);
}

这个函数简单地将原子变量置为1,所以kref一旦被初始化,它表示的引用计数便固定为1。
要获得对kref的引用,需要调用kref_get()函数

void kref_get(struct kref *kref)
{WARN_ON(!atomic_read(&kref->refcount));atomic_inc(&kref->refcount);
}

该函数增加引用计数值,它没有返回值。减少对kref的引用,调用函数kref_put():

void kref_put(struct kref *kref,void (*release)(struct kref *kref))
{WARN_ON(release == NULL);WARN_ON(release == (void (*)(struct kref*))kfree);if(atomic_dec_and_test(&kref->refcount))release(kref);
}

该函数将使引用计数减1,如果减少到0,则调用作为参数提供的release函数,注意WARN_ON()声明,提供的release函数不能简单地采用kfree,它必须是一个仅接受一个kref结构体作为参数的特有函数,而且还没有返回值。

上述的所有函数定义与声明分别在文件lib/kref.c和文件linux/kref.h中。

8 sysfs

sysfs文件系统是一个处于内存中的虚拟文件系统,它为我们提供了kobject对象层次结构的视图。帮助用户能以一个简单文件系统的方式来观察系统中各种设备的拓扑结构,借助属性对象,kobject可以用到处文件的方式,将内核变量提供给用户读取或写入。

sysfs的诀窍是把kobject对象与目录项紧紧联系起来,这是通过kobject对象中的dentry域实现的。dentry结构体表示目录项,通过连接kobject到指定的目录项上,无疑方便地将kobject映射到该目录上。从此,把kobject导出形成文件系统就变得如同在内存中构建目录项一样简单。由于kobject被映射到目录项,同时kobject形成一棵树,因此sysfs的生成就很简单了。

sysfs中添加和删除kobject

仅仅初始化kobject是不能自动将其导出到sysfs中的,想要把kobject导入sysfs,需要用到函数kobject_add():

int kobject_add(struct kobject *kobj);

kobject在sysfs中位置取决于kobject在对象层次结构中的位置。如果kobject的parent指针被设置,那么在sysfs中kobject将被映射为父目录下的子目录,如果parent没有设置,那么kobject将被映射为kset->kobj中的子目录。如果给定的kobject中parent或kset字段都没有设置,那么就认为kobject没有父对象,所以就会被映射成sysfs下的根级目录。

你不必分别调用kobject_init()和kobject_add(),因为系统提供函数kobject_register()可帮助你一步到位:

int kobject_register(struct kobject *kobj);

该函数即初始化给定的kobject对象,同时又将其加入到对象层次结构体中。

从sysfs中删除一个kobject对应文件目录,需要使用函数kobject_del():

void kobject_del(struct kobject *kobj);

类似地,函数kobject_unregister()包含了kobjeect_del和kobject_put二者的功能:

void kobject_unregister(struct kobject *kobj);

上述四个函数都定义与文件lib/kobject.c中,声明于文件linux/kobject.h中。

向sysfs添加文件

sysfs仅仅是一个漂亮的树,但是没有提供实际数据的文件。

默认的文件集合是通过kobject和kset中的ktype字段提供的。因此所有具有相同类型的kobject在它们对应的sysfs目录下都拥有相同的默认文件集合,kobj_type字段含有一个字段,default_attrs,它是一个attribute结构体数组。这些属性负责将内核数据映射成sysfs中的文件。

attribute结构体定义在文件linux/sysfs.h中:

struct attribute {char			* name;	/* 属性名 */struct module 		* owner;	/* 所属模块,如果存在 */mode_t			mode;	/* 权限 */
};

name提供该属性的名称,最终出现在sysfs中的文件名就是它;owner字段在存在所属模块的情况下指向其所属module结构体。如果一个模块没有该属性,那么该字段为NULL。mode表示了sysfs中该文件的权限。sysfs中的所有文件和目录的uid和gid标志均为0。

sysfs_ops字段描述了如何使用他们,sysfs_ops字段指向了一个定义于文件linux/sysfs.h的同名的结构体:

struct sysfs_ops {ssize_t	(*show)(struct kobject *, struct attribute *,char *);ssize_t	(*store)(struct kobject *,struct attribute *,const char *, size_t);
};

show方法在读操作时被调用。它会拷贝由attr提供的属性值到buffer指定的缓冲区中,缓冲区大小为PAGE_SIZE字节。
store方法在写操作时调用,它会从buffer中读取size大小的字节,并将其存放入attr表示的属性结构体变量中。

  • 创建新属性
int sysfs_create_file(struct kobject *kobj,const struct attribute *attr);

sysfs_create_file来创建新属性,通过attr参数指向相应的attribute结构体,而kobj则制定了属性所在的kobject对象。

除了添加文件外,还有可能需要创建符号连接,在sysfs中创建一个符号连接相当简单:

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

该函数创建的符号连接名由name指定,连接则由kobj对应的目录映射到target指定的目录

  • 删除新属性
    删除一个属性需通过函数sysfs_remove_file完成:
sysfs_remove_file(struct kobject *kobj,const struct attribute *attr);

另外由sysfs_create_link()创建的符号连接可通过函数sysfs_remove_link删除:

void sysfs_remove_link(struct kobject *kobj,char *name);

上述的四个函数在文件linux/kobject.h中声明,sys_create_file和sysfs_remove_file函数定义于文件fs/sysfs/file.c中;sysfs_create_link和sysfs_remove_link函数定义在文件fs/sysfs/symlink.c中。

9 内核事件层

内核事件层把事件模拟为信号,从明确的kobject发出,所以每个事件源都是一个sysfs路径。

每个事件都有一个可选的负载,相比传递任意一个表示负载的字符串到用户空间而言,内核事件层使用sysfs属性代表负载。

在内核代码中向用户空间发送信号使用函数kobject_uevent():

int kobject_uevent(struct kobject *kobj,enum kobject_action action struct attribute *attr);

第一个参数指定发送该信号的kobject对象,实际的内核事件将包含该kobject映射到sysfs的路径。
第二个参数指定来描述该信号的动作。实际的内核事件将包含一个映射成枚举类型kobject_action的字符串。该函数不是直接提供一个字符串,而是利用一个枚举变量来提高可重用性和保证类型安全,而且也消除了打字错误或别的其他错误,该枚举变量定义于文件linux/kobject_uevent.c中,其形式为KOBJ_foo。当前值包含KOBJ_MOUNT、KOBJ_UNMOUNT、KOBJ_ADD等,这些值分别映射"mount"、“unmonut”、“add”等。
最后一个参数是指向attribute结构体的可选指针,它可看作为事件的“负载”。

该函数分配内存,所以可以睡眠。但在内核中实现了原子和非原子操作两个版本,其不同在于原子操作使用GFP_ATOMIC标志分配内存。

int kobject_uevent_atomic(struct kobject *kobj,enum kobject_action action,struct attribute *attr);

这两个函数分别定义和声明在文件lib/kobject_uevent.c于文件linux/kobject_uevent.h中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379601.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开发Windows Mobile今日插件 -- 内存电量,桌面便笺,桌面记单词

本篇文章讲解的是开发 Windows Mobile 上的今日插件。关于是今日插件,在 PPC 或者 SP SDK 的帮助文档中有相关的章节介绍,在网络上也有一些帖子和资源讲解。在这里简要回顾一下。今日插件就是在windows mobile的桌面上显示的条目,例如系统提供…

算法---递归

递归结题三部曲 何为递归?程序反复调用自身即是递归。 我自己在刚开始解决递归问题的时候,总是会去纠结这一层函数做了什么,它调用自身后的下一层函数又做了什么…然后就会觉得实现一个递归解法十分复杂,根本就无从下手。 相信…

给定条件找最小值c语言程序_根据给定条件最小化n的最小步骤

给定条件找最小值c语言程序Problem statement: 问题陈述: Given a number n, count minimum steps to minimize it to 1 performing the following operations: 给定数字n ,执行以下操作,计算最少的步骤以将其最小化为1: Operat…

那个年代的苏联歌曲

小时候,不时听父亲提起电影《这里的黎明静悄悄》,怎么也想不到如此美丽的名字为什么要和战争联系起来。后来在大学看了这部电影之后,开始认为这名字是合适的,因为电影讲的是女性——战场中的女性,各自都怀揣着爱情去保…

linux系统编程---进程总结

进程控制总结1 进程创建的三种方式forkvfrokclone2 进程终止进程正常退出returnexit_exit进程异常退出进程收到某个信号,而该信号使进程终止abort3 进程等待进程等待的方法waitwaitpid4 进程替换替换原理替换函数制作一个简单的shell1 进程创建的三种方式 参考文章…

银行账务转账系统(事务处理)

流程如下: 创建项目工程如下: transfer包下的代码如下: package beyond.transfer.dao;import java.sql.Connection; import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import beyond.utils.DataSourceUtils;pu…

【msdn wpf forum翻译】TextBox中文本 中对齐 的方法

原文链接:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/49864e35-1dbf-4292-a361-93f1a8400558问题:TextBox中文本中对齐,使用 TextBox.HorizontalContentAlignment"Center"行不通(TextBox.VerticalConte…

c语言 函数的参数传递示例_C语言中带有示例的remove()函数

c语言 函数的参数传递示例C语言中的remove()函数 (remove() function in C) The remove() function is defined in the <stdio.h> header file. remove()函数在<stdio.h>头文件中定义。 Prototype: 原型&#xff1a; int remove(const char* filename);Parameter…

使用ThreadLocal绑定连接资源(事务)

dao层代码如下&#xff1a; package beyond.transfer.dao;import java.sql.Connection; import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import beyond.utils.DataSourceUtils; import beyond.utils.MyDataSourceUtils;public class TransferDa…

算法---栈和队列

栈和队列1 栈栈的顺序存储栈的链式存储2 队列队列的顺序存储队列的链式存储3 栈和队列的应用用栈实现队列用队列实现栈最小栈1 栈 参考文章&#xff1a; https://zhuanlan.zhihu.com/p/346164833 https://zhuanlan.zhihu.com/p/120965372#:~:text%E6%A0%88%E6%98%AF%E4%B8%80%…

在WebBrowser中通过模拟键盘鼠标操控网页中的文件上传控件

引言 这两天沉迷了Google SketchUp&#xff0c;刚刚玩够&#xff0c;一时兴起&#xff0c;研究了一下WebBrowser。 我在《WebBrowser控件使用技巧分享》一文中曾谈到过“我现在可以通过WebBrowser实现对各种Html元素的操控&#xff0c;唯独无法控制Html的上传控件”&#xff0c…

编写最简单的字符设备驱动

编写最简单的字符设备驱动1 编写驱动代码2 编写makefile3 编译和加载驱动4 编写应用程序测试驱动参考文章&#xff1a; linux驱动开发第1讲&#xff1a;带你编写一个最简单的字符设备驱动 linux驱动开发第2讲&#xff1a;应用层的write如何调用到驱动中的write 1 编写驱动代码…

Linux设备驱动开发---字符设备驱动程序

字符设备驱动程序1 主设备和次设备的概念设备号的注册和释放静态方法动态方法区别2 设备文件操作struct file_operations与struct file、struct inode关系3 分配和注册字符设备class_createcdev_adddevice_create4 字符设备驱动程序字符设备通过字符&#xff08;一个接一个的字…

Java中的异常栈轨迹和异常链

Java中允许对异常进行再次抛出&#xff0c;以提交给上一层进行处理&#xff0c;最为明显的例子为Java的常规异常。 常规异常&#xff1a;有Java所定义的异常&#xff0c;不需要异常声明&#xff0c;在未被try-catch的情况下&#xff0c;会被默认上报到main()方法。 Example: pu…

同步---信号量

信号量1 信号量2 驱动程序和测试程序3 内核的具体实现总结1 信号量 Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已经被占用的信号量时&#xff0c;信号量会将其放到一个等待队列&#xff0c;然后让其睡眠&#xff0c;这时处理器去执行其他代码。当持有信号量的进…

算法---KMP算法

字符串1 KMP算法状态机概述构建状态转移1 KMP算法 原文链接&#xff1a;https://zhuanlan.zhihu.com/p/83334559 先约定&#xff0c;本文用pat表示模式串&#xff0c;长度为M&#xff0c;txt表示文本串&#xff0c;长度为N&#xff0c;KMP算法是在txt中查找子串pat&#xff0…

文件上传 带进度条(多种风格)

文件上传 带进度条 多种风格 非常漂亮&#xff01; 友好的提示 以及上传验证&#xff01; 部分代码&#xff1a; <form id"form1" runat"server"><asp:ScriptManager ID"scriptManager" runat"server" EnablePageMethods&quo…

同步---自旋锁

1 自旋锁的基本概念 自旋锁最多只能被一个可执行线程持有&#xff0c;如果一个执行线程试图获得一个已经被使用的自旋锁&#xff0c;那么该线程就会一直进行自旋&#xff0c;等待锁重新可用。在任何时刻&#xff0c;自旋锁都可以防止多余一个的执行线程同时进入临界区。 Linu…

实习日志----4.播放时段参数设置

由于客户在下发广告时&#xff0c;一则广告可在多个时段播放&#xff0c;这就需要设置多个播放时段的参数。 但在这种情况下&#xff0c;我并不知道用户每次需要下发几个时段&#xff0c;所以前台不能设定死。 因此我要实现这么一个功能&#xff0c;让用户根据自己的需要来动态…

linux系统编程---线程总结

线程总结1 线程的实现线程创建线程退出线程等待线程清理2 线程的属性线程的分离线程的栈地址线程栈大小线程的调度策略线程优先级3 线程的同步互斥锁读写锁条件变量信号量线程是系统独立调度和分配的基本单位。同一进程中的多个线程将共享该进程中的全部系统资源&#xff0c;例…