Linux内核大讲堂 (一) 设备驱动的基石驱动模型(2)

本文详细解读了 Linux 内核中的 kobject_uevent 函数,包括其核心功能、内部实现原理以及如何触发用户空间事件。通过分析函数定义和流程,展示了如何在内核添加新组件时通知用户空间。

上节我们分析到int kset_register(struct kset *k)函数中的kobject_add_internal(&k->kobj),我们接着分析,先唤起来一下大家的记忆,给出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_uevent(&k->kobj, KOBJ_ADD),这个东西是用来告诉用户空间有新朋友来啦,他叫什么什么名字。我们来看看它具体是怎么干活的。
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 */
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*/
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)
  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)
  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 */
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) {
  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);
   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;

  retval = call_usermodehelper(argv[0], argv,
          env->envp, UMH_WAIT_EXEC);
}

exit:
kfree(devpath);
kfree(env);
return retval;
}
我们经过前面那些初始化等之后,最后调用了call_usermodehelper函数。
static inline int
call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
{
struct subprocess_info *info;
gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;

info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
if (info == NULL)
  return -ENOMEM;
return call_usermodehelper_exec(info, wait);
}
凭着在linux上的编程经验我们都知道,这个东西按照传入的参数执行了一个程序。
没错,这个就是用于与用户空间交互的重要角色。后们将会在后续合适的章节中详细讲述。

 

 

转载自:http://blog.youkuaiyun.com/z2007b/article/details/6395333

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值