内存文件系统 /sys文件 bus总线 kobject kset(一)

sysfs挂载/sys目录下 如

bloak目录,存放块设备信息。每个块设备都会对应bloak目录下的一个子目录记录设备的相关信息。

bus 每条总线在该文件夹下对应一个子目录,如i2c目录,子目录如i2c下又对应两个子目录 Devices目录和Drivers目录 Drivers目录:包括注册到该总线的所有的设备驱动 Devices目录:对应属于该总线类型的设备

class目录:按照设备功能进行分类,如net子目录包含了网络接口

devices:所有设备

kernel:内核的配置参数

module:系统中所有模块的信息

firmware:系统中的固件

FS:系统中的文件系统

power:系统中电源选项

204914_T1Mh_274829.png

所有的设备都位于devices目录中,别的目录下设备会通过链接连接到device目录下

kobject 面向对象的管理机制,创建一个kobject对象,就会在/sys下创建一个目录

structk object {
constchar
*name;
structlist_head
entry;
structkobject
*parent;//指向父对象
structkset
*kset;
structkobj_type
*ktype;
structsysfs_dirent *sd;
structkref
kref;//对象引用计数
unsignedint state_initialized:1;
unsignedint state_in_sysfs:1;
unsignedint state_add_uevent_sent:1;
unsignedint state_remove_uevent_sent:1;
};

注册kobject步骤 函数

kobject_init(struct kobject * kobj) 初始化
kobject_add(struct kobject * kobj) 添加对象
kobject_init_and_add(struct kobject *kobj, struct kobj_type*ktype,struct kobject *parent, const char *fmt, ...) 相当于完成上面两步。struct kobject *parent表示目录,如果为空创建在sysfs目录下,*fmt表示设备名字
kobject_del() 删除对象
kobject_get()将kobject对象计数加1
kobject_put()  kobject对象计数减1

文件属性

Kobject的ktype成员是一个指向kobj_type结构的指针, 该结构中记录了kobject对象的一些属性以及一些操作。
struct kobj_type {
void(*release)(struct kobject *kobj);   
struct sysfs_ops *sysfs_ops;       //读写 属性文件
struct attribute **default_attrs;  //属性文件
};
release:用于释放kobject占用的资源,当kobject的引用计数为0时被调用。

属性结构体

struct attribute {
char *name; /*属性文件名*/
mode_tmode; /*属性的保护位*/ //读写属性
};
struct attribute (属性):对应于kobject的目录下的一个文件,Name成员就是文件名。

结构体读写

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

Show:当用户读属性文件时,该函数被调用,该函数将属性值存入buffer中返回给用户态;
Store:当用户写属性文件时,该函数被调用,用于存储用户传入的属性值。

kobject模块测试源码

#include <linux/device.h> 
#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/init.h> 
#include <linux/string.h> 
#include <linux/sysfs.h> 
#include <linux/stat.h> 
  
MODULE_AUTHOR("cicue"); 
MODULE_LICENSE("Dual BSD/GPL");
 
/*声明release、show、store函数*/

void obj_test_release(struct kobject *kobject); 
ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf);
ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);
/*对应于kobject的目录下的一个文件,Name成员就是文件名*/  
struct attribute test_attr = { 
        .name = "kobj_config", 
        .mode = S_IRWXUGO, 
}; 
  
static struct attribute *def_attrs[] = { 
        &test_attr, 
        NULL, 
}; 
  
/kobject对象的操作 
struct sysfs_ops obj_test_sysops = 
{ 
        .show = kobj_test_show, 
        .store = kobj_test_store, 
}; 
 
/*定义kobject对象的一些属性及对应的操作*/ 
struct kobj_type ktype =  
{ 
        .release = obj_test_release, 
        .sysfs_ops=&obj_test_sysops, 
        .default_attrs=def_attrs, 
};
/*release方法释放该kobject对象*/  
void obj_test_release(struct kobject *kobject) 
{ 
        printk("eric_test: release .\n"); 
}
/*当读文件时执行的操作*/ 
ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf)
{ 
        printk("have show.\n"); 
        printk("attrname:%s.\n", attr->name); 
        sprintf(buf,"%s\n",attr->name); 
        return strlen(attr->name)+2; 
}
/*当写文件时执行的操作*/  
ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)
{ 
        printk("havestore\n"); 
        printk("write: %s\n",buf); 
        return count; 
} 
  
struct kobject kobj;//声明kobject对象
 
static int kobj_test_init(void) 
{ 
        printk("kboject test init.\n"); 
        kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");//初始化kobject对象kobj,并将其注册到linux系统
        return 0; 
} 
  
static void kobj_test_exit(void) 
{ 
        printk("kobject test exit.\n"); 
        kobject_del(&kobj); 
} 
  
module_init(kobj_test_init);
module_exit(kobj_test_exit);

运行结果是创建目录  /sys/kobject_test/kobj_config

热插拔事件,当系统配置发生变化时,如添加kset到系统,移动kobject,一个通知会从内核空间移动到用户空间,这既是热插拔,热插拔会导致用户空间相应的处理程序被调用,这些程序通过调用设备驱动,创建设备节点来响应热插拔。

kset是kobject的集合

struct kset {
struct list_head list; //连接该kset中所有kobject的链表头
spinlock_t list_lock;
struct kobject kobj; //内嵌的kobject
struct kset_uevent_ops *uevent_ops; //处理热插拔事件的操作集合
}

子目录中还有子目录是kset,如果子目录只有文件是kobject.

1)int kset_register(struct kset *kset)
在内核中注册一个kset
2)void kset_unregister(struct kset *kset)
从内核中注销一个kset

功能结构

Struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env);
}
三个函数功能
1)filter:决定是否将事件传递到用户空间。如果filter返回0,将不传递事件。(例: uevent_filter)
2)name:用于将字符串传递给用户空间的热插拔处理程序。
3)uevent:将用户空间需要的参数添加到环境变量中。(例:dev_uevent)

当该kset所管理的kobject和kset状态发生变化时(如被加入,移动),这三个函数将被调用。

kset模块测试源码

#include <linux/device.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/string.h>
    #include <linux/sysfs.h>
    #include <linux/stat.h>
    #include <linux/kobject.h>
    MODULE_AUTHOR("cicue");
    MODULE_LICENSE("GPL");
    struct kset *kset_p;
    struct kset kset_c;
    /* 函数声明 */
    void obj_test_release(struct kobject *);
    ssize_t kobj_test_show(struct kobject *,struct attribute *,char *);
    ssize_t kobj_test_store(struct kobject *,struct attribute *,const char *,size_t);
    static struct attribute test_attr =
    {
            .name = "kobj_config",
            .mode = S_IRWXUGO,
    };
    static struct attribute *def_attrs[] =
    {
            &test_attr,
            NULL,
    };
    static struct sysfs_ops obj_test_sysops =
    {
            .show = kobj_test_show,
            .store = kobj_test_store,
    };
    static struct kobj_type ktype =
    {
            .release = obj_test_release,
            .sysfs_ops = &obj_test_sysops,
            .default_attrs = def_attrs,
};
    void obj_test_release(struct kobject *kobject)
    {
            printk("[kobj_test: release!]\n");
    }
    ssize_t kobj_test_show(struct kobject *kobject,struct attribute *attr,char *buf)
    {
            printk("Have show -->\n");
            printk("attrname: %s.\n",attr->name);
            sprintf("buf,%s\n",attr->name);
            return strlen(attr->name) + 2;
    }
    ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr, const char *buf,size_t size)
    {
            printk("Have store -->\n");
            printk("write: %s.\n",buf);
            return size;
    }
    static int kset_filter(struct kset *kset,struct kobject *kobj)
    {
        printk("Filter: kobj %s.\n",kobj->name);
        return 1;
    }
    static const char *kset_name(struct kset *kset,struct kobject *kobj)
    {
        static char buf[20];
        printk("Name kobj %s.\n",kobj->name);
        sprintf(buf,"%s","kset_name");
        return buf;
    }
    static int kset_uevent(struct kset *kset,struct kobject *kobj, struct kobj_uevent_env *env)

{
        int i = 0;
        printk("uevent: kobj %s.\n",kobj->name);
        while(i < env->envp_idx)
        {
            printk("%s.\n",env->envp[i]);
            i ++;
        }
        return 0;
    }
    static struct kset_uevent_ops uevent_ops =
    {
        .filter = kset_filter,
        .name = kset_name,
        .uevent = kset_uevent,
    };

    static int __init kset_test_init(void)
    {
        int ret = 0;
        printk("kset test init!\n");
        /* 创建并注册 kset_p */
        kset_p = kset_create_and_add("kset_p",&uevent_ops,NULL);
        /* 添加 kset_c 到 kset_p */
        kobject_set_name(&kset_c.kobj,"kset_c");
        kset_c.kobj.kset = kset_p;
        /* 对于较新版本的内核,在注册 kset 之前,需要  
             * 填充 kset.kobj 的 ktype 成员,否则注册不会成功 */
        kset_c.kobj.ktype = &ktype;
        ret = kset_register(&kset_c);
        if(ret)
            kset_unregister(kset_p);
        return 0;
    }
    static void __exit kset_test_exit(void)
    {
        printk("kset test exit!\n");
        kset_unregister(kset_p);
        kset_unregister(&kset_c);
    }
    module_init(kset_test_init);
    module_exit(kset_test_exit);

运行效果为 /sys目录下 创建了 /kset_p/kset_c/kobj_config

bus地址总线

1)地址总线注册bus_register(struct bus_type *bus);

若成功,新的总线将被添加进系统,并可在sysfs 的 /sys/bus 下看到。
2)总线的删除使用:void bus_unregister(struct bus_type *bus)

总线方法
1)int (*match)(struct device * dev, struct device_driver * drv)
        当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。
2)int (*uevent)(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。

总线属性由结构bus_attribute 描述,定义如下:

struct bus_attribute {
struct attribute
attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char *
buf, size_t count);
}

1)int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
创建属性
2)void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
删除属性

bus地址总线测试源码

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
MODULE_AUTHOR("cicue");
MODULE_LICENSE("Dual BSD/GPL");
static char *Version = "$Revision: 1.0 $";
 

/*当一个新设备或者驱动被添加到这个总线时,该方法被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零值。*/

static int my_match(struct device *dev, struct device_driver *driver)
{
        return !strncmp(dev->kobj.name, driver->name, strlen(driver->name));
}

/*声明总线*/
struct bus_type my_bus_type = {
        .name = "my_bus",  //总线名字
        .match = my_match, //总线match函数指针
};
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
        return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
/*内核代码中如此定义:#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store),
它将bus_attr_作为给定的name的前缀来创建总线的真正名称。对应下面的是bus_attr_version*/
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
/*模块加载函数*/
static int __init my_bus_init(void)
{
        int ret;
        /*注册总线*/
        ret = bus_register(&my_bus_type);
        if (ret)
                return ret;
        /*创建属性文件*/
        if (bus_create_file(&my_bus_type, &bus_attr_version))
                printk(KERN_NOTICE "Fail to create version attribute!\n");
        return ret;
}
/*模块卸载函数*/
static void my_bus_exit(void)
{
        bus_unregister(&my_bus_type);
}
module_init(my_bus_init);
module_exit(my_bus_exit);

运行结果:创建  /sys/bus /my_bus/ 里面包含下面的

devices drivers        目录

drivers_autoprobe  uevent   drivers_probe  version 文件


转载于:https://my.oschina.net/u/274829/blog/287084

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值