linux对象模型

  死亡日志

  我步入丛林,因我希望生活得有意义,深刻,以免当我生命终结时,发现自己从没有活过。 原文地址:http://blog.chinaunix.net/u3/92745/showart_2145668 .html

  LINUX设备驱动之设备模型一--kobject

  LINUX设备驱动驱动程序模型的核心数据结构是kobject,kobject数据结构在\linux\kobject.h中定义:

  struct kobject {

  const char *name;

  struct list_head entry;

  struct kobject *parent;

  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;

  };

  每个kobject都有它的父节点parent、kset、kobj_type指针,这三者是驱动模型的基本结构,kset是kobject的集合,在\linux\kobject.h中定义:

  struct kset {

  struct list_head list;

  spinlock_t list_lock;

  struct kobject kobj;

  struct kset_uevent_ops *uevent_ops;

  };

  可以看到每个kset内嵌了一个kobject(kobj字段),用来表示其自身节点,其list字段指向了所包含的kobject的链表头。我们在后面的分析中将看到kobject如果没有指定父节点,parent将指向其kset内嵌的kobject。

  每个kobject都有它的kobj_type字段指针,用来表示kobject在文件系统中的操作方法,kobj_type结构也在\linux\kobject.h中定义:

  struct kobj_type {

  void (*release)(struct kobject *kobj);

  struct sysfs_ops *sysfs_ops;

  struct attribute ** default_attrs;

  };

  release方法是在kobject释放是调用,sysfs_ops指向kobject对应的文件操作,default_attrskobject的默认属性,sysfs_ops的将使用default_attrs属性(在后面的分析中我们将会看到)。

  从上面的分析我们可以想象到kobject、kset、kobj_type的层次结构:

  500)this.width=500;" width="500" border="0">

  我们可以把一个kobject添加到文件系统中去(实际上是添加到其父节点所代表的kset中去),内核提供kobject_create_and_add()接口函数:

  struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

  {

  struct kobject *kobj;

  int retval;

  kobj = kobject_create();

  if (!kobj)

  return NULL;

  retval = kobject_add(kobj, parent, "%s", name);

  if (retval) {

  printk(KERN_WARNING "%s: kobject_add error: %d\n",

  __func__, retval);

  kobject_put(kobj);

  kobj = NULL;

  }

  return kobj;

  }

  kobject _create()为要创建的kobject分配内存空间并对其初始化。

  struct kobject *kobject_create(void)

  {

  struct kobject *kobj;

  kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);

  if (!kobj)

  return NULL;

  kobject_init(kobj, &dynamic_kobj_ktype); return kobj; } kobject_init()对kobject基本字段进行初始化,用输入参数设置kobj_type属性。

  这里粘出代码以供参考:

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

  {

  char *err_str;

  if (!kobj) {

  err_str = "invalid kobject pointer!";

  goto error;

  }

  if (!ktype) {

  err_str = "must have a ktype to be initialized properly!\n";

  goto error;

  }

  if (kobj->state_initialized) {

  /* do not error out as sometimes we can recover */

  printk(KERN_ERR "kobject (%p): tried to init an initialized "

  "object, something is seriously wrong.\n", kobj);

  dump_stack();

  }

  kobject_init_internal(kobj);

  kobj->ktype = ktype;

  return;

  error:

  printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);

  dump_stack();

  }

  static void kobject_init_internal(struct kobject *kobj)

  {

  if (!kobj)

  return;

  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()函数:

  int kobject_add(struct kobject *kobj, struct kobject *parent,

  const char *fmt, ...)

  {

  va_list args;

  int retval;

  if (!kobj)

  return -EINVAL;

  if (!kobj->state_initialized) {

  printk(KERN_ERR "kobject '%s' (%p): tried to add an "

  "uninitialized object, something is seriously wrong.\n",

  kobject_name(kobj), kobj);

  dump_stack();

  return -EINVAL;

  }

  va_start(args, fmt);

  retval = kobject_add_varg(kobj, parent, fmt, args);

  va_end(args);

  return retval; } 在上面的初始化中已把位变量设位1

  va_start(args, fmt)和va_end(args)使用可变参数(可见参数用法不在这里分析),在kobject_add_varg中将把fmt指向的内容赋给kobject的name字段。下面我们详细看看kobject_add_varg函数:

  static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,

  const char *fmt, va_list vargs)

  {

  int retval;

  retval = kobject_set_name_vargs(kobj, fmt, vargs);

  if (retval) {

  printk(KERN_ERR "kobject: can not set name properly!\n");

  return retval;

  }

  kobj->parent = parent;

  return kobject_add_internal(kobj);

  }

  kobject_set_name_vargs(kobj, fmt, vargs),如果kobj的name字段指向的内容为空,则为分配一个内存空间并用fmt指向的内容初始化,把地址赋给kobj的name字段。

  int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,

  va_list vargs)

  {

  const char *old_name = kobj->name;

  char *s;

  if (kobj->name && !fmt)

  return 0;

  kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);

  if (!kobj->name)

  return -ENOMEM;

  /* ewww... some of these buggers have '/' in the name ... */

  while ((s = strchr(kobj->name, '/')))

  s[0] = '!';

  kfree(old_name);

  return 0;

  }

  char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)

  {

  unsigned int len;

  char *p;

  va_list aq;

  va_copy(aq, ap);

  len = vsnprintf(NULL, 0, fmt, aq);

  va_end(aq);

  p = kmalloc(len+1, gfp);

  if (!p)

  return NULL;

  vsnprintf(p, len+1, fmt, ap);

  return p;

  }

  继续kobject_add_varg()返回kobject_add_internal(kobj),就是在这个函数理为kobj创建文件系统结构:

  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和它的name字段,不存在则返回错误信息。

  parent = kobject_get(kobj->parent);

  获得其父节点,并增加父节点的计数器,kobject结构中的kref字段用于容器的计数,kobject_get和kobject_put分别增加和减少计数器,如果计数器为0,则释放该kobject,kobject_get返回该kobject。

  /* join kset if set, use it as parent if we do not already have one */

  if (kobj->kset) {

  if (!parent)

  parent = kobject_get(&kobj->kset->kobj);

  kobj_kset_join(kobj);

  kobj->parent = parent;

  }

  在这里我们可以看到,如果调用kobject_create_and_add()时参数parent设为NULL,则会去检查kobj的kset是否存在,如果存在就会把kset所嵌套的kobj作为其父节点,并把kobj添加到kset中去。

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

  打印一些调试信息,接着为kobj创建目录:

  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;

  }

  如果创建不成功,则回滚上面的操作,成功的话则设置kobj的state_in_sysfs标志。

  在看看create_dir()函数中具体创建了那些内容:

  static int create_dir(struct kobject *kobj)

  {

  int error = 0;

  if (kobject_name(kobj)) {

  error = sysfs_create_dir(kobj);

  if (!error) {

  error = populate_dir(kobj);

  if (error)

  sysfs_remove_dir(kobj);

  }

  }

  return error;

  }

  sysfs_create_dir()先为kobj创建了一个目录文件

  int sysfs_create_dir(struct kobject * kobj)

  {

  struct sysfs_dirent *parent_sd, *sd;

  int error = 0;

  BUG_ON(!kobj);

  if (kobj->parent) parent_sd = kobj->parent->sd; else parent_sd = &sysfs_root; error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd); if (!error) kobj->sd = sd; return error; } 如果kobj->parent为NULL,就把&sysfs_root作为父节点sd,即在/sys下面创建结点。

  然后调用populate_dir:

  static int populate_dir(struct kobject *kobj)

  {

  struct kobj_type *t = get_ktype(kobj);

  struct attribute *attr;

  int error = 0;

  int i;

  if (t && t->default_attrs) {

  for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {

  error = sysfs_create_file(kobj, attr);

  if (error)

  break;

  }

  }

  return error;

  }

  得到kobj的kobj_type,历遍kobj_type的default_attrs并创建属性文件,文件的操作会回溯到sysfs_ops的show和store会调用封装了attribute的kobj_attribute结构的store和show方法(在后面的代码中将会分析)。

  由于上面kobject_init(kobj, &dynamic_kobj_ktype)用默认dynamic_kobj_ktype作为kobj_type参数,而dynamic_kobj_ktype的default_attrs为NULL,所以这里没有创建属性文件。

  至此,我们已经知道了kobject_create_and_add()函数创建kobject,挂到父kobject,并设置其kobj_type,在文件系统中为其创建目录和属性文件等。

  另外,如果我们已静态定义了要创建的kobject,则可以调用kobject_init_and_add()来注册kobject,其函数如下:

  int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

  struct kobject *parent, const char *fmt, ...)

  {

  va_list args;

  int retval;

  kobject_init(kobj, ktype);

  va_start(args, fmt);

  retval = kobject_add_varg(kobj, parent, fmt, args);

  va_end(args);

  return retval;

  }

  通过上面的分析我们很轻松就能理解这个函数。

  内核提供注销kobject的函数是kobject_del()

  void kobject_del(struct kobject *kobj)

  {

  if (!kobj)

  return;

  sysfs_remove_dir(kobj);

  kobj->state_in_sysfs = 0;

  kobj_kset_leave(kobj);

  kobject_put(kobj->parent);

  kobj->parent = NULL;

  }

  删除kobj目录及其目录下的属性文件,清kobj的state_in_sysfs标志,把kobj从kset中删除,减少kobj->parent的计数并设其指针为空。

  LINUX设备驱动之设备模型二--kset

  我们已经知道了kset内嵌了kobject来表示自身的节点,创建kset就要完成其内嵌kobject,注册kset时会产生一个事件,事件而最终会调用uevent_ops字段指向结构中的函数,这个事件是通过用户空间的hotplug程序处理。下面我们一步一步分析。

  内核同样提供了创建和注册kset的函数kset_create_and_add()

  struct kset *kset_create_and_add(const char *name,

  struct kset_uevent_ops *uevent_ops,

  struct kobject *parent_kobj)

  {

  struct kset *kset;

  int error; kset = kset_create (name, uevent_ops, parent_kobj); if (!kset) return NULL; error = kset_register(kset); if (error) { kfree(kset); return NULL; } return kset; } 输入参数有一个kset_uevent_ops类型的结构变量,其结构包含三个函数指针,我们在后面的分析到这三个函数在什么时候被调用,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);

  };

  继续看上面的函数,先调用kset_create ()创建一个kset,接着调用kset_register()注册它。

  static struct kset *kset_create(const char *name,

  struct kset_uevent_ops *uevent_ops,

  struct kobject *parent_kobj)

  {

  struct kset *kset;

  int retval;

  kset = kzalloc(sizeof(*kset), GFP_KERNEL);

  if (!kset)

  return NULL;

  retval = kobject_set_name(&kset->kobj, name);

  if (retval) {

  kfree(kset);

  return NULL;

  }

  kset->uevent_ops = uevent_ops;

  kset->kobj.parent = parent_kobj;

  /*

  * The kobject of this kset will have a type of kset_ktype and belong to

  * no kset itself. That way we can properly free it when it is

  * finished being used.

  */

  kset->kobj.ktype = &kset_ktype;

  kset->kobj.kset = NULL;

  return kset;

  }

  为kset分配内存,如我们上面分析,初始化了kset内嵌的kobject(这里还未将kobject注册到文件系统),另外用输入参数初始化kset的uevent_ops字段。

  接着看kset的注册函数kset_register():

  int kset_register(struct kset *k)

  {

  int err;

  if (!k)

  return -EINVAL;

  kset_init(k);

  err = kobject_add_internal(&k->kobj);

  if (err)

  return err;

  kobject_uevent(&k->kobj, KOBJ_ADD);

  return 0;

  }

  在这里终于看到调用kobject_add_internal()将kset内嵌的kobject注册到文件系统,这个函数我们在上面已经分析。

  我们上面说到注册kset会产生一个事件,就是在这里调用了kobject_uevent(&k->kobj, KOBJ_ADD)

  kobject_uevent()在\lib\kobject_uevent.c中:

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

  {

  return kobject_uevent_env(kobj, action, NULL);

  }

  转入kobject_uevent_env():

  这个函数比较长,我们分段分析 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 */ 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; 如果如果kobj的kset和parent字段都不存在,说明找不到所属kset,也就没有uevent_ops,不能产生事件,返回错误信息;相反则找到了存在kset的kobj或父kobject(依次往上找),并赋值给uevent_ops。

  /* skip the event, if uevent_suppress is set*/

  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;

  }

  如果设置了uevent_suppress字段,说明不希望产生事件,忽略事件正确返回。注意驱动程序将在适当的地方产生改事件。

  /* skip the event, if the filter returns zero. */

  if (uevent_ops && uevent_ops->filter)

  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;

  }

  如果uevent_ops->filter返回0,同样忽略事件正确返回。

  if (uevent_ops && uevent_ops->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;

  分配一个kobj_uevent_env结构内存,用于存放环境变量的值。

  /* complete object path */

  devpath = kobject_get_path(kobj, GFP_KERNEL);

  if (!devpath) {

  retval = -ENOENT;

  goto exit;

  }

  获得引发事件的kobject在sysfs中的路径。

  /* 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;

  }

  } 调用add_uevent_var()kobj_uevent_env填充action_string,kobject路径,子系统名称以及其他指定环境变量。

  /* let the kset specific function add its stuff */

  if (uevent_ops && uevent_ops->uevent) {

  retval = uevent_ops->uevent(kset, kobj, env);

  if (retval) {

  pr_debug("kobject: '%s' (%p): %s: uevent() returned "

  "%d\n", kobject_name(kobj), kobj,

  __FUNCTION__, retval);

  goto exit;

  }

  }

  调用uevent_ops的uevent函数,编程人员可在此函数中实现自定义的功能。

  /*

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

  设置KOBJ_ADD和KOBJ_REMOVE的标志。

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

  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; } #endif /* call uevent_helper, usually only enabled during early boot */ if (uevent_helper[0]) { char *argv [3]; argv [0] = uevent_helper; argv [1] = (char *)subsystem; argv [2] = NULL; retval = add_uevent_var(env, "HOME=/"); if (retval) goto exit; retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); if (retval) goto exit; 添加HOME和PATH环境变量。

  retval = call_usermodehelper(argv[0], argv,

  env->envp, UMH_WAIT_EXEC);

  }

  exit:

  kfree(devpath);

  kfree(env);

  return retval;

  }

  调用hotplug函数。

  看一下kset_unregister()

  void kset_unregister (struct kset *k)

  {

  if (!k)

  return;

  kobject_put(&k-> kobj);

  }

  减少其内嵌的kobj计数,为0则释放其内存空间。

  已经分析完kobject和kset,linux的设备模型就是基于这两个数据结构的,在此基础上,后续将分析设备模型中的device、driver、和bus。

  

 

你可能感兴趣的:(linux,职场,模型,休闲,linux系统)