浅析2.6设备模型

2.6内核引入了sysfs文件系统,sysfs被看成与Proc devfs devpty同类别的文件系统,该文件系统是一个虚拟的文件系统,它可以产生一个包括所有系统硬件的层级视图,跟proc文件系统类似。

sysfs的顶级目录包括 block device bus drivers class power firmware

block目录包含所有块设备,devices目录包含系统所有的设备并根据设备挂接的总线类型组成层次结构,bus目录包含系统中所有的总线类型,drivers目录包含内核中所有已经注册的设备驱动,class目录包含了系统所有设备类型。

要想了解sys,就必须要知道kobject结构。

struct kobject {
 /*对象名称*/
 const char  *name;
   /*用于挂接该Kobj对象到kset的链表*/
 struct list_head entry;
 /*指向父对象的指针*/
 struct kobject  *parent;
 /*所属的kset指针*/
 struct kset  *kset;
 /*指向对象类型描述符的指针*/
 struct kobj_type *ktype;
 struct sysfs_dirent *sd;
 struct kref  kref;
 /*位段 状态*/
 unsigned int state_initialized:1;
 unsigned int state_in_sysfs:1;
 unsigned int state_add_uevent_sent:1;
 unsigned int state_remove_uevent_sent:1;
 unsigned int uevent_suppress:1;
};

内核通过kref成员实现对象引用计数管理,当引用计数为0,所有对象使用的资源将被释放。

ktype成员是指向kobj_type结构的指针,表示该对象的类型。

struct kobj_type {
 void (*release)(struct kobject *kobj);
 /*属性操作*/
 struct sysfs_ops *sysfs_ops;
 /*默认属性*/
 struct attribute **default_attrs;
};

struct sysfs_ops {
 ssize_t (*show)(struct kobject *, struct attribute *,char *);
 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
show store两个成员函数用户完成属性的读写,当用户空间读取属性,调用show函数,写调用store函数。

这里要提的是属性在sysfs中呈现为一个文件,而kobject呈现为一个目录。

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct kobject *parent, const char *fmt, ...) 初始化kobject,并将其注册到linux系统

void kobject_del(struct kobject * kobj)Linux系统中删除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("David Xie");

MODULE_LICENSE("Dual BSD/GPL");

 

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

 

struct attribute test_attr = {

        .name = "kobj_config",

        .mode = S_IRWXUGO,

};

 

static struct attribute *def_attrs[] = {

        &test_attr,

        NULL,

};

 

 /*****读写文件调用函数********/

struct sysfs_ops obj_test_sysops =

{

        .show = kobj_test_show,

        .store = kobj_test_store,

};

/*******定义ktype结构***********/

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("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; //定义全局变量kobj结构

static int kobj_test_init()

{

        printk("kboject test init.\n");

/******初始化kobj并添加kobjNULL表示就在sys目录下,"kobject_test"表示目录名称***/

        kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");

        return 0;

}

 

static int kobj_test_exit()

{

        printk("kobject test exit.\n");

        kobject_del(&kobj);

        return 0;

}

 

module_init(kobj_test_init);

module_exit(kobj_test_exit);

加载模块:

多了kobjetc_test目录。

多了kobj_config属性文件。读属性文件,调用show函数

写属性文件调用store函数。

kset结构:

kobject通常通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合,每个子系统都存在一个kset结构。

struct kset {
 /*用于链接该kset中整个kobject的链表*/
 struct list_head list;
 spinlock_t list_lock;
 /*嵌入的kobject结构*/
 struct kobject kobj;
 /*事件操作集*/
 struct kset_uevent_ops *uevent_ops;
};

kset中的kobject组成一个双向循环链表。

int kset_register(struct kset *kset)在内核中注册一个kset

void kset_unregister(struct kset *kset)从内核中注销一个kset

当kset中的kobject被创建或删除时会产生相应的事件,kobject所属的kset将有机会过滤事件或为用户空间添加信息,每个kset能支持一些特点事件变量,在热插拔事件发生时,kset成员函数可以设置一些事件变量,kset中的uevent_ops就是执行事件操作集的指针。

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

}

filter:决定是否将事件传递到用户空间。如果filter返回0,将不传递事件。(例: uevent_filter

name:用于将字符串传递给用户空间的热插拔处理程序。

uevent:将用户空间需要的参数添加到环境变量中。(例:dev_uevent)

#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("David Xie");
MODULE_LICENSE("Dual BSD/GPL");
 
struct kset kset_p;
struct kset kset_c;

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);
    
struct attribute test_attr = {
.name = "kobj_config",
.mode = S_IRWXUGO,
};
     
static struct attribute *def_attrs[] = {
&test_attr,
NULL,
};
    
    
struct sysfs_ops obj_test_sysops =
{
.show = kobj_test_show,
.store = kobj_test_store,
 };
    
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("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;
}

int kset_filter(struct kset *kset, struct kobject *kobj)
{
        printk("Filter: kobj %s.\n",kobj->name);
        return 1;
}
 
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;
}
 
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;
}

struct kset_uevent_ops uevent_ops =
{
        .filter = kset_filter,
        .name   = kset_name,
        .uevent = kset_uevent,
};
struct kobject kobj;
int kset_test_init()
{
        printk("kset test init.\n");
        kobject_set_name(&kset_p.kobj,"kset_p");
 kset_p.uevent_ops = &uevent_ops;
        kset_register(&kset_p);
 
        kobject_set_name(&kset_c.kobj,"kset_c");
        kset_c.kobj.kset = &kset_p;
        kset_register(&kset_c);
 
 kobject_init_and_add(&kobj,&ktype,&kset_c.kobj,"kobject_test");

        return 0;
}
 
int kset_test_exit()
{
        printk("kset test exit.\n");
        kset_unregister(&kset_p);
        kset_unregister(&kset_c);
 kobject_del(&kobj);
        return 0;
}
 
module_init(kset_test_init);
module_exit(kset_test_exit);

产生热插拔事件。

当屏蔽该句: kobject_set_name(&kset_c.kobj,"kset_c");
        kset_c.kobj.kset = &kset_p;
        kset_register(&kset_c);

时就不会产生热插拔事件。

下面对kset的源码进行分析:

int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
{
 va_list vargs;
 int retval;
 /*第一个省略参数*/
 va_start(vargs, fmt);
 /*对name赋值*/
 retval = kobject_set_name_vargs(kobj, fmt, vargs);
 va_end(vargs);

 return retval;
}

kobject_set_name表示对kobj的name赋值

int kset_register(struct kset *k)
{
 int err;

 if (!k)
  return -EINVAL;
 /*初始化kset*/
 kset_init(k);
 /*向内核添加kobj*/
 err = kobject_add_internal(&k->kobj);
 if (err)
  return err;
 /*热插拔事件*/
 kobject_uevent(&k->kobj, KOBJ_ADD);
 return 0;
}

void kset_init(struct kset *k)
{
 kobject_init_internal(&k->kobj);
 INIT_LIST_HEAD(&k->list);
 spin_lock_init(&k->list_lock);
}

static void kobject_init_internal(struct kobject *kobj)
{
 if (!kobj)
  return;
 /*将对象引用计数设置为1*/
 kref_init(&kobj->kref);
 /*初始化链表头*/
 INIT_LIST_HEAD(&kobj->entry);
 kobj->state_in_sysfs = 0;
 kobj->state_add_uevent_sent = 0;
 kobj->state_remove_uevent_sent = 0;
 kobj->state_initialized = 1;
}

kobject_add_internal源码:

static int kobject_add_internal(struct kobject *kobj)
{
 int error = 0;
 struct kobject *parent;

 if (!kobj)
  return -ENOENT;

 if (!kobj->name || !kobj->name[0]) {
  WARN(1, "kobject: (%p): attempted to be registered with empty "
    "name!\n", kobj);
  return -EINVAL;
 }
 /*获取kobj中的parent域*/
 parent = kobject_get(kobj->parent);
 
 /* join kset if set, use it as parent if we do not already have one */
 /*如果kobj中的kset域不为空,那么就让kset的kobj做为本函数形参kobj的parent*/
 if (kobj->kset) {
  if (!parent)
   parent = kobject_get(&kobj->kset->kobj);
  kobj_kset_join(kobj);
  kobj->parent = parent;
 }
 
 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
   kobject_name(kobj), kobj, __func__,
   parent ? kobject_name(parent) : "<NULL>",
   kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
 /*在/sys目录创建子目录*/
 error = create_dir(kobj);
 /*出错处理*/
 if (error) {
  kobj_kset_leave(kobj);
  kobject_put(parent);
  kobj->parent = NULL;
  
  /* be noisy on error issues */
  if (error == -EEXIST)
   printk(KERN_ERR "%s failed for %s with "
          "-EEXIST, don't try to register things with "
          "the same name in the same directory.\n",
          __func__, kobject_name(kobj));
  else
   printk(KERN_ERR "%s failed for %s (%d)\n",
          __func__, kobject_name(kobj), error);
  dump_stack();
 } else
  kobj->state_in_sysfs = 1;

 return error;
}

kobject_add_internal主要作用是(1)如果kobj存在kset,那么就让kset的kobj做为本函数形参kobj的parent (2)/sys创建目录

那么对于上述kset程序来说,第一个 

kobject_set_name(&kset_p.kobj,"kset_p");
 kset_p.uevent_ops = &uevent_ops;
  kset_register(&kset_p);

(1)作用是没有的,应为kset_p的kobj不存在kset。

kobject_uevent源码

int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
 return kobject_uevent_env(kobj, action, NULL);
}

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
         char *envp_ext[])
{
 struct kobj_uevent_env *env;
 const char *action_string = kobject_actions[action];
 const char *devpath = NULL;
 const char *subsystem;
 struct kobject *top_kobj;
 struct kset *kset;
 struct kset_uevent_ops *uevent_ops;
 u64 seq;
 int i = 0;
 int retval = 0;

 pr_debug("kobject: '%s' (%p): %s\n",
   kobject_name(kobj), kobj, __func__);

 /* search the kset we belong to */
 /*找到依附的kset结构*/
 top_kobj = kobj;
 while (!top_kobj->kset && top_kobj->parent)
  top_kobj = top_kobj->parent;

 if (!top_kobj->kset) {
  pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
    "without kset!\n", kobject_name(kobj), kobj,
    __func__);
  return -EINVAL;
 }
 
 kset = top_kobj->kset;
 uevent_ops = kset->uevent_ops;

 /* skip the event, if uevent_suppress is set*/
 /*如果kset结构中存在uevent_suppress指针,那我们直接返回*/
 if (kobj->uevent_suppress) {
  pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
     "caused the event to drop!\n",
     kobject_name(kobj), kobj, __func__);
  return 0;
 }
 /* skip the event, if the filter returns zero. */
 if (uevent_ops && uevent_ops->filter)
  /*如果操作函数集的filter函数返回为0,也直接返回*/
  if (!uevent_ops->filter(kset, kobj)) {
   pr_debug("kobject: '%s' (%p): %s: filter function "
     "caused the event to drop!\n",
     kobject_name(kobj), kobj, __func__);
   return 0;
  }

 /* originating subsystem */
 if (uevent_ops && uevent_ops->name)
  /*执行name函数*/
  subsystem = uevent_ops->name(kset, kobj);
 else
  subsystem = kobject_name(&kset->kobj);
 if (!subsystem) {
  pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
    "event to drop!\n", kobject_name(kobj), kobj,
    __func__);
  return 0;
 }

 /* environment buffer */
 env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
 if (!env)
  return -ENOMEM;

 /* complete object path */
 /*获取kobj的路径*/
 devpath = kobject_get_path(kobj, GFP_KERNEL);
 if (!devpath) {
  retval = -ENOENT;
  goto exit;
 }

 /* default keys */
 retval = add_uevent_var(env, "ACTION=%s", action_string);
 if (retval)
  goto exit;
 retval = add_uevent_var(env, "DEVPATH=%s", devpath);
 if (retval)
  goto exit;
 retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
 if (retval)
  goto exit;

 /* keys passed in from the caller */
 if (envp_ext) {
  for (i = 0; envp_ext[i]; i++) {
   retval = add_uevent_var(env, "%s", envp_ext[i]);
   if (retval)
    goto exit;
  }
 }

 /* let the kset specific function add its stuff */
 if (uevent_ops && uevent_ops->uevent) {
  /*执行uevent函数*/
  retval = uevent_ops->uevent(kset, kobj, env);
  if (retval) {
   pr_debug("kobject: '%s' (%p): %s: uevent() returned "
     "%d\n", kobject_name(kobj), kobj,
     __func__, retval);
   goto exit;
  }
 }

 /*
  * Mark "add" and "remove" events in the object to ensure proper
  * events to userspace during automatic cleanup. If the object did
  * send an "add" event, "remove" will automatically generated by
  * the core, if not already done by the caller.
  */
 if (action == KOBJ_ADD)
  kobj->state_add_uevent_sent = 1;
 else if (action == KOBJ_REMOVE)
  kobj->state_remove_uevent_sent = 1;

 /* we will send an event, so request a new sequence number */
 spin_lock(&sequence_lock);
 seq = ++uevent_seqnum;
 spin_unlock(&sequence_lock);
 retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
 if (retval)
  goto exit;

#if defined(CONFIG_NET)
 /* send netlink message */
 if (uevent_sock) {
  struct sk_buff *skb;
  size_t len;

  /* allocate message with the maximum possible size */
  len = strlen(action_string) + strlen(devpath) + 2;
  skb = alloc_skb(len + env->buflen, GFP_KERNEL);
  if (skb) {
   char *scratch;

   /* add header */
   scratch = skb_put(skb, len);
   /*打印action_string devpath信息 */
   sprintf(scratch, "%s@%s", action_string, devpath);

   /* copy keys to our continuous event payload buffer */
   for (i = 0; i < env->envp_idx; i++) {
    len = strlen(env->envp[i]) + 1;
    scratch = skb_put(skb, len);
    strcpy(scratch, env->envp[i]);
   }

   NETLINK_CB(skb).dst_group = 1;
   retval = netlink_broadcast(uevent_sock, skb, 0, 1,
         GFP_KERNEL);
   /* ENOBUFS should be handled in userspace */
   if (retval == -ENOBUFS)
    retval = 0;
  } else
   retval = -ENOMEM;
 }

通过kobject_uevent_env的分析,我们可以知道,对于

kobject_set_name(&kset_p.kobj,"kset_p");
 kset_p.uevent_ops = &uevent_ops;
  kset_register(&kset_p);

来说执行到

if (!top_kobj->kset)就直接返回了,因为kset_p中的kobject中没有kset结构。

反之当存在

kobject_set_name(&kset_c.kobj,"kset_c");
       kset_c.kobj.kset = &kset_p;
       kset_register(&kset_c);

时,在执行kobject_uevent_env的kset = top_kobj->kset;uevent_ops = kset->uevent_ops;时,此时的kset就是kset_p,uevent_ops就是uevent_ops,

所以就会执行uevent_ops的热插拔事件。

综合上述,我们可以画图:

 


 

 
 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值